Android - Remove action button from notification - android

I want to dismiss the notification action buttons (not the whole notification) when clicking on those action buttons. (Lets say: a download notification with stop action button. When click on stop, dismiss stop button and change contentText to 'Download canceled')
The only thing it comes to my mind is to cancel notification and notify another one with the same id, but this seems to be an ugly workaround...
So, is there any way to remove action buttons from notifications?
(i think there is no need to put any code, but I will if its necessary...)

If you are using the NotificationCompat.Builder from the v4 Support Library, you can simply access the builder's action collection directly (Unfortunately no public mutators are provided).
The following will do the trick (Of course you must update re-notify):
NotificationCompat.Builder notifBuilder = NotificationCompat.Builder(context);
...
notifBuilder.mActions.clear();

I am using following workaround:
NotificationCompat.Builder builder = //existing instance of builder
//...
try {
//Use reflection clean up old actions
Field f = builder.getClass().getDeclaredField("mActions");
f.setAccessible(true);
f.set(builder, new ArrayList<NotificationCompat.Action>());
} catch (NoSuchFieldException e) {
// no field
} catch (IllegalAccessException e) {
// wrong types
}
from here: https://code.google.com/p/android/issues/detail?id=68063
Note:
Proguard may break the button clearing in obfuscated build. Fix is to add the following two lines in proguard-rules.pro
-keep class androidx.core.app.NotificationCompat { *; }
-keep class androidx.core.app.NotificationCompat$* { *; }

I had the same problem and found a solution for this.
I created another builder and added two "empty" actions like this:
builder.addAction(0, null, null);
builder.addAction(0, null, null);
(one for each button I had, so if you have three, call it three times).
Then when calling Notify, it removes the buttons.

Even though the accepted answer works, as per documentation, the designed way to do this is by using NotificationCompat.Extender class. For example in Kotlin:
private val clearActionsNotificationExtender = NotificationCompat.Extender { builder ->
builder.mActions.clear()
builder
}
private val notificationBuilder by lazy {
NotificationCompat.Builder(context)
.addAction(R.drawable.ic_play_arrow, "Play", playPendingIntent)
}
private fun updateNotification(){
notificationBuilder
.extend(clearActionsNotificationExtender) // this will remove the play action
.addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)
}

NotificationCompat.Builder notifBuilder = NotificationCompat.Builder(context);
remove Whole Action Button :
builder.mActions.clear();
for remove special action button :
builder.mActions.remove(index);
finally :
notificationManager.notify(notificationID, builder.build());

Android provides the notification area for alerting users about the events that have occurred. It also provides a notification drawer that user can pull down to see more detailed information about the notification.
Notification Drawer consists of
View (contains tittle,detail,small icon)
Action ( any action which may occur in case the user clicks the notification drawer view)
To set up a notification so it can be updated, issue it with a notification ID by calling NotificationManager.notify(ID, notification). To update this notification once you've issued it, update or create a NotificationCompat.Builder object, build a Notification object from it, and issue the Notification with the same ID you used previously.
mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Sets an ID for the notification, so it can be updated
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("New Message")
.setContentText("You are downloading some image.")
.setSmallIcon(R.drawable.ic_stop)
numMessages = 0;
// Start of a loop that processes data and then notifies the user
...
mNotifyBuilder.setContentText("Download canceled")
.setNumber(++numMessages);
// Because the ID remains unchanged, the existing notification is
// updated.
mNotificationManager.notify(
notifyID,
mNotifyBuilder.build());
...

Related

Bundled notification replaces the first notification

When creating a bundled notification using setGroup() and setGroupSummary() I am having some strange issues regarding the behaviour of the notifications.
So, as a reference. This example contains the issue:
var isFirstNotificationInGroup = true
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager.activeNotifications.forEach {
if (it.notification.group == groupId) {
isFirstNotificationInGroup = false
}
}
}
val builder = NotificationCompat.Builder(this, channelId).apply {
color = resources.getColor(R.color.colorAccent)
priority = NotificationCompat.PRIORITY_MAX
setSmallIcon(R.drawable.ic_dotoo_logo)
setContentTitle(title)
setContentText(body)
setStyle(NotificationCompat.BigTextStyle()
.bigText(body))
setAutoCancel(true)
setCategory(NotificationCompat.CATEGORY_SOCIAL)
setGroup(groupId)
setGroupSummary(isFirstNotificationInGroup)
}
< ... >
with(NotificationManagerCompat.from(this)) {
notify(notificationId, builder.build())
}
What happens?
The first notification will be shown as it should. So no issues here.
Then, when we show the second notification. It replaces the first one. This shouldn't happen. And no, it is not due to the notification ID. That's not related to this as far as I know.
But, when we show a third (or more) notification, the bundle works as expected and shows two (or more) bundled notifications. But the first one is... gone.
Thanks in advance for helping me.
I have fixed it by creating a seperate summary notification when isFirstNotificationInGroup is true.
This will be send just before the 'real' notification will be send.

