Prevent notification PendingIntent starts Activity already started? - android

The service creates a persistent Notification and starts the main activity on click via PendingIntent. Here is the code.
Intent notificationIntent = new Intent(getApplicationContext(), ViewPagerActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(StreamingService.this, 0, notificationIntent, 0);
However, if the main activity is started when I press notification, it will be started again, and again and...
In the end, I have the same activity pilled up, one on the top of another. I can see this by pressing the back button, which will kill the Main activity once and then get me back to the same activity until I close the last one.
How can I prevent this to happen? Can PendingIntent detect that aiming activity is running so it does not create the same activity again, but rather start the running one?
PS. I apologize if not being able to explain this well. If this is the case, let me know and I will rephrase the problem.

I also found this solution. Add this attribute to Manifest
<activity android:name=".MyActivity"
android:label="#string/app_name"
android:launchMode="singleTop" // <-- THIS LINE
>
for each Activity you need this feature. So far it work with no errors at all.
Which solution is better? Mine is easier, if nothing.

Depending on the exact behaviour you want to implement, you could pass one of these flags as the last param of getActivity():
FLAG_ACTIVITY_REORDER_TO_FRONT
FLAG_ACTIVITY_SINGLE_TOP

Try using another value in REQUEST_CODE. don't use default value 0 in REQUEST_CODE.
or your pendingIntent will restart your activity.
if you want to use normal activity cycle, input request value when creating PendingIntent.
PendingIntent resultPendingIntent = PendingIntent.getActivity(
context, REQUEST_CODE,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);

Related

Android - PendingIntent does not call onCreate

Everything is working fine with my GcmListenerService if the activity is not already running.
I click on the notification and the app starts with the righ activity, actually EventHistoryActivity.
But, if EventHistoryActivity is already running, nothing happens.
I expect a call to onCreate but it does not happen.
I'm doing this in the sendNotification method:
resultIntent = new Intent(this, EventHistoryActivity.class);
resultIntent.putExtra("reload", true);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_CANCEL_CURRENT);
I tried also PendingIntent.FLAG_UPDATE_CURRENT and PendingIntent.FLAG_ONE_SHOT but with no luck.
Where is the problem ?
Again, if the app is running but EventHistoryActivity is not, all is ok.
Am I doing something wrong or is not onCreate the method I should expect to be called ?
You're probably getting the call to onNewIntent
This is called for activities that set launchMode to "singleTop" in
their package, or if a client used the FLAG_ACTIVITY_SINGLE_TOP flag
when calling startActivity(Intent). In either case, when the activity
is re-launched while at the top of the activity stack instead of a new
instance of the activity being started, onNewIntent() will be called
on the existing instance with the Intent that was used to re-launch

Android notification starting new instance of activity no matter what the launchMode and/or flags are set to

