I am trying to make notification with remove button, notifications are stored in SQLite DB and after the press, I want to remove "record" by id. Firstly I get a notification, store it to db , store unique id and pass it to a method which creates a notification. It gets fine in the method.
public static String CUSTOM_ACTION = "com.example.app.Services.REMOVE";
Intent snoozeIntent = new Intent(this, MyBroadcastReceiver.class);
snoozeIntent.setAction(CUSTOM_ACTION);
snoozeIntent.putExtra("NOT_WORKING", String.valueOf(id));
PendingIntent snoozePendingIntent =
PendingIntent.getBroadcast(this, 0, snoozeIntent, 0);
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
String channelId = getString(R.string.default_notification_channel_id);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setDefaults(Notification.DEFAULT_ALL)
.setSmallIcon(R.drawable.ic_stat_bell)
.setContentTitle("Loggly")
.setContentText(messageBody)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_delete_forever_black_24dp, "Remove", snoozePendingIntent);
then I have a broadcast receiver for handling data from notification. Here is how it looks in my manifest file.
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="com.example.app.Services.REMOVE"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
And here is the broadcast receiver the value which I got from extras is always null ... I have been trying to find a solution, but with no result.
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
String id;
if (extras != null) {
id = extras.getString("NOT_WORKING");
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager notificationManager = (NotificationManager) context.getSystemService(ns);
if (notificationManager != null) {
notificationManager.cancel(0);
}
Intent inten = new Intent(context, RemoveService.class);
inten.putExtra("NOT_WORKING", id);
context.startService(inten);
}
}
}
Finally, I am starting intent service, which should delete the "record" from the database, but when the broadcast receiver does not receive id it can't do anything.
I just created a sample notification on my side and the problem is the way you are creating the pendingIntent. Just add the proper flag to the pendingIntent parameter and it will work fine.
PendingIntent snoozePendingIntent = PendingIntent.getBroadcast(this, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
You can use FLAG_ONE_SHOT as well. If it satisfies your use case. You can go through different flags which can be used in PendingIntent
Related
I have an app already on playstore long time ago, recently the notifications is not open with users
this is my code
private void showNotification () {
PhoneUtils.clearAllNotifications(getApplicationContext());
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = “app”;
int notificationId = 100;
createNotificationChannel(channelId , notificationManager);
Notification notification = new NotificationCompat.Builder(getApplicationContext(), channelId)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getApplicationContext().getResources().getString(R.string.app_name))
.setContentText(mAlert)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.setContentIntent(getOpenNotificationIntent())
.setDefaults(Notification.DEFAULT_ALL)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.build();
notificationManager.notify(notificationId, notification);
}
private PendingIntent getOpenNotificationIntent () {
int requestID = (int) System.currentTimeMillis();
Intent intent = new Intent(“com.app.OPEN”);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_TYPE, mType);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_ID, mId);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_DIALOG_ID, mDialogId);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_MESSAGE_ID, mMessageId);
Notification notification = null;
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), requestID,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
<receiver
android:name=".fcm.OpenNotificationReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.app.OPEN" />
</intent-filter>
</receiver>
As of Android 8 (Oreo) you can no longer register a BroadcastReceiver for an implicit Intent in the manifest. That's what you are doing with this:
<receiver
android:name=".fcm.OpenNotificationReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.app.OPEN" />
</intent-filter>
</receiver>
Instead of this, you should use an explicit Intent as follows:
Change the manifest entry to this:
<receiver
android:name=".fcm.OpenNotificationReceiver">
</receiver>
and change the code you use to create the PendingIntent for the Notification to this:
Intent intent = new Intent(this, OpenNotificationReceiver.class);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_TYPE, mType);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_ID, mId);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_DIALOG_ID, mDialogId);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_MESSAGE_ID, mMessageId);
Notification notification = null;
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), requestID,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
For more information see https://developer.android.com/about/versions/oreo/background and search for "Broadcast limitations"
modify getOpenNotificationIntent method.
private PendingIntent getOpenNotificationIntent () {
int requestID = (int) System.currentTimeMillis();
Intent intent = new Intent(“com.app.OPEN”);
//add this line
intent.setPackage(getApplicationContext().getPackageName());
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_TYPE, mType);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_ID, mId);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_DIALOG_ID, mDialogId);
intent.putExtra(ForSaleConstants.ACTIVITY_NOTIFICATION_MESSAGE_ID, mMessageId);
Notification notification = null;
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), requestID,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
Having had issues with building for Android 12 - I thought I'd share this...
Android API version 31 (Android S) and above require any PendingIntent to have one of two flags set: FLAG_IMMUTABLE or FLAG_MUTABLE. (Android doc. 20) ServerPingWithAlarmManager.onCreate doesn’t do this, and so is throwing an IllegalArgumentException.
https://discourse.igniterealtime.org/t/android-api-31-requires-mutability-flag-for-pendingintent-serverpingwithalarmmanager-4-4-5-fails/91553
Without the Mutable or Immutable flag set PendingIntent.getBroadcast crashes.
In my app on a device running 'Pie' I trigger a notification with a try again action when a job (yigit/android-priority-jobqueue) fails. the action button "Try Again" calls the broadcast receiver onReceive as expected and the action name is correct but the intent extras are null. The channels have been created also.
The notification code is as follows:
NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = 123;
Intent intent = new Intent(getApplicationContext(), MyReceiver.class);
PendingIntent contentPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
Intent tryAgainIntent = new Intent(getApplicationContext(), MyReceiver.class);
tryAgainIntent.setAction(TRY_AGAIN_ACTION);
tryAgainIntent.putExtra(Extras.POST_ID, postId);
PendingIntent tryAgainPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, tryAgainIntent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), "com.example.android.GENERAL_NOTIFICATIONS")
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle("Post sync failed")
.setContentText("Syncing the post failed")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setWhen(System.currentTimeMillis())
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Syncing the post failed, try again?"))
.setContentIntent(contentPendingIntent)
.addAction(R.drawable.ic_logo, "Try Again", tryAgainPendingIntent)
.setAutoCancel(true);
// send the notification
manager.notify(notificationId, builder.build());
My receiver is as follows:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
if(TRY_AGAIN_ACTION.equals(action)) {
// gets here fine
Long postId = intent.getLongExtra(Extras.POST_ID, -1);
// postId is always -1
Long postId = (Long) extras.get(Extras.POST_ID);
// postId is always -1 here also
}
}
}
My manifest entry is:
<receiver
android:name=".receivers.MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="TRY_AGAIN_ACTION" />
</intent-filter>
</receiver>
I'm stumped as to why it all works except the intent extras in the receiver are null, any suggestions?
I have defined a service that issues notifications when an event occurs. I add the notification id as an extra in the intent so that, when the user opens the app through the notification, i can retrieve the id in the activity and cancel the specific notification.
The problem is that, if there are multiple notifications visible in the drawer at any time, all of them end up with the same Extra value. This is because of the FLAG_UPDATE_CURRENT behaviour. It will update the extras of any existing matching Intents with the new Extra. So now i cannot retrieve the correct notification id to cancel it.
Is there a way to work around this so that the previous Intents retain their Extra value.
According to the doc :
public static final int FLAG_UPDATE_CURRENT
Added in API level 3
Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.
Service onStartCommand() :
int notificationId = getId();
Intent contentIntent = new Intent(this,MainActivity.class);
contentIntent.putExtra(MainActivity.KEY_NOTIFICATION_ID, notificationId);
PendingIntent contentPendingIntent = PendingIntent.getActivity(this, REQUEST_CODE_OPEN_APP, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(MyApplication.getContext())
.setContentTitle("title")
.setContentText("text")
.setSmallIcon(R.drawable.icon)
.setContentIntent(contentPendingIntent);
notificationManager.notify(notificationId,notificationBuilder.build());
In the Activity's onCreate() :
Bundle bundle = getIntent().getExtras();
if(bundle != null){
int notificationId = bundle.getInt(KEY_NOTIFICATION_ID);
if(notificationId != 0){
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
}
}
One workaround is to have a unique request code for every PendingIntent. You can reuse the notification id itself. We can set an action for the intent as well, so that the activity can recognize the intent. So change the code to :
Service onStartCommand() :
public static final String ACTION_NOTIFIED = "ACTION_NOTIFIED"; //added
int notificationId = getId();
Intent contentIntent = new Intent(this,MainActivity.class);
contentIntent.putExtra(MainActivity.KEY_NOTIFICATION_ID, notificationId);
contentIntent.setAction(ACTION_NOTIFIED);//added
PendingIntent contentPendingIntent = PendingIntent.getActivity(this, notificationId, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);//changed
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(MyApplication.getContext())
.setContentTitle("title")
.setContentText("text")
.setSmallIcon(R.drawable.icon)
.setContentIntent(contentPendingIntent);
notificationManager.notify(notificationId,notificationBuilder.build());
In the Activity's onCreate() :
if(getIntent().getAction().equals(AppService.ACTION_NOTIFIED)){//added
Bundle bundle = getIntent().getExtras();
if(bundle != null){
int notificationId = bundle.getInt(KEY_NOTIFICATION_ID);
if(notificationId != 0){
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
}
}
}//added
I have a question related to creating notifications in Android with an attached action. My goal is to have an action that won't re-open my app but will simply perform some logic as specified by a class in my app. Here is my code to create said notification.
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, RetryReceiver.class);
final PendingIntent retryIntent = PendingIntent.getBroadcast(context, notificationId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(context)
.setContentTitle(title)
.setTicker(ticker)
.setContentText(message)
.setSmallIcon(R.drawable.notifcation_sprout_leaf)
.setLargeIcon(largeIcon)
.setAutoCancel(true);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
mNotifyBuilder.addAction(R.drawable.refresh_action_bar, "Retry", retryIntent);
}
// Creates an explicit intent for an Activity in your app
Intent mainIntent = new Intent(context, MainActivity.class);
// The TaskStackBuilder needs multiple intents in case there are multiple failures in succession
// Thus default it to have a MainActivity intent it can fall back on
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(mainIntent);
stackBuilder.addNextIntent(composeIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(notificationId, PendingIntent.FLAG_UPDATE_CURRENT);
mNotifyBuilder.setContentIntent(resultPendingIntent);
// Because the ID remains unchanged, the existing notification is updated.
notificationManager.notify(notificationId, mNotifyBuilder.build());
Here is my class to receive the broadcast:
public class RetryReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final Bundle bundle = intent.getExtras();
// do shit
}
}
I have also registered the receiver in the AndroidManifest.xml as such:
<receiver
android:name=".RetryReceiver"
android:enabled="true"
android:exported="true" >
</receiver>
For some reason, the code in my receiver is never being fired, anyone have any suggestions?
You need to call setContentIntent(retryIntent) on your mNotifyBuilder - it is not set automatically.
Attribute android:exported="true" is to alow the broadcast receiver receive messages from sources outside its application.
The absence of any filters means that it can be invoked only by Intent objects that specify its exact class name. This implies that the receiver is intended only for application internal use.
So android:exported should be declared as android:exported="false" or not declared because it defaults to false in that case.
As I can't see no other problem with your code. Please try with android:exported="false"
See Receiver Android documentation
I have an app that has an ongoing notification to help with memorization. I want to be able to dismiss this notification with one of the action button, but I don't want to open the app when the button is hit. I would prefer to use the built-in notification action buttons and not create a RemoteViews object to populate the notification. I saw one post mention using a BroadcastReceiver on this button which is received in my app, and though his tutorial was quite unhelpful, it sounds like this is headed in the right direction.
Intent resultIntent = new Intent(getBaseContext(), Dashboard.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(getBaseContext());
stackBuilder.addParentStack(Dashboard.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
Intent cancel = new Intent(getBaseContext(), CancelNotification.class);
stackBuilder.addParentStack(Dashboard.class);
stackBuilder.addNextIntent(cancel);
PendingIntent pendingCancel =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
NotificationCompat.Builder mb = new NotificationCompat.Builder(getBaseContext());
mb.setSmallIcon(R.drawable.cross_icon);
mb.setContentTitle(ref);
mb.setContentText(ver);
mb.setPriority(NotificationCompat.PRIORITY_LOW);
mb.setOngoing(true);
mb.setStyle(new NotificationCompat.BigTextStyle().bigText(ver));
mb.setContentIntent(resultPendingIntent);
mb.addAction(R.drawable.ic_cancel_dark, "Dismiss", pendingCancel);
manager.notify(1, mb.build());
Start with this:
int final NOTIFICATION_ID = 1;
//Create an Intent for the BroadcastReceiver
Intent buttonIntent = new Intent(context, ButtonReceiver.class);
buttonIntent.putExtra("notificationId",NOTIFICATION_ID);
//Create the PendingIntent
PendingIntent btPendingIntent = PendingIntent.getBroadcast(context, 0, buttonIntent,0);
//Pass this PendingIntent to addAction method of Intent Builder
NotificationCompat.Builder mb = new NotificationCompat.Builder(getBaseContext());
.....
.....
.....
mb.addAction(R.drawable.ic_Action, "My Action", btPendingIntent);
manager.notify(NOTIFICATION_ID, mb.build());
Create the BroadcastReceiver:
public class ButtonReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int notificationId = intent.getIntExtra("notificationId", 0);
// Do what you want were.
..............
..............
// if you want cancel notification
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
}
If you don´t want show any activity when user click on notification, define the intent passed in setContentIntent in this way:
PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, new Intent(), 0);
......
......
mb.setContentIntent(resultPendingIntent);
To close notification tray when clicked, call setAutoCancel() with true when building the notification: mb.setAutoCancel(true);
The accepted solution is not working in Android 8.1 and onwards.
Follow the same steps as in the accepted answer, but update this line:
//Create the PendingIntent
PendingIntent btPendingIntent = PendingIntent.getBroadcast(context, 0, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
See also PendingIntent.FLAG_UPDATE_CURRENT
I have tried using answer from #ramaral , but it is not working as the notificationId at the onReceive method always -1. I have found the solution for this problem by configuring
from
PendingIntent.getBroadcast(context, 0, approvedIntent, 0)
to
PendingIntent.getBroadcast(
context,
notificationId,
approvedIntent,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
Here is an example that you can follow.
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
val notificationId = Random.nextInt()
//Making Approve Intent for Button Action
val approvedIntent = Intent(context,
BroadcastReceiverOuting::class.java).apply {
action = ActionOuting.ACTION_APPROVED.name
putExtra(
Constant.NOTIFICATION_ID,
notificationUid
) //Compulsory to dismiss this notification
putExtra(Constant.STUDENT_IC, studentIc)
putExtra(Constant.OUTING_ID, outingId)
}
val approvedPendingIntent: PendingIntent =
PendingIntent.getBroadcast(
context,
notificationUid,
approvedIntent,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
val notification: Notification =
notificationCompatBuilder
.setStyle(bigTextStyle)
.setContentTitle(title)
.setContentText(description)
.setSmallIcon(R.drawable.ic_logo_128)
.setContentIntent(notifyPendingIntent)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setAutoCancel(true)
.addAction(
R.drawable.ic_baseline_approve_check_24,
context.getString(R.string.approve),
approvedPendingIntent
)
.build()
NotificationManagerCompat.from(context.applicationContext).apply {
notify(notificationUid, notification)
}
}
And then at the BroadcastReceiverOuting
override fun onReceive(context: Context?, intent: Intent?) {
if (intent != null && intent.action != null) {
val notificationUid = intent.getIntExtra(Constant.NOTIFICATION_ID, -1)
val studentIC = intent.getStringExtra(Constant.STUDENT_IC)!!
val outingID = intent.getStringExtra(Constant.OUTING_ID)!!
//Do something...
//To dismiss the notification
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(notificationUid)
}
}
Lastly, don't forget the AndroidManifest.xml .
<receiver
android:name=".utils.BroadcastReceiverOuting"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>