Notifications jumping one over the other on update - android

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());

Related

How can I show a different notification each time which open different activity?

I am trying to build an affirmation app which shows notification as an affirmation text and when user clicks on the notification it should take it to an activity, So far i am able to display notifications on hourly basis but i have no idea how to display different notifications each hour which opens a different activity each time because there is gonna be a random list of affirmations which will be displayed as notifications.
EDIT:
My MainActivity:
private void hourly()
{
Calendar calender=Calendar.getInstance();
Intent intent=new Intent(getApplicationContext(),notification_receiver.class);
PendingIntent pendingintent=PendingIntent.getBroadcast(this,100,intent,PendingIntent.FLAG_UPDATE_CURRENT);
// calender.setTimeInMillis(System.currentTimeMillis());
//// calender.set(Calendar.HOUR_OF_DAY, 10);
//// calender.set(Calendar.MINUTE, 30);
alarmmanager=(AlarmManager)getSystemService(ALARM_SERVICE);
int interval=60000;
alarmmanager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
interval,
interval, pendingintent);
Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();
}
My notification_receiver class:
public class notification_receiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
ArrayList<String> notificationTexts = null;
notificationTexts.add("This is a nice day"); // this will go to notificationTexts in position 0;
notificationTexts.add("This is nice morning");
Random rand = new Random();
int n = rand.nextInt(notificationTexts.size());
NotificationManager notificationManager= (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent repeating_activity=new Intent(context,repeating_activity.class);
repeating_activity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent=PendingIntent.getActivity(context,100,repeating_activity,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder=new NotificationCompat.Builder(context)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)
.setContentTitle("Affirmation")
.setContentText(notificationTexts.get(n))
.setAutoCancel(true);
notificationManager.notify(100,builder.build());
}
}
You already have a mechanism to show the notifications so what you are needing is a sort of backend where you can select a random text linked to a certain activity of your app.
In your main activity you can have 2 lists:
private ArrayList<Srtring> notificationTexts;
private ArrayList<Class> activitiesToStart;
Whith these two you add the text for your affirmations and the activity class you want to start under the same index of position in the list.
For example:
notificationTexts.add("This is a nice day"); // this will go to notificationTexts in position 0;
activitiesToStart.add(TheNiceDayActivity.class); //this will go to the activitiesToStart also in position 0;
In the mechanism you already have working, when you prepare the notification, you just have to calculate a random number between 0 and the length of either Lists, and pick the text from the first list and the activity from the second one. To pick the corresponding element you do it with list.get(position);
Once you got the activity, you create the PendingIntent that goes in the notification setup at the time of building it.
I haven't tried it but it should give you an idea on how it can be done.
EDIT I
A random number between 0 and 19 (including the 19) can be calculated with:
Random rand = new Random();
int n = rand.nextInt(20);

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

Handle multiple Notifications / Stacking Notifications from GCM

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 */;

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.

how to display notififcations one by one?

he, i am new to android platform.
Now i am developing a small application based on notifications.
In my application i am maintaining a timer based on that time every time a notification is displayed.
But the problem is first time notification is displayed and the second notification is appended above the first notification , third notification is append above the second notification..................
I want to display the notifications one by one.
if any one has idea how to display the notification one by one .please reply me.
here is my code
myTimer = new Timer();
myTimer.schedule(new TimerTask()
{
#Override
public void run()
{
TimerMethod();
}
}, 10000,10000);
}
private void TimerMethod()
{
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable()
{
public void run()
{
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
final Notification notifyDetails = new Notification(R.drawable.icon,"New Alert, Click Me!",System.currentTimeMillis());
Context context = getApplicationContext();
CharSequence contentTitle = "Notification Details...";
CharSequence contentText = "you have a new notification find clicking me";
Intent notifyIntent = new Intent(Notifi.this,Notifi.class);
PendingIntent intent =
PendingIntent.getActivity(Notifi.this, 0,
notifyIntent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
notifyDetails.setLatestEventInfo(context, contentTitle, contentText, intent);
mNotificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);
}
};
Thanks in advance
Store a fixed amount of time say 5000ms in a variable.
Now, start the 1st notification and get the current system time. To this add that 5000ms and continue processing. At the end of 5000ms close the notification. If the 2nd notification must be displayed within 5secs of the 1st one, then use a if condition statement and check whether 5000ms have elapsed since the start time of 1st notification. If it has elapsed then display the 2nd one, else wait till the 1st one completes.
You can also simply check if any notification is open using a simple flag that will be set to 1 when a notification starts and to 0 when it closes. If it is 0, a new notification can be displayed.
[You may also be able to use 2 threads to do this. On 1st thread run all processes. The 2nd one just interrupts the 1st one at set periods after being started. But haven't tried out this]
Hope I was able to help you.

Categories

Resources