I'm using the NotificationService and a Broadcastreceiver to get the incoming notifications. The service starts from the manifest:
<service android:name="com.myapp.notifications.MyNotificationListenerService"
android:label="#string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
and it's this;
public class MyNotificationListenerService extends NotificationListenerService{
Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
String pack = sbn.getPackageName();
String ticker = sbn.getNotification().tickerText.toString();
Bundle extras = sbn.getNotification().extras;
String title = extras.getString("android.title");
String text = extras.getCharSequence("android.text").toString();
Log.i("Package",pack);
Log.i("Ticker",ticker);
Log.i("Title",title);
Log.i("Text",text);
Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("package", pack);
msgrcv.putExtra("ticker", ticker);
msgrcv.putExtra("title", title);
msgrcv.putExtra("text", text);
LocalBroadcastManager.getInstance(context).sendBroadcast(msgrcv);
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.i("Msg","Notification Removed");
}
}
When a notification arrives the log shows me the title, the package and the other informations correctly.. The problem is this one;
I have an Activity that is the MainActivity in which i have the NavigationDrawer. The class where i want show the incoming notifications is a Fragment! So i created in the MainActivity the Broadcastreceiver and then i inflate with LayoutInflater the layout of the fragment to get all component i need in this way:
public BroadcastReceiver onNotice= new BroadcastReceiver() {
LinearLayout notificationLayout;
Drawable icon;
#Override
public void onReceive(Context context, Intent intent) {
final String pack = intent.getStringExtra("package");
String title = intent.getStringExtra("title");
String text = intent.getStringExtra("text");
LayoutInflater mInf = LayoutInflater.from(context);
View myView = mInf.inflate(R.layout.activity_main, null);
notificationLayout = (LinearLayout)myView.findViewById(R.id.notificationLayout);
TextView notificationDescription = (TextView) myView.findViewById(R.id.notificationDesc);
TextView notificationTitle = (TextView) myView.findViewById(R.id.notificationTitle);
CircularImageView notificationImage = (CircularImageView) myView.findViewById(R.id.img_thumbnail);
Toast.makeText(DrawerActivity.this, title, Toast.LENGTH_SHORT).show();
if(!pack.equals("") || !title.equals("") || !text.equals("")) {
notificationLayout.setVisibility(View.VISIBLE);
notificationTitle.setText(title);
notificationDescription.setText(text);
try {
icon = DrawerActivity.this.getPackageManager().getApplicationIcon(pack);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
notificationImage.setImageDrawable(icon);
} else {
notificationLayout.setVisibility(View.INVISIBLE);
}
}
};
When the notification arrives now appears the Toast that notify me the title of the notification but i can't see the values in the view.. the layout of the notification is empty.. How is possible?
Or how can i put the values from the activity to my fragment?
Whne you inflate a layout, it creates a new tree made up of new objects.
The way to accomplish what you are trying to would be to cache whatever views that you will change in your onCreate and refer to them in your BroadcastReceiver's onReceive.
private TextView textView;
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//TODO: Update Views
}
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.my_fragment, container, false);
//Cache views
textView = (TextView) root.findViewById(R.id.textview);
return root;
}
#Override
public void onResume() {
super.onResume();
getActivity().registerReceiver(broadcastReceiver, filter);
}
#Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(broadcastReceiver);
}
Related
I'm getting a NullPointerException for my listener in my class, even though everything should be working fine. I followed Coding in Flow's tutorial for the custom dialog, rewatched it a few times and checked source code and I didn't miss anything. here's the code
Fragment the dialog is called from
public class FragmentMain extends Fragment implements CustomDialog.DialogListener {
.
.
.
private int timeLimit = 0;
private Button dialogOpen;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
.
.
.
dialogOpen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
DialogDashboard dialogDashboard = new DialogDashboard();
dialogDashboard.show(getActivity().getSupportFragmentManager(), "notif_dialog");
}
}); //dialog called here
return view;
}
//getting text from spinner in dialog
#Override
public void applyText(String time) {
switch (time) {
case "30 Minutes":
timeLimit = 30;
case "1 Hour":
timeLimit = 60;
case "2 Hours":
timeLimit = 120;
case "4 Hours":
timeLimit = 240;
case "5 Hours":
timeLimit = 300;
}
}
}
Dialog Code:
public class CustomDialog extends AppCompatDialogFragment {
private Button confirmButton;
private Spinner spinner;
private DialogListener listener;
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.NotificationAlertDialog);
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.custom_dialog_notif, null);
confirmButton = view.findViewById(R.id.dialogButton);
spinner = view.findViewById(R.id.dialogSpinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(),
R.array.spinner_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
confirmButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String time = spinner.getSelectedItem().toString();
listener.applyText(time); // says listener is null when I just do e.printStackTrace();
dismiss();
}
});
builder.setView(view)
.setCancelable(true);
return builder.create();
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
listener = (DialogListener) context;
} catch (Exception e) {
e.printStackTrace();
throw new ClassCastException(context.toString() +
"must implement dialog listener"); // this says if the listener is null, but even if it's implemented it returns null...
}
}
public interface DialogListener {
void applyText(String time);
}
}
Root cause
You got NPE because onAttach() callback is called when the fragment is attached to its host/activity. The context param is the activity itseft, not the FragmentMain.
Solution
Step 1: When you click on the open dialog button in FragmentMain.
CustomDialog dialogDashboard = new CustomDialog();
dialogDashboard.show(getChildFragmentManager(), "notif_dialog");
Step 2: Modify the code in CustomDialog
Remove this code
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
listener = (DialogListener) context;
} catch (Exception e) {
e.printStackTrace();
throw new ClassCastException(context.toString() +
"must implement dialog listener");
}
}
When users click on the confirm button.
String time = spinner.getSelectedItem().toString();
Fragment fragment = getParentFragment();
if (fragment instanceof CustomDialog.DialogListener) {
listener = (DialogListener) fragment;
listener.applyText(time);
}
dismiss();
I have a MasterActivity that has a few fragments. One fragment allows the user to open up Square to pay. After the user signs their name in Square and the data is returned to my app, my app crashes because the Payment fragment can't find the MasterActivity as it is null.
This only happens when the user signs the receipt. If the user pays cash (no signature) the My App works fine.
I have noticed that the when the Square App is ready for a signature it rotates the screen, just before the signature capture activity is view-able, I can see my app rotate too.
After the Square application has returned to my app, I can see the successful upload of the data to my server from my app. Then, it is only when the payment fragment asks the MasterActivity to refresh the data, I get a null pointer exception for the DISP_KEY in MasterAcitvity .
Can any see what I might be doing wrong?
MasterActivity
public class MasterActivity extends AppCompatActivity {
private String TAG = getClass().getSimpleName();
private String DISP_KEY = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_master);
Intent intent = getIntent();
DISP_KEY = (String) intent.getSerializableExtra("SELECTED_DISPATCH");
initView();
refreshData();
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
public void refreshData() {
//Do some network download data stuff using the DISP_KEY
}
private void initView() {
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
//JUST TEXT
tabLayout.addTab(tabLayout.newTab().setText("Details"));
tabLayout.addTab(tabLayout.newTab().setText("Sections"));
tabLayout.addTab(tabLayout.newTab().setText("Area"));
tabLayout.addTab(tabLayout.newTab().setText("Notes"));
tabLayout.addTab(tabLayout.newTab().setText("Payments"));
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new TabPagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(adapter);
}
}
PaymentFragment
public class PaymentFragment extends Fragment {
private static final int SQUARE_CHARGE_REQUEST_CODE = 1;
private String TAG = getClass().getSimpleName();
private PosClient posClient;
public PaymentFragment() {
// Required empty public constructor for fragments
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_payment, container, false);
initGUI(view);
return view;
}
#Override
public void onStart() {
super.onStart();
mVehicle = ((MasterActivity) this.getActivity()).getCurrentVehicle();
if (mVehicle != null) {
reloadData();
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (this != null) {
((MasterActivity) context).registerDataReloadListener(this);
posClient = PosSdk.createClient(getActivity(), AppConfig.SQUARE_APP_ID);
} else {
((MasterActivity) context).onBackPressed();
}
}
#Override
public void onDetach() {
super.onDetach();
if (this != null) {
((MasterActivity) getActivity()).unregisterDataReloadListener(this);
}
}
public void reloadData() {
//Ask the master activity to reload data
}
// SQUARE
private void acceptSquarePayment() {
ChargeRequest.TenderType types;
ChargeRequest request = new ChargeRequest.Builder(Integer.parseInt(square_balance), CurrencyCode.USD)
.enforceBusinessLocation(square_location_id)
.restrictTendersTo(types)
.requestMetadata(square_call_num)
.autoReturn(3200, TimeUnit.MILLISECONDS)
.build();
Intent intent = posClient.createChargeIntent(request);
startActivityForResult(intent, SQUARE_CHARGE_REQUEST_CODE);
}
// GET SQAURE RESULTS
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
ChargeRequest.Success success = posClient.parseChargeSuccess(data);
sendReceiptToAPIServer(success.serverTransactionId, success.clientTransactionId, success.requestMetadata);
}
}
private void sendReceiptToAPIServer(String serverTransactionId, String clientTransactionId, String callNum) {
//Do some data upload stuff
//After the successufull upload, this is where the crash happens
reloadData();
}
#Override
public void onDataReload() {
mVehicle = ((MasterActivity) this.getActivity()).getCurrentVehicle();
if (getView() != null) {
reloadData();
}
}
}
this is my MainActivity
private DatabaseReference mDatabaseReference;
private RecyclerView recyclerView;
private PlaceRecyclerAdapter placeRecyclerAdapter;
private List<Places> placesList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDatabaseReference = FirebaseDatabase.getInstance().getReference().child("Places");
placesList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu,menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId()==R.id.action_add)
{
startActivity(new Intent(MainActivity.this,AddPostActivity.class));
finish();
}
return super.onOptionsItemSelected(item);
}
#Override
protected void onStart() {
super.onStart();
mDatabaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Places places = dataSnapshot.getValue(Places.class);
placesList.add(places);
placeRecyclerAdapter = new PlaceRecyclerAdapter(MainActivity.this,placesList);
recyclerView.setAdapter(placeRecyclerAdapter);
placeRecyclerAdapter.notifyDataSetChanged();
}
I am using this RecyclerAdapter to load cardview cards in the main activity
public PlaceRecyclerAdapter(Context context, List<Places> placesList) {
this.context = context;
this.placesList = placesList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.post_row,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Places places = placesList.get(position);
//String imageUrl= null;
holder.place.setText(places.getPlace());
holder.desc.setText(places.getDesc());
//imageUrl= places.getImage();
//todo: Use piccasso library to load images
//Picasso.with(context).load(imageUrl).into(holder.image);
}
#Override
public int getItemCount() {
return placesList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView place;
public TextView desc;
//public ImageView image;
public ViewHolder(View view) {
super(view);
place = (TextView) view.findViewById(R.id.postTitleList);
desc = (TextView) view.findViewById(R.id.postDescList);
//image = (ImageView) view.findViewById(R.id.postImageList);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
int pos = getAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
Places clickedDataItem = placesList.get(pos);
//Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getPlace(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, Details.class);
intent.putExtra("NAME", clickedDataItem.getPlace());
intent.putExtra("DESC", clickedDataItem.getDesc());
intent.putExtra("IMG", clickedDataItem.getImage());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
}
}
and here is my Details activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
dPlace = (TextView) findViewById(R.id.detail_title);
dDesc = (TextView) findViewById(R.id.detail_desc);
dImage = (ImageView) findViewById(R.id.detail_image);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
String name = bundle.getString("NAME");
String desc = bundle.getString("DESC");
String img = bundle.getString("IMG");
dPlace.setText(name);
dDesc.setText(desc);
Picasso.with(this).load(img).into(dImage);
now, clicking on a item in MainActivity I am able to go to the Details activity. suppose there are 3 items in database, and at first main activity shows only 3 items. but after going to Details activity, and then coming back to main activity, there are 6 items, the earlier 3 items are repeated. and if again I go to the Details activity and come back, there will be 9 items. I used (Activity)context).finish(); in RecyclerViewAdapter to finish the main activity, but I think it finishes the context from which I am able to get the details.
please help.
Sorry for my bad english.
Your firebase loading data items needs to go inside onCreate() as it will only gets called only once if its on backstack an onStart() will get called twice. So just implement the data item loading logic in onCreate instead of onStart()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDatabaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Places places = dataSnapshot.getValue(Places.class);
placesList.add(places);
placeRecyclerAdapter = new PlaceRecyclerAdapter(MainActivity.this,placesList);
recyclerView.setAdapter(placeRecyclerAdapter);
placeRecyclerAdapter.notifyDataSetChanged();
}
}
Update
placesList.clear();
placesList.add(places);
I have just started learning fragments in Android.I am now able to display data in fragments using ListFragment and ArrayAdapter. Now, I am trying to use onclickListener and intents on fragments like the way we do with activities and buttons.
I am trying to open email app when clicking on a fragment that appears as we click the listfragment.
But, I am not able to get one thing i want to accomplish which is the email address of a contact. How can i achieve it?
I have achieved these results but on second image the To: part is empty. it should appear kurt#nirvana.com from fragment.
public class ContactDetailsFragment extends Fragment {
private long contactId;
public ContactDetailsFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if (savedInstanceState != null){
contactId = savedInstanceState.getLong("contactId");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_contact_details, container, false);
}
#Override
public void onStart() {
super.onStart();
View view = getView();
if (view != null) {
TextView FNameText = (TextView) view.findViewById(R.id.textFName);
final Contact contact = myContact[(int) contactId];
FNameText.setText(contact.getFName());
TextView LNameText = (TextView) view.findViewById(R.id.textLName);
LNameText.setText(contact.getLName());
TextView PhoneText = (TextView) view.findViewById(R.id.textPhone);
PhoneText.setText(contact.getPhone());
final TextView EmailText = (TextView) view.findViewById(R.id.textEmail);
EmailText.setText(contact.getEmail());
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:"));
//intent.setType("text/message");
intent.putExtra(Intent.EXTRA_SUBJECT, "Announcement from Mobile Apps Class");
intent.putExtra(Intent.EXTRA_EMAIL, myContact.toString());
intent.putExtra(Intent.EXTRA_TEXT, "What do you want. How you want? Do you really know what is there in all those Whats?");
startActivity(Intent.createChooser(intent, "Email"));
//Toast.makeText(getActivity(),"Button is clicked!" +contactId, Toast.LENGTH_LONG).show();
}
});
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
savedInstanceState.putLong("contactId", contactId);
}
public void setContact(long id){
this.contactId = id;
}
}
Adding this line
intent.setData(Uri.parse("mailto:"+contact.getEmail()));
and removing
intent.putExtra(Intent.EXTRA_EMAIL, myContact.toString());
solved my problem.
I have 2 image views in a fragment set up as clickable and i am trying to play a sound when each one is clicked! i can do this in an activity but not in a fragment! i am trying to use to media player but this throws up an error.
public class HomeFragment extends Fragment {
public HomeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//giving me error cannot resolve method
final MediaPlayer mp = MediaPlayer.create(this, R.raw.music_marimba_chord);
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
ImageView share = (ImageView)view.findViewById(R.id.share);
share.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "");
startActivity(Intent.createChooser(sharingIntent, "Share via"));
ImageView send = (ImageView)view.findViewById(R.id.send);
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(""));
startActivity(intent);
}
});
return view;
}
// set fragment to portrait
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
Activity a = getActivity();
if(a != null) a.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
}
If you want to use SoundPool, the following will play a different sound when click or select event are triggered on the fragment :
public abstract class MainFragment extends Fragment {
private SoundPool soundPool;
private HashMap<Integer, Integer> soundPoolMap;
public void onCreate() {
initSounds(getActivity().getApplicationContext());
}
public void initSounds(Context context) {
soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 100);
soundPoolMap = new HashMap(1);
soundPoolMap.put(R.raw.music1, soundPool.load(context, R.raw.music1, 1));
soundPoolMap.put(R.raw.music2, soundPool.load(context, R.raw.music2, 1));
}
public void playSound(int soundID) {
float volume = 0.2f;
// play sound with same right and left volume, with a priority of 1,
// zero repeats (i.e play once), and a playback rate of 1f
soundPool.play(soundPoolMap.get(soundID), volume, volume, 1, 0, 1f);
}
private void playSoundClick() {
playSound(R.raw.music1);
}
private void playSoundSelect() {
playSound(R.raw.music2);
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
playSoundClick();
} else {
playSoundSelect();
}
}
return true;
}
}
The sound is played here :
soundPool.play(soundPoolMap.get(soundID), volume, volume, 1, 0, 1f);
where you can set volume left & right and priority in case you want this sound to prevail over other if another one is being played in the same SoundPool
To integrate with your work :
public class HomeFragment extends MainFragment {
public HomeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
super.onCreate();
ImageView share = (ImageView)view.findViewById(R.id.share);
share.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
playSound(R.raw.music1);
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "");
startActivity(Intent.createChooser(sharingIntent, "Share via"));
ImageView send = (ImageView)view.findViewById(R.id.send);
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
playSound(R.raw.music2);
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(""));
startActivity(intent);
}
});
return view;
}
}