Handle multiple Notifications / Stacking Notifications from GCM - android

I just implemented GCM and notifications in my Android app, coming from an Apache/PHP-based webserver.
The notifications are already working, but I'm stuck at stacking the notifications, as described here.
What I'm trying to do
I have two types of notifications in my app, using data coming from the GCM Service:
Type 1 (Messages):
[data] => Array
(
[t] => 1
[other data...]
)
Type 2 (News):
[data] => Array
(
[t] => 2
[other data...]
)
These 2 types are completely different notifications, and I would like to stack both of them separate from each other, but I can't get this to work.
I would like to stack them like this, as soon as there are multiple notifications:
Default View
Expanded View
What I tried
2 Notification IDs and Atomic Integer
I tried to use 2 different notification IDs, so that notifications with the same type get overidden.
if (msg.get("t").toString().equals("1")) {
notificationNumber = messageCounter.incrementAndGet();
} else {
notificationNumber = newsCounter.incrementAndGet();
}
[...]
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setNumber(notificationNumber);
If 2 messages are sent at the same time, everything works fine and the counter shows 2. But if there is a short delay between two notifications, the counter switches to 1.
Unique Notification IDs
I also tried to use unique IDs generated with
Date now = new Date();
Notification_id = now.getTime();
so that there isn't no stacking or overriding at all.
Question
How can I solve my problem? Can I access the content of the previously sent notifications, so that I can show every message in one line, like in the expanded view of Gmail? How can I check which / how many notifications are currently displayed?
Long question, thank you very much!

I finally found the solution and ended up using atomic integers, but in a seperated class:
import java.util.concurrent.atomic.AtomicInteger;
public class Global {
public static AtomicInteger Counter1 = new AtomicInteger();
public static AtomicInteger Counter2 = new AtomicInteger();
}
To reset the counter after the application opening, i put this in my MainActivity (called in onCreate() and onResume():
private void clearNotifications(){
NotificationManager mNotificationManager;
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancelAll();
Global.Counter1.set(0);
Global.Counter2.set(0);
}
When I create the notification, I check the counter:
Counter1 = Global.Counter1.incrementAndGet();
ContentText = (Counter1 < 2) ? /* Single notification */ : /* Stacking */;

Related

Notifications jumping one over the other on update

I have a service that performs several downloads simultaneously in different threads. For each download, a notification with progress is displayed. Progress is updated about once a second. If you run three or more downloads at the same time, when you update the notification, they randomly change their positions in the status bar, jumping one over the other.
I tried setting the priority setPriority (priority), I used setOnlyAlertOnce (true), but this does not help. At the same time, in some other applications I saw that notifications are updated in their places.
The question is, how can this be achieved?
At the moment, my notifications are created like this:
private LongSparseArray<Task> mTasksArray = new LongSparseArray<>();
private int notifyId = 0;
...
//setup notification id on task download start
private void initTask(long taskId) {
Task task = new Task();
...
task.setNotificationId(notifyId++);
mTasksArray.put(taskId, task);
}
...
//notification update, called about once a second for every running download
private void showNotify(Task task) {
int notificationId = task.getNotificationId();
Notification notification = getProgressNotification(task);
mNotifyManager.notify(notificationId, notification);
}
#NonNull
private Notification getProgressNotification(Task task) {
int max = task.getTotal();
int count = task.getCount();
/**
* using id of notification as priority, but actual priority values can be only from -2 to 2
* right now it can show three first downloads correctly (for id values 0,1,2), but if,
* for example, stop first task and start another one, next id and priority will be 3
* (actually priority will be 2, because max priority is 2) and 3th and 4th notifications
* will reorder on every update, because of same priority
*/
int priority = task.getNotificationId();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID);
builder.setContentTitle(mContext.getString(R.string.notif_title_downloading))
.setOnlyAlertOnce(true)
.setOngoing(true)
.setPriority(priority)
.setSmallIcon(R.drawable.ic_file_download_white_24dp)
.setProgress(max, count, false)
.addAction(getPauseAction(task))
.addAction(getStopAction(task))
.setContentText(String.format(Locale.ENGLISH, "%d/%d", count, max));
return builder.build();
}
Updated.
You need to use the same notification builder for each subsequent update. For example:
//First time
NotificationCompat.Builder builder = new NotificationCompat.Builder(...)
...
notificationManager.notify(id, builder.build());
//Second time
builder.setProgress(max, count, false);
notificationManager.notify(id, builder.build());

SQL and Notifications