I've been doing a lot of research on this and I've hit a wall. I've read what seems like all of the material on this topic (inluding this, this, and this as well as the docs) and nothing is helping me in what I want to do.
Basically, in the case that the app is open on the user's phone, I just want a notification to redirect the user to the already-existing Activity in my app. I use a pattern like this:
private PendingIntent buildPendingIntentForNotification(String type) {
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);// have tried without this and with Intent.FLAG_ACTIVITY_CLEAR_TOP
resultIntent.putExtra(CommonUtils.NOTIFICATION_TYPE, type);
PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return resultPendingIntent;
}
I've also tried declaring all 4 types of launchModes in the Android Manifest within the activity tag. No matter what I do, clicking on the notification (even if the app is the active foreground app on the screen and MainActivity is the active activity) always seems to restart the activity from onCreate. From what I can see during debugging, onDestroy is not called at any time between getting the notification and clicking on it. However, after the notification is clicked, onCreate is called and THEN onDestroy is called even though my activity is not being destroyed, which is very strange. I'm hoping someone can help me make sense of this because all of the launchMode and Intent.setFlags suggestions are not working for me. Thanks a lot!
Just for info's sake, here's the code I used to fix my problem (with credit to David's solution):
private PendingIntent buildPendingIntentForNotification(String type) {
Intent resultIntent = new Intent(this, MainActivity.class);
//resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.setAction(Intent.ACTION_MAIN);
resultIntent.addCategory(Intent.CATEGORY_LAUNCHER);
resultIntent.putExtra(CommonUtils.NOTIFICATION_TYPE, type);
PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return resultPendingIntent;
}
There are 2 ways of doing this:
Notification to restore a task rather than a specific activity?
Resume application and stack from notification
However, you may also be seeing this nasty Android bug.
You cannot control and cannot be sure about the state of your activities on the Android, meaning (in your context) you can only start an activity that has been paused not one that has been destroyed. The OS decides which activities will keep paused and which will destroy and you have no control over that. When your activity leaves the foreground successive callbacks are being invoked at undefined times and to the OS's
What you can simply do is to save your activity's instance state in the onPause() method (which we know for sure that will be called as soon as the activity leaves the foreground) and the on onCreate() you can restore the activity with the data as it previously was.
Note: If you have for example Activity A and activity B and you start activity B from within activity A, then on activity B's onCreate() method you can getIntent().getExtras()

Best way to start same activity and destroy the previous one

I am making some webview activity which would be loaded from main activitiy. Is there a way to run, let say every 1h some method which would start webview activity #2 in the background, kill #1 webview activitiy and switch seamlessly as possible from #2 to #1 , maybe some white screen for a second or something, because there are some memory leaks in webview and I would need to destroy activitiy once in a while, but would like to transition everything as smooth as possible?
Thank you in advance!
Probably this code'll works for you. I use it to restart my App, when my users change the app's language.
Intent intent = new Intent(getActivity(), StartUpActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
getActivity(),
123456, //request code ??? some dummy numbers.
intent,
PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager manager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
manager.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
System.exit(0);
You can read about the PendignIntent class here: http://developer.android.com/reference/android/app/PendingIntent.html
I'm not completly sure, what the request code is. I'm using some dummy numbers, and it works.
If you absolutely have to do this, I would suggest wrapping your activity in a "parent" activity and every time you need to kill the "child" (i.e. current) activity, simply transition to parent (with the Intent.FLAG_ACTIVITY_CLEAR_TOP flag) and then have the parent spawn a new instance of your activity. This way you don't have to do anything funky and it's consistent with how android activities work.

Performing action from notification drawer generates another instance of the already running activty

My android application generates a notification. This notification is definid in the following way:
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_alarms)
.setContentTitle("title")
.setContentText("example text");
Intent resultIntent = new Intent(this, MyActivity.class);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,resultIntent,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
So when I open the notification drawer and click on the notification area, the actvity MyActivity starts. But, If another instance of the MyActivty is already running, I obtain that two MyActivty instances will running at the same time, while I want that always just one instance at time, runs in my application.
I have tried using
android:launchMode="singleTop"
android:clearTaskOnLaunch="true"
but the result is the same.
How can I achieve this task?
Ultimately, you probably need to think about using Fragments, instead of Activities for the kind of UI interaction you are wanting (and really the way the NavDrawer is designed to work - and they way the official examples implement it).
You can use different intent flags to bring an old Activity back to the front, but this is really not the intended behavior you ultimately need (which is Fragment related).
Ex Intent Flags
Intent intent = new Intent(this, ActivityExamlpe.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

How to start an app the same way Android Launcher does (resume)

There are a lot of questions/answers about how to start an application from within your application in Android. But those solutions do not produce the same flow as if an icon was tapped in Android launcher.
For example, I do this (this is used with notifications):
intent = context.getPackageManager().getLaunchIntentForPackage("com.test.startup");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
pendingIntent = PendingIntent.getActivity(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
Then when I tap on notification the app is started, however, it is started somewhat differently than when I tap the icon in the App drawer. Specifically: with this approach my main Activity is always created (i.e. onCreate() then onResume() is called). However, if application was already started and then put in background, then starting it from Launcher will only cause onResume() of currently shown activity to be called (not onCreate() on the main one). Is there a way to trigger the same resume flow programmatically from within my app?
To summarize the task: when user taps on notification I need my app to be either started (if it's not already), or brought to the foreground in its current state (if it's in background) and have some data passed to it. The app will then take care of handling/rendering that data.
Your app is behaving the way it supposed to. Even if you try the launch the app from App drawer it will call the same callback. You have to understand the lifecycle. As your activity is in the background onCreate will not get called. But for the handling the data from the notification intent you should utilize callback method OnNewIntent() in activity. You should override this method and extract the data the from the new intent and should update UI. After onNewIntent onresume will be called.
I hope this solves your problem.
Here is my onPause code which works the way you expected i.e when user clicks on the notification it doesnt call onCreate again:
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
intent = new Intent(getApplicationContext(), PlayerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pIntent = PendingIntent.getActivity(getBaseContext(), 0, intent,0);
NotificationCompat.Builder noti =
new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_media_play)
.setContentTitle("Nepali Music And more")
.setContentText("Playing");
noti.setContentIntent(pIntent);
noti.setAutoCancel(true);
noti.setOngoing(true);
Notification notification = noti.getNotification();
notificationManager.notify(1, notification);
Focus mainly on the intent flags
You want to use the intent flags Intent.FLAG_ACTIVITY_CLEAR_TOP to find your activity and clear the stack above it. You also need the Intent.FLAG_ACTIVITY_SINGLE_TOP flag to prevent your activity from being recreated (to resume).
The Intent.FLAG_ACTIVITY_SINGLE_TOP is necessary since by default, the launch mode is "standard" which lets you create multiple instances of your activity. If you were to set your launch mode to SingleTop, then this flag own't be necessary

Categories

Resources