I have a service that will run a upload task in foreground then showing a progress in the notification. Since a user may upload multiple times with different id request then the
Use different id for different notification. In this code, you are using same default id so new notification is replacing old one.
At last I figure it out how to do it. First I needed to run it inside a onCreate with a little delay.
@Override
public void onCreate (){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
startForeground(requestCode, getMyActivityNotification("",completedParts,totalSize));
}
},500);
}
Then create a notification provider method.
//Notification provider
private Notification getMyActivityNotification(String caption, long completedUnits, long totalUnits){
int percentComplete = 0;
if (totalUnits > 0) {
percentComplete = (int) (100 * completedUnits / totalUnits);
}
//Return the latest progress of task
return new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
.setSmallIcon(R.drawable.ic_file_upload_white_24dp)
.setContentTitle(getString(R.string.app_name))
.setContentText(caption)
.setProgress(100, percentComplete, false)
.setContentInfo(String.valueOf(percentComplete +"%"))
.setOngoing(true)
.setAutoCancel(false)
.build();
}
Then updating notification progress should be separated from calling a foreground.
/**
* Show notification with a progress bar.
* Updating the progress happens here
*/
protected void showProgressNotification(String caption, long completedUnits, long totalUnits, int code) {
createDefaultChannel();
mCaption = caption;
requestCode = code;
completedParts = completedUnits;
totalSize = totalUnits;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
//Update the notification bar progress
mNotificationManager.notify(requestCode, getMyActivityNotification(caption,completedUnits,totalUnits));
}
}
You should pass different Id to get different notification.
In Your Code:
Use getId()
method to get different Id for each notification:
private static final AtomicInteger c = new AtomicInteger(0);
public static int getID() {
return c.incrementAndGet();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
.setSmallIcon(R.drawable.ic_file_upload_white_24dp)
.setContentTitle(getString(R.string.app_name))
.setContentText(caption)
.setProgress(100, percentComplete, false)
.setContentInfo(String.valueOf(percentComplete +"%"))
.setOngoing(true)
.setAutoCancel(false);
NotificationManager manager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(getId(), builder.build());
startForeground(getId(),builder.build()); // you need to pass different id here. Or i think this method is not there.
For android O you need notification channel:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
notificationChannel.enableVibration(true);
notificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setSound(notification, Notification.AUDIO_ATTRIBUTES_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
notificationBuilder.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setSound(notification)
.setContentIntent(pendingIntent)
.setContentText(message)
.setContentTitle(getResources().getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_visualogyx_notification)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message).setBigContentTitle(getResources().getString(R.string.app_name)))
.setTicker("Hearty365")
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher_round));
notificationManager.notify(getID(),notificationBuilder.build());
Before you call startForeground, you need to call ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH)
to keep the old notification(s).
The following is a utility class that you can use in the foreground service to manage the notifications:
class ForegroundNotifications(
private val context: Context,
private val foregroundServiceHelper: ForegroundServiceHelper,
private val notificationService: NotificationService
) {
var activeNotificationId: Int? = null
private set
val notificationIds: Set<Int>
get() = entries.keys
val isEmpty: Boolean
get() = entries.isEmpty()
private val entries: LinkedHashMap<Int, Notification> = LinkedHashMap()
fun notifyAndDetachPrevious(notificationId: Int, notification: Notification) {
synchronized(this) {
foregroundServiceHelper.startForegroundAndDetachPreviousNotification(
notificationId,
notification
)
entries[notificationId] = notification
activeNotificationId = notificationId
}
}
fun cancel(notificationId: Int) {
synchronized(this) {
if (notificationId == activeNotificationId) {
val newActiveNotificationId = entries.keys.findLast { it != activeNotificationId }
val notification = entries[newActiveNotificationId]
if (newActiveNotificationId != null && notification != null) {
notifyAndDetachPrevious(newActiveNotificationId, notification)
}
}
entries.remove(notificationId)
if (isEmpty) {
foregroundServiceHelper.stopForeground()
} else {
notificationService.cancel(context, id = notificationId)
}
}
}
}
interface NotificationService {
fun createDefaultChannel(context: Context)
fun notify(context: Context, tag: String? = null, id: Int, notification: Notification)
fun cancel(context: Context, tag: String? = null, id: Int)
}
interface ForegroundServiceHelper {
fun startForegroundAndDetachPreviousNotification(
notificationId: Int,
notification: Notification
)
fun stopForeground()
}
class ForegroundServiceHelperImpl(
private val service: Service
) : ForegroundServiceHelper {
override fun startForegroundAndDetachPreviousNotification(
notificationId: Int,
notification: Notification
) {
ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH)
service.startForeground(notificationId, notification)
}
override fun stopForeground() {
ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_REMOVE)
}
}