I have 5 activities in my app. Every activity starts the same foreground service.
In onStartCommand method of the service foreground notification is created which unfortunately means that every call of startForegroundService() in any activity plays notification sound (even though the service is already running). How can I create the foreground notification only once or at least how not to play notification sound on successive startForegroundService() calls?
The other related question is: how can I go back to my application when I click the foreground notification? I have 5 activites and I would like to reopen the activity that was the last one the user was interacting with.
#1. before starting the service just check if its already running or not. In that case this will help you https://stackoverflow.com/a/5921190/6413387
#2. To reopen your last opened activity, you need to update the pending intent of your notification. Hope you will find your answer here https://stackoverflow.com/a/20142620/6413387
How can I create the foreground notification only once or at least how not to play notification sound on successive startForegroundService() calls?
You can check if the notification is already visible and show it only if it's not visible. You need to have a reference to the notification PendingIntent and notificationId.
fun isNotificationVisible(context: Context, notificationIntent: Intent, notificationId: Int): Boolean {
return PendingIntent.getActivity(context, notificationId, notificationIntent, PendingIntent.FLAG_NO_CREATE) != null
}
how can I go back to my application when I click the foreground notification?
You need a PendingIntent to open the app from a notification. To open the last activity shown you can remember this using Preferences in the onResume() method of each activity and route the notification into a routing activity that starts the right activity according to the value saved into the preferences.
val intent = Intent(context, RouteActivity::class.java)
val notificationBuilder = NotificationCompat.Builder(context, channelId)
.setContentIntent(intent)
val notificationManager = NotificationManagerCompat.from(context)
val notification = notificationBuilder.build()
notificationManager.notify(notificationId, notification)
Another way to do this is to update the notification PendingIntent if it's already visible with the last activity shown. In this case you don't have to store any value on Preferences and you don't need a route activity.
Related
I have an app that has a background service running that listens for events.
One of the events should unlock the phone and bring the app to the foreground.
What approaches here are possible?
I was thinking, would it be possible to send a local notification that is actually high priority so it opens the app automatically?
Currently I try to open apps activity this way:
private fun getIntent(pin: String): Intent = Intent(context, XActivity::class.java).apply {
putExtra(XActivity.EXTRA_SMTH, x)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
private fun showActivity(x: String) {
val intent = getIntent(x)
context.startActivity(intent)
}
This code piece works alright if the app is in the foreground, but does not if the app is in the background.
Any ideas/solutions are welcomed.
At first, if you listened for ACTION_SCREEN_ON or ACTION_SCREEN_ON, make sure to explicitly set your listeners ref.
Secondly, due to background restriction, you cannot start an Activity from background. You have to start a foreground service which you will start when the receiver receives the event. From that service, you can launch your activity with your desired intent.
Foreground service needs a notification. Inside your service, create a notification with your intent like following and call startForeground() with this notification. Also create and register NotificationChannel before if not already.
val fullScreenIntent = Intent(this, XActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationBuilder =
NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Launch Activity")
.setContentText("Tap to launch Activity")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM) // Set your desired category
// Use a full-screen intent only for the highest-priority alerts where you
// have an associated activity that you would like to launch after the user
// interacts with the notification. Also, if your app targets Android 10
// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
// order for the platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true)
val alarmNotification = notificationBuilder.build()
I have an app with several activities. I am implenting Firecbase FCM and it all works pretty well except in handling which activity to go to when the user clicks on the notification icon when it is received.
As I'm not sure how/when the FirebaseMessagingService is instantiated I cannot set it up with access to any of my objects. The only thingI have been able to do is to store the current Activity in the App as
public static Activity CurrentActivity { get; set; }
I then set the above in the OnCreate() of each activity, then in FirebaseMessagingService .OnMessageReceived() I setup my pending intent as below:
Intent intent = new Intent(this, App.CurrentActivity.GetType());
intent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, App.NOTIFICATION_ID, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new NotificationCompat.Builder(this, App.CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.ic_notification)
.SetContentTitle("FCM Message")
.SetContentText("test")
.SetAutoCancel(true) //dismisses the notification on click
.SetContentIntent(pendingIntent);
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(App.NOTIFICATION_ID, notificationBuilder.Build());
This works in so far so when the user clicks on the Notification it goes to the correct activity.
The problem is if a notification is recevied when the user is in Activity A but doesn't click on it and moves to Activity B and then clicks on the notification. My Pending Intent will be Activity A. However after the Notification is shown, Activity A is then displayed.
I was thinking if it is possible in OnMessageReceived() to detect is the app is in foreground or background then if in foreground I could just do and intent to display an Alert Dialog of the message and if in background then my original code would work fine.
Does anyone have any idea if this is possible and how?
I have managed to sort this out so thought I would post an update for anyone else who has a similar problem.
In my FirebaseMessagingService.OnMessageReceived() I created a PendingIntent to a standard activity called NotificationActivity.
public override void OnMessageReceived(RemoteMessage message)
From message I extracted the body and title and passed to NotificationActivity
NotificationActivity.OnCreate() will then just display an AlertDialog and on the OK click I closed the activity using this.Finish()
This works perfectly when the user receives a notification when in the app. The user can click on the notification and it will show the AlertDialog. On close it just resumes on whatever was the current activity.
I have an Alarm App that have foreground service with a Heads-Up Notification and that notification have two actions where one send an intent to the Service and can open an activity depending on the app configuration.
The problem is that when i click on a action that sends the intent to the service the notification doesn't hide. This not seems to occur when the intent opens a Activity
I don't want a foreground service without a Notification, i just want it to hide it back to the Notification Drawer when the intent is sent to the service
Here is the code:
NotificationCompat.Builder(mAlarmApplication, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_alarm)
.setAutoCancel(false)
.setOngoing(true)
.setVibrate(LongArray(0))
.setContentTitle("Title")
.setContentText("Content")
.addAction(0, dismissActionText, dismissPendingIntent)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(alarmScreenPendingIntent)
.setFullScreenIntent(alarmScreenPendingIntent, true)
Here is the link of the app https://play.google.com/store/apps/details?id=com.garageapp.alarmchallenges.
The problem occurs when alarm start and my current solution is to update the old heads up notification with a new one that is not a heads up but the UX is not a good because on Android 8+ the notification new notification pops up aging
Seems like your Notification is bonded with your Service. If so, then you have to kill the notification in Service
Did you try?
public static void cancelNotification(Context ctx, int notifyId) {
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager nMgr = (NotificationManager) ctx.getSystemService(ns);
nMgr.cancel(notifyId);
}
You are using .setOngoing(true) which should not be removed while service is working.
.setAutoCancel(true) will also not working with .setOngoing(true).
You have to use .setOngoing(false) to dismiss the notification.
If you or user remove your foreground notification your service will go to background, I think that best work is to not using heads up notification for foreground by not setting its priority to MAX
Use two notifications at same time one in drawer and another heads up:
-The first notification with priority DEFAULT for starting foreground ( auto cancel set to false and ongoing set to true) show this one with startForground()
-The Second notification (Heads up (Priority MAX) auto cancel set to true and on going set to false) for your actions show this with notifyManager.notify()
These two notifications must have different IDs
another solution:
If you want to use one heads up notification with actions for foreground service you may do this:
use a heads up notification with your action buttons for foreground service when the user clicks actions this action must call the foreground service and then the foreground service could call startForeground (with same id) with a new notification with priority set to default, if your notification could not be updated you may need to call stopForeground(true) or notificationManager.cancel(id) first before calling startForeground with new notification. both of these two notifications should has on going set to true and auto cancel set to false
In my opinion the first solution is better than the second because the notification may not update in second solution.
As the documentation says :
A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of ...
android system does not allow you to have a foreground service without notification or a hidden notification. and that's because of user awareness of what is happening in his/her system.
also killing the notification will stop your foreground service.
so you never can have both of the options (foreground service and hidden notification)
a not clear solution for your problem:
when you call action that sends the intent to the service, do this with a mediator activity i mean first open an activity and in the activity send intent to the service.
I hope this solve your problem as you told :
The problem is that when i click on a action that sends the intent to the service the notification doesn't hide. This not seems to occur when the intent opens a Activity
I want to show a notification while a timer is running and when the user clicks the notifiaction it opens the timer activity.
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(this, Activity.class).addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
The problem right now is that when I start the timer and press home and then click the notification it opens the Activity with the running timer.
But when I start the timer, then open another activity (via ActionBar.NAVIGATION_MODE_LIST), then press home and click the notification it opens a new Activity (empty).
I thought addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); would help (thats what I used to navigate between Activities)
im using android:launchMode="singleTop" and android:configChanges="orientation|screenSize"
for every Activity.
I wouldn't rely on your activity persisting and it is unnecessary
Store the timer start time into sharedpreferences and then simply load up the start time when the activity is recreated.
If something is meant to happen after a certain time, like a countdown time, then you'll need to set up an Alarm and handle it that way.
Remember that activities are just UI. Don't trust an activity to be doing anything when the user isn't looking at it.
In my app, I place my Service in the foreground to prevent it from being killed by using:
startForeground(NOTIFY_ID, notification);
This also displays the notification to the user (which is great). The problem is that later I need to update the notification. So I use the code:
notification.setLatestEventInfo(getApplicationContext(), someString, someOtherString, contentIntent);
mNotificationManager.notify(NOTIFY_ID, notification);
The question then is: will doing this knock the Service out of it's special foreground status?
In this answer, CommonsWare indicates that this behavior is possible, but he's not sure. So does anyone know the actual answer?
Note: I am aware that a simple way to get out of this question is to repeatedly call startForeground() every time I want to update the notification. I'm looking to know whether this alternative will also work.
To clarify what has been said here:
From what I understand, if you cancel the notification the service
will cease being a foreground service, so keep that in mind; if you
cancel the notification, you'll need to call startForeground() again
to restore the service's foreground status.
This part of the answer suggest it is possible to remove an ongoing Notification set by a Service by using NotificationManager.cancel() on the persistent Notification.
This is not true.
It's impossible to remove a ongoing notification set by startForeground() by using NotificationManager.cancel().
The only way to remove it, is to call stopForeground(true), so the ongoing Notification is removed, which ofcourse also makes the Service stop being in the foreground. So it's actually the other way around; the Service doesn't stop being in the foreground because the Notification is cancelled, the Notification can only be cancelled by stopping the Service being in the foreground.
Naturally one could call startForeground() after this right away, to restore the state with a new Notification. One reason you would want to do this if a ticker text has to be shown again, because it will only run the first time the Notification is displayed.
This behaviour is not documented, and I wasted 4 hours trying to figure out why I couldn't remove the Notification.
More on the issue here: NotificationManager.cancel() doesn't work for me
The RandomMusicPlayer (archived) app at the Android developer site uses NotificationManager to update the notification of a foreground service, so chances are pretty good that it retains the foreground status.
(See setUpAsForeground() and updateNotification() in the MusicService.java class.)
From what I understand, if you cancel the notification the service will cease being a foreground service, so keep that in mind; if you cancel the notification, you'll need to call startForeground() again to restore the service's foreground status.
When you want to update a Notification set by startForeground(), simply build a new notication and then use NotificationManager to notify it.
The key point is to use the same notification id.
Updating the Notification will NOT remove the Service from the foreground status (this can be done only by calling stopForground );
Example:
private static final int notif_id=1;
#Override
public void onCreate (){
this.startForeground();
}
private void startForeground() {
startForeground(notif_id, getMyActivityNotification(""));
}
private Notification getMyActivityNotification(String text){
// The PendingIntent to launch our activity if the user selects
// this notification
CharSequence title = getText(R.string.title_activity);
PendingIntent contentIntent = PendingIntent.getActivity(this,
0, new Intent(this, MyActivity.class), 0);
return new Notification.Builder(this)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(R.drawable.ic_launcher_b3)
.setContentIntent(contentIntent).getNotification();
}
/**
this is the method that can be called to update the Notification
*/
private void updateNotification() {
String text = "Some text that will update the notification";
Notification notification = getMyActivityNotification(text);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(notif_id, notification);
}