I want to display a notification inside the app that disappears when the notification is tapped without starting an activity.
I use an empty intent and it works:
Intent intent = new Intent();
PendingIntent contentIntent = PendingIntent.getActivity(context, (int)System.currentTimeMillis(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, "")
.setSmallIcon(R.drawable.icon)
.setContentTitle(title)
.setContentText(text)
.setAutoCancel(true)
.setTicker(text);
.setContentIntent(contentIntent);
However, there are some crashes:
java.lang.RuntimeException: bad array lengths
at android.os.Parcel.readIntArray(Parcel.java:789)
at android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag(INotificationManager.java:339)
at android.app.NotificationManager.notify(NotificationManager.java:139)
at android.app.NotificationManager.notify(NotificationManager.java:112)
...
According to this SO answer the crashes seem to happen because intent is not starting an activity.
A notification can be dismissed by its ID, but I cannot dismiss it when there is no activity to start and call the dismiss method.
How can I dismiss the notification on tap inside the app without using an empty intent?
Update:
According to this SO questions it seems to be an Android issue. The crashes I got reported also happened on Android 4.3.
Update 2:
According to this SO answer it seems to be due to a too large bitmap set with setLargeIcon. So I am reducing the bitmap size now with Glide.
You can create an intent which calls a BroadcastReceiver which then cancels the notification.
Your PendingIntentcan be created like this:
private PendingIntent getCancelNotificationIntent() {
Intent cancelIntent = new Intent(context, CancelNotification.class);
return PendingIntent.getBroadcast(context, 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
The CancelNotification.java class would look similar to this:
public class CancelNotification extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
notificationManager.cancel(NOTIFICATION_ID);
}
}
EDIT:
NOTIFICATION_ID is a constant you define and pass to the NotificationManager like this:
notificationManager.notify(NOTIFICATION_ID, yourNotificationObject);
NOTE: don't forget to register the receiver in your manifest file like this:
<receiver android:name="com.example.package.CancelNotification" />
If you don't want to start an activity when users tap the notification you can build an pending intent which sends a broadcast or start a service.
Here is an example:
PendingIntent.getBroadcast(context, 12, new Intent("any intent action"), PendingIntent.FLAG_UPDATE_CURRENT)
But what prevents your activity to have implementation like this:
#Override
public void onCreate(Bundle savedInstanceState) {}
with additional nodisplay theme in manifest:
android:theme="#android:style/Theme.NoDisplay"
It'd be simply a no-op one.
Related
I'm using single activity architecture in my app. Now I'm stuck with handing notifications. I'm receiving notification from firebase and when app is on background, the google play services handle such notifications great. When it's tapped it brings the app from background to foreground (it does't recreate activity / app).
I need to have the same behaviour for notification received while the app is on foreground. Therefore I override onMessageReceived() in my firebase service and create new notification here. I tried many variation of Intent's Flags passed to the notification and launchMode in manifest but it always results into activity recreation (activity has different hashcode and it's onCreate() it's called) after tapping on notification created by onMessageReceived().
Here is the code:
#Override
public void onMessageReceived(#NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Logger.d("Msg received " + remoteMessage.getNotification().getTitle());
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, FCM_CHANNEL_ID)
.setContentTitle(remoteMessage.getNotification().getTitle())
.setContentText(remoteMessage.getNotification().getBody())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(remoteMessage.getNotification().getBody()))
.build();
NotificationManagerCompat manager = NotificationManagerCompat.from(getApplicationContext());
manager.notify(FCM_NOTIFICATION_ID, notification);
}
manifest: android:launchMode="singleTask"
Any idea what to change to prevent activity recreation? (I'm testing on android 10, MainActivity extends AppCompatActivity)
Thanks.
Finally I get it work. All magic was done by adding those two lines to my code.
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
Original answer
Did you try this?
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Use the
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK flags.
This will start activity as the root of the stack.
I have an app which associates some entities with a unique ID and notifies about the entities to the user, which I'm gonna use notificationID to be the same as entity ID.
I have built a notification with a dismiss action based on the following sample solution exactly without any modification.
So far things are going well until I try to create 2 notifications with different ID using the sample.
A problem arises in that the dismiss button only receives the notificationID of the first notification:
The first notification behaves normally as expected.
But the second notification's getExtra() in BroadcastReceiver takes the notificationID of the FIRST notification instead and cancelling the notification just keeps cancelling the first notification.
My create Notification function, I just call this function twice with different IDs:
void createNoti(int NOTIFICATION_ID){
Intent buttonIntent = new Intent(context, ButtonReceiver.class);
buttonIntent.putExtra("notificationId", NOTIFICATION_ID);
PendingIntent btPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, buttonIntent, 0);
NotificationCompat.Builder mb = new NotificationCompat.Builder(getBaseContext());
mb.addAction(R.drawable.ic_Action, "My Action", btPendingIntent);
manager.notify(NOTIFICATION_ID, mb.build());
}
BroadcastReceiver class:
public class ButtonReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int notificationId = intent.getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
}
I believe the issue is in passing in 0 into PendingIntent:
PendingIntent btPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, buttonIntent, 0);
I had the same issue until I started passing in the notification id as the 2nd argument; so instead of passing in 0, pass in the id of the notification:
PendingIntent btPendingIntent = PendingIntent.getActivity(getApplicationContext(), NOTIFICATION_ID, buttonIntent, 0);
After I made that change, I noticed that when clicking on individual notifications (especially notifications in a group) everything worked as intended.
Creating notification:
PendingIntent pIntent = PendingIntent.getActivity(context, (int) taskId, intent, 0);
intent.setAction(Utils.MARK_AS_DONE);
PendingIntent pIntentMarkAsDone = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setTicker(ticker)
.setContentTitle(title)
.setContentText(description)
.setSmallIcon(getAlarmIcon(type))
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),R.mipmap.ic_launcher))
.setContentIntent(pIntent)
.addAction(0, context.getString(R.string.mark_as_done), pIntentMarkAsDone);
Notification notification = builder.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify((int) taskId, notification);
I added the adding using a pending intent with getBroadcast.
Receiver:
public class NotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Log to check
}
}
This class should "receive" the action. I also add on Manifest
Manifest:
<receiver android:name=".NotificationReceiver">
<intent-filter>
<action android:name="<package_name>.MARK_AS_DONE"/>
</intent-filter>
</receiver>
Well, onReceive is not receiving. What am I doing wrong?
TL;DR: Create a fresh Intent, rather than reusing the one in intent, and get rid of the <intent-filter> from the <receiver>.
Your first line is:
PendingIntent pIntent = PendingIntent.getActivity(context, (int) taskId, intent, 0);
This implies that intent identifies some activity. If you created this via new Intent(context, YourActivityClass.class), then it has a ComponentName set inside of it, identifying your activity.
Then, you call setAction() on intent and use it with getBroadcast(). However, other than setting (or replacing) the action, everything else in intent is the same as it was. In particular, the ComponentName identifying the activity is still there. So, when the broadcast is sent, Android cannot deliver it, as the component is invalid (an activity cannot directly receive a broadcast), and the action string is ignored (as once a ComponentName is set on an Intent, things like actions and categories no longer count for routing).
So, I recommend that you create two Intent objects, one for the activity, one for the receiver.
Note that you do not need an action string for the receiver. You can use the explicit Intent constructor (new Intent(context, NotificationReceiver.class)). In fact, having the action string on the receiver is bad for security, as now any app can send you that broadcast. So, I recommend removing the <intent-filter> and using an explicit Intent to create your broadcast PendingIntent.
My Android notification Action buttons are not working at all. I have the following code in my service, and the receiver is NOT registered in the manifest because it makes no change. I can send the broadcast from another activity, and it works great, but there is a problem somewhere.
Here are the PendingIntents that pair with the buttons
Intent next = new Intent(getString(R.string.receiver_notification_media_change));
next.setAction(NOTIFICATION_MEDIA_CHANGE_NEXT);
PendingIntent pendingIntentNext = PendingIntent.getBroadcast(getApplicationContext(), 0, next, PendingIntent.FLAG_UPDATE_CURRENT);
Intent last = new Intent(getString(R.string.receiver_notification_media_change));
last.setAction(NOTIFICATION_MEDIA_CHANGE_BACK);
PendingIntent pendingIntentLast = PendingIntent.getBroadcast(getApplicationContext(), 0, last, PendingIntent.FLAG_UPDATE_CURRENT);
Notification:
Notification.Builder mBuilder = new Notification.Builder(getApplicationContext())
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setSmallIcon(smallDrawableResId)
.addAction(R.drawable.icon1, "as", pendingIntentLast)
.addAction(R.drawable.icon2, "asdf", pendingIntentNext)
.setContentTitle(title)
.setContentText("title")
.setLargeIcon(icon)
.setContentIntent(pendingIntent) //to an activity. Works great
.setOngoing(true)
.setStyle(new Notification.MediaStyle()
.setShowActionsInCompactView(0, 1));
Here is the BroadcastReceiver which is declared in the class below.
private BroadcastReceiver notificationMediaChanger = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
System.out.println("RECEIVEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
if(action.equals(NOTIFICATION_MEDIA_CHANGE_NEXT))
playNextSong();
else if(action.equals(NOTIFICATION_MEDIA_CHANGE_BACK))
playPreviousSong();
}
};
OnCreate the receiver is registered
registerReceiver(notificationMediaChanger, new IntentFilter(getString(R.string.receiver_notification_media_change))); //LocalBroadcastManager.getInstance(getApplicationContext()) appears to be equivalent.
And OnStop it is removed:
unregisterReceiver(notificationMediaChanger);
Your action strings do not match.
Intent next = new Intent(getString(R.string.receiver_notification_media_change));
next.setAction(NOTIFICATION_MEDIA_CHANGE_NEXT);
For some reason, you are replacing one action string with another. I do not know why.
registerReceiver(notificationMediaChanger, new IntentFilter(getString(R.string.receiver_notification_media_change)));
Here, you are using the first action string. Your Intent has the second action string. These are presumably not the same.
Also:
LocalBroadcastManager is not used by PendingIntent
Unless the Notification is only on the screen while your activity is on the screen (which would be bizarre), you need to register your receiver in the manifest
I am trying to add a button to my notification in android.
I use the addAction method in order to add an intent which supposes to open up the main activity (same as clicking the entire notification) but with an extra bundle with data.
this is what I have done so far:
notificationManager = (NotificationManager)this.getSystemService(NOTIFICATION_SERVICE);
//regular intent to view main activity
PendingIntent contentIntent = PendingIntent.getActivity(this,Constants.MAIN_ACTIVITY,
new Intent(this, MainActivity.class), 0);
//intent for viewing transaction dialog, within main activity using PURCHASE_DIALOG request code
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(Constants.LIST, (java.util.ArrayList<? extends android.os.Parcelable>) list);
PendingIntent purchaseIntent = PendingIntent.getActivity(
this, Constants.PURCHASE_DIALOG,
new Intent(this, MainActivity.class), 0, bundle);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(Constants.NOTIFICATION_TOPIC)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(watchResponse.toString()))
.setAutoCancel(true)
.addAction(R.drawable.ic_launcher, "Buy", purchaseIntent)
.setContentText(message);
mBuilder.setContentIntent(contentIntent);
notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
I simply expect that my onResume in MainActivity class will be called once clicking the extra action, and there i'll be able to get the bundle trough getIntent(), yet nothing happens when I click on it. the button is clicked, but the activity remains open and my application activity wont start.
I had a very similar issue but a very different solution. Pending intent is also not fired if you have declared <service android:enabled="false"></service> in your manifest.xml file.
Replace from android:enabled="false" to android:enabled="true"
This might not be a direct issue of the problem. But if you create the service in android studio using default template it automatically adds these properties to the service.
If this does not work there is a similar question you can find here:
Android Notification Action is not fired (PendingIntent)