How to get id of a particular Notification

Hi I have created an Notification class from which I can create multiple notification like this:
int id=0;
id++;
notification = new NotificationCompat.Builder(this)
.setContentTitle(title)
.setSmallIcon(icon)
.setContentText(dataNotes)
.setWhen(time)
.setAutoCancel(false)
.setOngoing(true)
.addAction(action)
.setStyle(new NotificationCompat.BigTextStyle().bigText(dataNotes))
.build();
nm.notify(id,notification);
So, my question is how can I get id of each notification that i create.I want to use that id when i want to cancel a particular notification.
I know StatusBarNotification contains getId()method to get id but i don't know how to implement it.Can anyone help me.
Simply declare your notification Id as static. Every time you create a notification a unique Id will be set corresponding to each notification.
This will fetch you the intent id every time you perform a action on the notification.
Create a notification button as follows to perform some action on each notification.
public ActionButtonConfig getEditCityButton(CountryConfig countryConfig,
CityConfig cityConfig, Integer notificationId) {
ArrayList<Class> params = new ArrayList<>();
params.add(context.getClass());
params.add(countryConfig.getClass());
params.add(cityConfig.getClass());
params.add(notificationId.getClass());
ArrayList<Object> arguments = new ArrayList<>();
arguments.add(context);
arguments.add(countryConfig);
arguments.add(cityConfig);
arguments.add(notificationId);
return getActionButton(NotificationButton.ACTION, getParamArray(params),
arguments.toArray());
}
This Action Button Config will provide you enabling any action you need to perform on the notification.
public static int notificationCount = 0;
void buildNotification(NotificationConfig notification) {
try {
buildNotification(//get items from notification config here and also notificationCount);
notificationCount++;
} catch (Exception e) {
Log.e("Notification builder err",e);
}
}
In the above code see that notification count is static and every time a notification is generated, notification count is updated and previous value can be used at later stage henceforth.
Further notifying using Notification Manager to generate the notification as done by you in the last line.
Hope this works. Cheers

How can I avoid blinking notification update while changing button

I have a Notification, which supports play,pause forward and back.
private static Notification createNotification(String interpret, String title, boolean paused) {
// if (builder == null)
builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MAX);
builder.setAutoCancel(false);
builder.setContentTitle(title);
builder.setContentText(interpret);
builder.setOngoing(true);
builder.setOnlyAlertOnce(true);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentIntent(PendingIntent.getActivity(context, 9, new Intent(context, ApplicationActivity.class), Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT));
builder.addAction(R.drawable.av_previous, "", PendingIntent.getBroadcast(context.getApplicationContext(), 0, new Intent(NotificationPlayerControlReceiver.MUSIC_PLAYER_INTENT).putExtra("resultcode", NotificationPlayerControlReceiver.PREVIOUS), PendingIntent.FLAG_CANCEL_CURRENT));
if (paused)
builder.addAction(R.drawable.av_play, "", PendingIntent.getBroadcast(context.getApplicationContext(), 2, new Intent(NotificationPlayerControlReceiver.MUSIC_PLAYER_INTENT).putExtra("resultcode", NotificationPlayerControlReceiver.PLAY), PendingIntent.FLAG_CANCEL_CURRENT));
else
builder.addAction(R.drawable.av_pause, "", PendingIntent.getBroadcast(context.getApplicationContext(), 3, new Intent(NotificationPlayerControlReceiver.MUSIC_PLAYER_INTENT).putExtra("resultcode", NotificationPlayerControlReceiver.PAUSE), PendingIntent.FLAG_CANCEL_CURRENT));
builder.addAction(R.drawable.av_next, "", PendingIntent.getBroadcast(context.getApplicationContext(), 1, new Intent(NotificationPlayerControlReceiver.MUSIC_PLAYER_INTENT).putExtra("resultcode", NotificationPlayerControlReceiver.NEXT), PendingIntent.FLAG_CANCEL_CURRENT));
Notification notification = builder.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
notification.tickerView = null;
return notification;
}
Updating the notification:
public static void update(String interpret, String title, boolean paused) {
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(0, createNotification(interpret, title, paused));
}
To avoid blinking on update, I´ve set the builder to a global variable and I reuse it on every update, which works great. but reusing it, means that also all buttons I´ve added are reused and there is no possibility to remove Actions I´ve added before.
The button change only works, if I reinitialize the NotificationCompat.Builder on every update, which means I get the blinking again.
How do I avoid blinking, but letting the button change?
EDIT:
Just checked out Rocket Player, they didn´t solve the problem too, but Google Play Music did
Like Boris said, the problem is that a new notification will be build every update.
My solution covers the same logic, but I use the NotificationBuilder...
here is the code:
if (mNotificationBuilder == null) {
mNotificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(iconId)
.setContentTitle(title)
.setContentText(message)
.setLargeIcon(largeIcon)
.setOngoing(true)
.setAutoCancel(false);
} else {
mNotificationBuilder.setContentTitle(title)
.setContentText(message);
}
keep in mind that mNotificationBuilder is a private field in the class.
The issue is that you create new notification every time you want to update. I had the same issue and it fixed when I did the following:
retain the instance of the notification inbetween different calls of createNotification.
set this instance to null every time it is removed from the notification bar.
do the following code:
Code:
private static Notification createNotification(String interpret, String title, boolean paused) {
if (mNotification == null) {
// do the normal stuff you do with the notification builder
} else {
// set the notification fields in the class member directly
... set other fields.
// The below method is deprecated, but is the only way I have found to set the content title and text
mNotification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
}
return mNotification;
}
And now when you call notify no blinking will appear:
manager.notify(0, createNotification(interpret, title, paused));
PS: I also faced a problem that if I executed setLatestEventInfo the large and small icons got scrwed up. That's why I did:
int tmpIconResourceIdStore = mNotification.icon;
// this is needed to make the line below not change the large icon of the notification
mNotification.icon = 0;
// The below method is deprecated, but is the only way I have found to set the content title and text
mNotification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
mNotification.icon = tmpIconResourceIdStore;
Looking into Adnroid ccode this line mNotification.icon = 0; disables the icon screw up.
I know that this is a rather old question, but since I didn't found a solution anywhere else, I thought answering this now might help others with the same problem.
This problem is kind of tricky to begin with. I encountered it today as well, and being my stubborn self, I found a solution after searching and trying for a while.
How to solve this problem:
In order to be compatible with API-Levels lower than 19, my solution is to use the NotificationCompat classes from the support-library.
As suggested by others, I keep the reference to the NotificationCompat.Builder for as long as the notification is required. The actions I use in my Notification are only added upon initial creation of the Builder, and those actions that will change depending on the situation, I also store in a private member of the service. Upon change, I re-use the Builder object and adjust the NotificationCompat.Action object according to my needs. Then I call the Builder.getNotification() or Builder.build() method, depending on API-level (probably not necessary due to the support-libs, but I didn't check that. If I can omit that, please write a comment, so I can improve my code ;)
Here's an example code of what I just described above:
public Notification createForegroundNotification(TaskProgressBean taskProgressBean, boolean indeterminate) {
Context context = RewardCalculatorApplication.getInstance();
long maxTime = TaskUtils.getMaxTime(taskEntry);
long taskElapsedTime = TaskUtils.calculateActualElapsedTime(taskProgressBean);
long pauseElapsedTime = taskProgressBean.getPauseElapsedTime();
int pauseToggleActionIcon;
int pauseToggleActionText;
PendingIntent pauseToggleActionPI;
boolean pauseButton = pauseElapsedTime == 0;
if(pauseButton) {
pauseToggleActionIcon = R.drawable.ic_stat_av_pause;
pauseToggleActionText = R.string.btnTaskPause;
pauseToggleActionPI = getPendingIntentServicePause(context);
} else {
pauseToggleActionIcon = R.drawable.ic_stat_av_play_arrow;
pauseToggleActionText = R.string.btnTaskContinue;
pauseToggleActionPI = getPendingIntentServiceUnpause(context);
}
String contentText = context.getString(R.string.taskForegroundNotificationText,
TaskUtils.formatTimeForDisplay(taskElapsedTime),
TaskUtils.formatTimeForDisplay(pauseElapsedTime),
TaskUtils.formatTimeForDisplay(taskProgressBean.getPauseTotal()));
// check if we have a builder or not...
boolean createNotification = foregroundNotificationBuilder == null;
if(createNotification) { // create one
foregroundNotificationBuilder = new NotificationCompat.Builder(context);
// set the data that never changes...plus the pauseAction, because we don't change the
// pauseAction-object, only it's data...
pauseAction = new NotificationCompat.Action(pauseToggleActionIcon, getString(pauseToggleActionText), pauseToggleActionPI);
foregroundNotificationBuilder
.setContentTitle(taskEntry.getName())
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(getPendingIntentActivity(context))
.setOngoing(true)
.addAction(R.drawable.ic_stat_action_done, getString(R.string.btnTaskFinish), getPendingIntentServiceFinish(context))
.addAction(pauseAction);
}
// this changes with every update
foregroundNotificationBuilder.setContentText(contentText);
if(indeterminate) {
foregroundNotificationBuilder.setProgress(0, 0, true);
} else {
foregroundNotificationBuilder.setProgress((int) maxTime, (int) taskElapsedTime, false);
}
// if this is not the creation but the button has changed, change the pauseAction's data...
if(!createNotification && (pauseButton != foregroundNotificationPauseButton)) {
foregroundNotificationPauseButton = pauseButton;
pauseAction.icon = pauseToggleActionIcon;
pauseAction.title = getString(pauseToggleActionText);
pauseAction.actionIntent = pauseToggleActionPI;
}
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
? foregroundNotificationBuilder.getNotification() // before jelly bean...
: foregroundNotificationBuilder.build(); // since jelly bean...
}
The variables foregroundNotificationBuilder, pauseAction and foregroundNotificationPauseButton are private members of the service class.
The getPendingIntent...() methods are convenience methods that simply create the PendingIntent objects.
This method is then called when I need to update the notification using the NotificationManager, as well as handed over to the service's startForeground() method. This solves the flickering and the problems with the not updatable actions in the notification.

NotificationManager.cancel(id) is not working inside a broadcast receiver

Android: I am trying to cancel a notification from the notification bar after a package being installed.
What I am doing is the following:
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
Uri data = intent.getData();
//some code goes here
//get the id of the notification to cancel in some way
notificationhelper._completeNotificationManager.cancel(id);
}
}
}
where
public class notificationhelper {
public static NotificationManager _completeNotificationManager = null;
public void complete() {
if (_completeNotificationManager == null)
_completeNotificationManager = (NotificationManager) _context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(
R.drawable.notification,
_context.getString(R.string.notification),
System.currentTimeMillis());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.flags |= Notification.FLAG_NO_CLEAR;
_completeNotificationManager.notify(TEXT, id, notification);
}
}
But the notificationhelper._completeNotificationManager.cancel(id) does not work. I tried to use notificationhelper._completeNotificationManager.cancelAll(); and it works. What I am doing wrong?
In my experience, you can't cancel all notifications with a particular ID, regardless of tag.
That is, if you create two notifications like so:
notificationManager.notify(TAG_ONE, SAME_ID, notification_one);
notificationManager.notify(TAG_TWO, SAME_ID, notification_two);
Then, notificationManager.cancel(SAME_ID) won't cancel either of them! I suspect that this is because the "tag" field, if unspecified in notify() and cancel(), defaults to null, which you have to cancel explicitly.
So, to cancel these two notifications, you have to call:
notificationManager.cancel(TAG_ONE, SAME_ID);
notificationManager.cancel(TAG_TWO, SAME_ID);
In your case, you're supplying "TEXT" as the tag but cancelling just using the id, which defaults to using tag=null.
So, either don't provide TEXT as your tag:
_completeNotificationManager.notify(id, notification);
Or, if you need separate notifications and don't want them to clobber each other, keep track of the active tags:
_completeNotificationManager.notify(TEXT, id, notification);
collectionOfActiveTags.add(TEXT);
...
for (String activeTag : collectionOfActiveTags)
notificationhelper._completeNotificationManager.cancel(activeTag, id);
I wish that what you're trying to do was supported, as it seems that it should be.
Well this is probably irrelevant at this point, but it should be posted here so that people like me dealing with the same problem might find the solution.
If NotificationManager.cancel() isn't working, try changing the ID for the notification.
notificationManager.notify(NOTIFICATION_ID, notification);
When I changed NOTIFICATION_ID from 1 to [RANDOM_NUMBER], it magically started working. I assume that 1 is somehow reserved, although there is no note in any documentation...
An of course make sure you use the same NOTIFICATION_ID to cancel:
notificationManager.cancel(NOTIFICATION_ID);
My notifications were not getting removed because my service was Foreground Service and NOT a regular service started by StartService.
If your service is foreground, call stopForeground(true) instead of stopself(). So now my code looks like this:
NotificationManagerCompat.from(this).cancel(NotificationHelper.PLAYER_NOTIFICATION_ID);
stopForeground(true);
and it worked, notification was removed.
I was facing the same issue recently. I have managed to solve it.
So from what i understood.
use the id which is basically a random number to notify and send this same id to the piece of code (receiver/activity...) where you want to cancel it.
When using tags, it seems to not work for me as I was giving one tag to all notifications but with unique id. It worked only on the first tag so I completely avoided using tags. If you want to use tags, issue unique tags along with unique id and use them both while cancelling.
So final answer... what I used and what works for me:
STEP 1:
int notif_id = (int)(System.currentTimeMillis()%10000);
STEP2: add this id inside the action intent (I am launching an activity where the notification gets cancelled on the action click):
Intent notificationSettingsIntent = new Intent(context.getApplicationContext(), NotificationSettingsActivity.class);
notificationSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
notificationSettingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
notificationSettingsIntent.putExtra("fromNotification",true);
notificationSettingsIntent.putExtra("notif_id",notif_id);
PendingIntent notificationSettingsActivityPendingIntent = PendingIntent.getActivity(context,notif_id,notificationSettingsIntent,PendingIntent.FLAG_ONE_SHOT);
STEP 3: notify using the id in the step 1 but with no tags
NotificationManagerCompat notificationCompat = NotificationManagerCompat.from(context.getApplicationContext());
notificationCompat.notify(notif_id,notificationBuilder.build());
Now in the Activity which gets opened by my action click, I cancel the notification as:
NotificationManagerCompat notificationCompat = NotificationManagerCompat.from(context.getApplicationContext());
notificationCompat.cancel(getIntent().getIntExtra("notif_id"));
Works every time now.
Sorry for late joining!
But following worked fine for me.
NotificationManagerCompat mNotificationManager = NotificationManagerCompat.from(context.getApplicationContext());
mNotificationManager.cancel("<TAG>",<Notificatoin-id>);
Following worked for me:
final NotificationManagerCompat mNotificationManager = NotificationManagerCompat.from(context.getApplicationContext());
mNotificationManager.cancel(<Notificatoin-id>);
Since there is no accepted answer, I am posting another one with same scenario I faced
private fun stopForegroundService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_DETACH)
}else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N){
stopForeground(true)
}
notificationManager.cancel(NOTIFICATION_ID)
}
Point to note is first you need to set stopForeground(false) then call notificationManager.cancel(NOTIFICATION_ID)
If you change the order, it won't work