Currently I'm setting up an alarm which when activated displays a notification to the user.
I can now set the text from my SQL to my notification but its only the first row from my database each time. Now I've come up with two possible solutions which are only theory's.
Is there a way in which each time the database is called to the notification it will move onto the next result?
As I mentioned above I'm setting an alarm and when the alarm goes off it then calls the notification. Now In my pending intent for the alarm I'm giving it a id which you can see as id is there a way of giving it to my notification ID and then setting up a statement where if the notification ID is equal to or in the list of my alarm ID's stored on my SQL then it will search for the correct text to input.
Pending Intent....
pendingIntent = PendingIntent.getBroadcast(v.getContext(), id, receiver,PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, myCalendar.getTimeInMillis(), pendingIntent);
intentArrayList.add(pendingIntent);
Broadcast....
MyDatabaseHandler myDatabaseHandler = new MyDatabaseHandler(context, null, null, 1);
Cursor cursor = myDatabaseHandler.getAllProducts();
// Information we are trying to acquire but can only get first result
String name = cursor.getString(cursor.getColumnIndexOrThrow("alarm_name"));
String date = cursor.getString(cursor.getColumnIndexOrThrow("alarm_date"));
// Alarm ID FROM SQL which we want to match with the NOTIFICATION ID.....
String alarm_request_code = cursor.getString(cursor.getColumnIndexOrThrow("alarm_code"));
////
Log.i("The reciever is working", "perfect");
//create an intent to service
Intent service_Intent = new Intent(context, MessageService.class);
context.startService(service_Intent);
//Notification
NotificationManager notificationManager = (NotificationManagercontext.getSystemService(context.NOTIFICATION_SERVICE);
Intent moveActivity = new Intent(context, AlarmActivity.class);
moveActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Works with moveActivity to move the user back to main application.
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,moveActivity, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentIntent(pendingIntent)
.setContentTitle(name)
.setContentText(date)
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true);
notificationManager.notify(Integer.valueOf(alarm_request_code), builder.build());
}
}
////////////////////////////////////
Clearing up some issues
Whenever my alarm goes off currently a notification is sent to the user with a static name and description.
What I'm trying to accomplish is that when this alarm goes off, my notification is either given an ID matching the one of Alarm or looks up the alarm to then populate the text and title of the notification with the active alarms details.
This is what my current layout looks like inside of my SQLiteOpenHelper
private static final String DATABASE_NAME = "alarmList.db";
public static final String TABLE_NAME = "alarm_list";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_ALARMTIME = "alarm_time";
public static final String COLUMN_AlARMDATE = "alarm_date";
public static final String COLUMN_ALARMNAME = "alarm_name";
// Alarm request code column for working with switch and
public static final String COLUMN_ALARMRC = "alarm_code";
Followed by default oncreate and update methods
You could do this a few different ways. One would be to add a last_used date column, or a use_count column, and update that each time the record is used. You would then have a query that selects the least used item from your table.
Another way would be to store your alerts within your application and track that information there. The downside of that would be that, unless you store that count/last-used information out (probably to an external stats file) you would lose those counts when you restarted the application.
The concept would really be the same any way you were to approach this. You need to store usage information about your alerts somewhere.
I think this is what you are asking for, but after re-reading your question a few times, I am not as certain. Maybe you could provide some samples of your table structure and some of the data from those tables, and also provide an example of what you are trying to accomplish. If what I have here in my answer isn't what you are looking for, no worries. I will either update it to be more in-line with what you need or remove it altogether. Just want to make sure it is what you are looking for.
More on the last_used query, as requested:
You would want to write a query similar to:
select alert_id, alert_group, alert_text
from alerts
where alert_group = #parm_alert_group
order by last_used asc
limit 1 -- this may be different depending on the flavor of DBMS used
-- Oracle does not have "limit", but can be reproduced in other ways.
;
Then, when you use that alert item, you would want to update that record with the current datetime so that the next time your select query returns the record used longest ago.
update alerts set last_used = now() where alert_id = #parm_alert_id;
You could of course perform both of those actions within a function that returns the alert information and updates the last_used column before returning it, but that is just bonus functionality. The same idea is there using two separate calls, or a single call.

How to count Notification of Any application

Hello i make one example which show count of notification which user got, with help of NotificationListenerService what i actually implemented yet...
public void onNotificationPosted(StatusBarNotification sbn) {
final String pkgnm = sbn.getPackageName();
int ID = sbn.getId();
}
but my problem is some application notify twice and also there time milisec is different and key also different.
So how can i filter the notification only once.
Thanks in Advance

