I've a music player service and I'm showing a notification to users when it goes foreground. But when user click notification to come back to App, it's updating icons again (icons images are setted at onCreate). I know it's updating icons because it's recreating activity.
Here is a piece of code:
public void startForeground(String songName){
Intent showTaskIntent = new Intent(AudioPlayer.this, PlayerActivity.class);
showTaskIntent.setAction(Intent.ACTION_MAIN);
showTaskIntent.addCategory(Intent.CATEGORY_LAUNCHER);
showTaskIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(
this,
0,
showTaskIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(getApplicationContext())
.setContentTitle(getString(R.string.app_name))
.setContentText(songName)
.setSmallIcon(R.drawable.logo)
.setWhen(System.currentTimeMillis())
.setContentIntent(contentIntent)
.build();
startForeground(NOTIFICATION_ID, notification);
I've tried many different flags, but it always update. Other related cases and solutions at stack don't helped me.
--
The main problem - explained:
When you open the music player, you have play button, for example.
You click play, it plays. Play image is changed for pause image.
Activity goes foreground. It still playing.
You click notification. Activity come back (onCreate, onStart, onResume). Then it changes pause image for play image.
If I click on App icon (System Icon not notification) it calls, onRestart.
That is what I need, I think.
I hope someone can help me.
Add android:launchMode="singleTask" to main activity in AndroidManifest.xml
Related
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.
After a long search, I was not able to find exactly what I need.
I simply want : When a notification is displayed to the user, if the app is simply in background I want to reopen MainActivity. But if the app/activity has been killed, I want to restart the app completely.
My actual code :
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(notification.getSubject())
.setContentText(notification.getMessage())
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
mBuilder.setContentIntent(
PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_UPDATE_CURRENT));
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = mBuilder.build();
notif.flags = Notification.DEFAULT_VIBRATE | Notification.FLAG_AUTO_CANCEL;
Case which is not working actually is to restart the app if app or activity has been killed.
EDIT : By killed I mean the app has been closed in the app manager. Or we lost the focus and the activity is destroyed. In those cases I would like to fully restart the app.
I think you don't entirely understand how android functions. Whenever your program is needed, the program is started by android (See the Application class for an onCreate that you can override).
Whenever an activity is necessary it is (re)created or brought to the foreground.
That means that an activity restart does not necessarily imply a restart of the application. In practice, if the program was still in memory, then an application restart won't happen.
If you want to investigate this further
create an Application.onCreate method so you see whether your app is restarted or not.
trigger the notification
go to the android settings, application tab, and there select 'show cached processes'. Clear your process from the list.
tap the notification.
This should trigger a restart of the application. If this is indeed the problem then you cannot 'solve' it. Android decides when it will kill your application and when it removes it from memory.
The best solution then is to redesign your activity that whatever the application restart would trigger is also performed in the activity itself. Or so, without more detail what functionality you would like to see performed on an 'activity/application-restart' it is difficult to advice further on this matter.
I think you need to change your setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) value to setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT).
The reason is due to the way that FLAG_ACTIVITY_REORDER_TO_FRONT
works. When you use this flag, Android looks for an instance of the
desired activity in your activity stack, starting from the front of
the stack and scanning until it gets to the root/back of the stack. As
soon as it finds an instance of the specified activity, it brings that
one to the front (ie: if there are multiple instances of the specified
activity it will bring to the front the most recent instance).
Original I found here.
See android developer doc here.
I hope its help you.
I am using the following code showing the notification, it works properly. I am look for the listener from which i can come to know whether the notification is expanded or not.
I want to open the app if the notification is expanded or else open the particular activity of the app.
Intent intent= new Intent(context, Activity1.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, getNextRandom(), intent, 0);
Builder newBuilder newBuilder = new Notification.Builder(context);
newBuilder.setContentTitle(title)
.setContentText(subtitle)
.setAutoCancel(true)
.setSmallIcon(R.drawable.icon)
.setTicker(notificationMessage)
.setContentIntent(pendingIntent);
Notificationnotification = new Notification.BigTextStyle(newBuilder)
.setBigContentTitle(notificationTitle)
.bigText(text)
.build();
Let me know if there is way to acheive this?
There is no way to know if the notification is open... What you can do is add buttons to the expanded notification that pressing on them will act differently than pressing on the notification itself.
There maybe no direct way, but maybe the following hack can help!!
You can have a custom notification layout and use RemoteView, Set a setOnClickPendingIntent to launch a service for the entire view so that you get notified when the notification is clicked.
When the user clicks the notification, service is started and show the new expanded custom layout from the service (you replace the old notification with the new one)
Maybe show your initial notification also from the same service using startforeground, so that your app is not killed and the view click can be received faster since service is already running and response time for changing the view is lower.
see this for remoteview and this for launching service from notification click.
I'm developing some kind of media controls for my android wear. The scenario is the following:
The WearableListenerService in the wear app is notified when the media data changes, i.e. artist/title. An ongoing notification is shown which has a full screen second page with some controls in it.
Because I don't want it to show on my smartphone, the notifications are build in the wearable app.
The second page consists of an activity baked in a display intent:
Intent pageIntent = new Intent(this, ControlActivity.class);
pageIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(
getApplicationContext(),
0,
pageIntent,
0);
Notification controlPage = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.extend(
new Notification.WearableExtender()
.setCustomSizePreset(Notification.WearableExtender.SIZE_FULL_SCREEN)
.setDisplayIntent(pendingIntent))
.build();
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.icon)
.setContentTitle(currentArtist)
.setContentText(currentTitle)
.setOngoing(true)
.extend(
new NotificationCompat.WearableExtender()
.addPage(controlPage)
.addAction(new NotificationCompat.Action(button, null, intent))
.setContentAction(0)
.setContentIcon(button)
.setBackground(BitmapFactory.decodeResource(getResources(), bg))
.setHintHideIcon(true)
)
.build();
NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification);
Now the notification is refreshed from time to time to display new data on the first notification page which forces the activity in the second page to be recreated every time the notification is updated. This results in some ugly behavior where the user can see that the activity inside the second page is recreated.
Is there any way to prevent the activity in the second page from being completely recreated (onDestroy, onCreate) and/or only update the data on the first page of the notification as it should be?
Is it the PendingIntent creation that doesn't really work here?
The Manifest entry looks as the following:
<activity
android:name=".media.ControlActivity"
android:allowEmbedded="true"
android:exported="true"
android:launchMode="singleTop"
>
I've tried all combinations of flags as well as only updating the necessary parts but nothing changes.
Help is greatly appreciated. Let me know if you need further information.
Edit: The main problem seems to be that the embedded activity (for a custom layout) in the second page of the notification is always recreated as soon as I call notify. Reusing, only updating changed values on the first page or updating nothing has no effect.
Simply broadcast the new information to your second activity and update the fields when you receive the broadcast. And make it local so the broadcast concerns only your application.
In the second activity add the BroadcastReceiver as a member attribute :
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
/*do what you're supposed to do, update your data
you get the information from the intent by calling intent.getXExtra() as usual*/
}
};
And register it in your onCreate() method :
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter("your-filter-name"));
In your notification activity, when you update the information, add this :
Intent intent = new Intent("your-filter-name");
intent.putExtra(...);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Your second activity won't be recreated each time and the modifications should go smoothly :)
there is no method to prevent recreate the embedded activity in the notification, you can try this way in your sence.
use a action in second page, and start activity when click the action, when the notification is updated by time, the activity will not be recreated
I am using code similar to Creating a simple notification to create and show a notification from a network call.
The issue is that I want the activity that responds to the notification to do it's business and then on a backbutton click, put the previously active activity back in the foreground, with it's back stack intact. This is regardless of if the previously active activity was part of my app or somebody elses.
Currently it is following the generated TaskStackBuilder. Leading it back up the app hierarchy and out to the home screen. This is bad UI-design as it breaks the work-flow of anyone using the device, forcing them to manually go back to their app and spending more buttonclicks than necessary. It is also rather unintuitive.
According to the official desing guidelines this is how it should work. The implementation I linked to higher up makes back button have the same functionality as up button should have
It is also a common way to implement it in a plethora of other apps, including official google ones (google-play update notifications come to mind), so there must be a relatively standard way to do this.
Intent intent = new Intent(getApplicationContext(), MainActivity.class)
//add Intent.FLAG_ACTIVITY_CLEAR_TASK flag this will clear all activitys and
//launched activity at top. Means no other activity of this application will be running
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
or
// add Intent.FLAG_ACTIVITY_MULTIPLE_TASK which start one more task your applications
// where activity will not be cleared;
.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
Notification n = new Builder(context.getApplicationContext())
.setContentTitle("simple notification title")
.setContentText("simple message")
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.addAction(R.drawable.ic_launcher, "And more",pendingIntent ).build();
NotificationManager notificationManager =(NotificationManager) context.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, n);