Activity not starting from Status Bar Notification

I've found a lot of Stack Overflow posts about this issue but they all use the now deprecated Notification methods instead of using the Notification.Builder class.
I'm successfully creating status bar notifications, but when clicked nothing happens. A warning in LogCat shows up:
Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy#412d6df0
Here is the code I'm using to build my Notification:
public class LunchNotificationBuilder {
private Notification notification;
public LunchNotificationBuilder(Lunch lunch, Context context) {
Builder builder = new Builder(context);
Calendar reminderTime = (Calendar)lunch.getReminderTime().clone();
builder.setWhen(reminderTime.getTimeInMillis());
builder.setTicker("Reminder for " + lunch.getTitle());
builder.setContentTitle("LunchBunch Notification");
builder.setContentText("Upcoming lunch at " + lunch.getTitle());
builder.setSmallIcon(R.drawable.ic_launcher);
Intent intent = new Intent(context, InviteDetails.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT));
this.notification = builder.getNotification();
}
public Notification getNotification() {
return this.notification;
}
}
The Lunch class is just a data structure I created, don't worry about that.
The Context being passed in is Application.getApplicationContext().
I'm setting the Intent.FLAG_ACTIVITY_NEW_TASK flag as suggested by the documentation, and the PendingIntent has the PendingIntent.FLAG_ONE_SHOT flag.
Another note: The activity I'm trying to launch (InviteDetails) works just fine when I explicitly call startActivity elsewhere in the code. Something about this PendingIntent business isn't working.
Turns out I don't know how to use Android. I was clicking the status bar at the top, not dragging down to see the full notification before clicking. my code was working. doh!

Categories

Resources