NotificationManager failing to display notification with "notify: id corrupted: sent 1, got back 0" warning

I've had a problem with NotificationManager in my app for the past couple of days and I don't seem to be getting closer to solving it.
I have a very simple service which does not do anything at the moment. It is just supposed to display notification:
public class UpdateService extends Service {
private static final String TAG = "UpdateService";
private static int NOTIFICATION_ID = 1;
private UpdateServiceBinder binder = new UpdateServiceBinder();
#Override
public void onCreate() {
Log.i(TAG, "Service created");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service started");
sendNotification(100);
return Service.START_NOT_STICKY;
}
private void sendNotification(int updatedItems) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.icon)
.setContentTitle("Sync")
.setContentText("Updated " + updatedItems);
Intent resultIntent = new Intent(getBaseContext(), MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(getApplicationContext());
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendindIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendindIntent);
NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(NOTIFICATION_ID, builder.build());
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public boolean onUnbind(Intent intent) {
return true;
}
public class UpdateServiceBinder extends Binder {
public UpdateService getService() {
return UpdateService.this;
}
}
}
Unfortunately, when the service is started and the notification is supposed to be displayed nothing happens. The service is definitely started according to log messages. At the same time there is warning from NotificationManager:
11-30 23:24:34.620: I/UpdateService(28356): Service created
11-30 23:24:34.800: I/UpdateService(28356): Service started
11-30 23:24:34.808: W/NotificationManager(28356): notify: id
corrupted: sent 1, got back 0
I tried using different numbers than 1 but it did not help. I am not sure what to make out of it. The code I'm using comes from the Android documentation on service in here and notifications in here. When I isolate the code in separate clean app which is setup in similar way to the one I'm working on notification is displayed correctly. Has anybody had this problem before or has any ideas?
Any help appreciated.
Thanks!
I had this problem then I remember that I had disabled the "Show Notification" from my "App info". I enabled it and notifications are back again.
Go to Settings > Apps (Application Manager).
Tap the app you want to stop.
Tap to uncheck the box for Show notifications.
FYI it might differ from one android device to the other, however they are all more or less the same.
Startin from API 25 (Nougat), Android puts rate limiting in place to block apps from DDOSing the notification shade. The commits to AOSP introducing this change can be seen here.
I was seeing these logs on my side because I was trying to update a progress notification inside a loop:
Observable.range(0, 100)
.subscribe(progress -> updateProgressNotification(progress));
Adding an delay of 200ms between each emission fixes the problem for me:
Observable.range(0, 100)
.zipWith(Observable.interval(200, TimeUnit.MILLISECONDS), (progress, delay) -> progress)
.subscribe(progress -> updateProgressNotification(progress));
You can read more about this problem on my blog.
I found a solution to this problem. I had to change the package name from com.gttn.sample to com.gttn.sample2. Not really sure why I had to do it but the notifications show properly and the warning disappeared.
When you install and reinstall the app many times something
unchecked the "Show Notifications" in Settings > ApplicationManager > "Your app".
Just check again "Show Notifications" and be happy!
I'm not sure why you're using getBaseContext(), but I suggest you use getApplicationContext() instead. Also, I'm not sure why you're binding to the Service.
just looked into this problem myself. without additional lines from your logfile (should be within 5 lines after) there might be a message similar to "E/NotificationService( 287): Suppressing notification from package by user request." This is a new setting in ICS (maybe JB) to disable notifications on a per-app basis. Go to Settings --> Apps --> and click the checkbox "show notifications" near the top. for obvious reasons, changing the package name would circumvent this setting very effectively.
I have written code which download the song and update the progress of the download as the progress in the notification. If u see in the line 6 of code ,If that condition is removed then notification is updated very fast as the while loop reads the bytes from the input stream now this causes repeated updates on the notification and this is recognised as the problem starting from noughat verions which keeps the rate limit on the notification update.Hope you got some idea about it.
int previousPercentage = 0;
int currentPercentage = 0;
while ((bytesRead = inputStream.read(bytes)) != -1) {
totalbytes = totalbytes + bytesRead;
currentPercentage = (totalbytes * 100) / length_of_file;
if (previousPercentage != currentPercentage) {// line : 6
updateProgressNotification(builder, notificationId, currentPercentage);
}
previousPercentage = currentPercentage;
fileOutputStream.write(bytes, 0, bytesRead);
}

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

Categories

Resources