0

A. Whats the most elegant way to have an android M notification bring an existing target activity from the back stack to the top (i.e. not create a new target activity instance on top), while keeping the remaining back stack intact, under the following two scenarios:

  1. Task with target activity is in the foreground
  2. Task with target activity is in the background

B. If A is not possible, whats the most elegant way to have the notification

  1. Just continue showing the task with its stack intact if its already in the foreground (i.e. not create a new target activity on top)
  2. Bring the background task to the foreground intact without creating a new target activity instance on top.

This is like (re)launching the app from its icon on home screen or app drawer.

C. If B is also not possible, whats the most elegant way to have the notification perform B, and then create a new target activity instance on top EXCEPT when the target activity is already on top.

I don't think its pertinent, but the notification is set from a foreground service.

This question has been asked in various forms earlier and has received many answers. However APIs have changed, what worked earlier no longer does, most answers are many years old, answers are quoted from docs which sometimes don't follow practice, terminology used in those QnA are wrong and add to overall confusion, and on top of that some accepted answers are just plain wrong (e.g. this thread). Further, the behavior is quite complex given the many different scenarios that can occur and after having read pretty much all QnA on this topic, I didn't find sufficient engineering preciseness in those.

Also It's OK to Ask and Answer Your Own Questions is encouraging!

I'm going to start documenting my findings as answers to this question.

Answers, comments, corrections most welcome, only if its a proven mechanism for Android M, API 23 only. Please mention whether its for A, B or C. Dont want to go off on any more wild goose chases ;-)

Community
  • 1
  • 1
zehawk
  • 210
  • 1
  • 10

3 Answers3

1

Next I tried the chosen answer here on SO which David Wasser has posted on a couple of these questions. This mechanism is rather nifty. When the user clicks on the notification a dummy (blank) activity is created and one can then choose to launch the target activity.

There are two scenarios:

  1. Task in background: notification when clicked launches the blank activity which in turn launches the target activity, so this is quite fine
  2. Task in foreground: this is the problem case. Since the dummy activity and by extension the target activity get created on top of the existing stack, it defeats the purpose. To solve that, David has mentioned

In the onCreate() method of the NotificationActivity, check if your application is running, and if it isn't running call startActivity() and launch your application.

In the same question, Raginmari has provided a superb solution to perform this check: making a call to isTaskRoot() to check if the dummy activity is the root activity of the task. If the dummy activity IS the root, then its Scenario 1, else its Scenario 2.

So, this is great. At this point I have a perfectly workable solution satisfying B in my question:

B. If A is not possible, whats the most elegant way to have the notification

  1. Just continue showing the task with its stack intact if its already in the foreground (i.e. not create a new target activity on top)

  2. Bring the background task to the foreground intact without creating a new target activity instance on top.

Community
  • 1
  • 1
zehawk
  • 210
  • 1
  • 10
0

One would expect from the documentation that singleTask is exactly what I need:

The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.

But this doesnt work to satisfy A, B or C. Findings:

  1. If task is in foreground & has X (target activity instance) on top -> clicking on notification creates another X (multiple clicks on notification creates multiple instances of X stacked on top, each of which have a call to onCreate, and can be destroyed via back
  2. If task is in foreground & has X (target activity instance), with Y on top -> same behavior as in #1, back destroys new instance X to reveal Y, and finally the target activity instance X
  3. If task is in background -> same behavior as #1 and #2

Next I tested using singleTop. It had the same behavior as #1 both when task is in foreground or background.


Next I tried the chosen answer here on SO, without any launchmode flags.

final Intent notificationIntent = new Intent(context, YourActivity.class); notificationIntent.setAction(Intent.ACTION_MAIN); notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER); notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

No hope, new instances are created on top, so problem is yet unsolved.

Community
  • 1
  • 1
zehawk
  • 210
  • 1
  • 10
0

Next I tried the chose answer here on SO by CommonsWare which is really straightforward. So straightforward that I thought it would surely not work :)

Add notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)

But work it did, and turns out this was exactly the solution I was looking for, as it satisfies A in my question:

A. Whats the most elegant way to have an android M notification bring an existing target activity from the back stack to the top (i.e. not create a new target activity instance on top), while keeping the remaining back stack intact, under the following two scenarios:

  1. Task with target activity is in the foreground
  2. Task with target activity is in the background

Findings:

  1. If task is in foreground & has X (target activity instance) on top -> clicking on notification shows the same X (i.e. call to onResume is made for X)
  2. If task is in foreground & has X (target activity instance), with Y on top -> same behavior as in #1 (additionally call to onDestroy is made for Y)
  3. If task is in background -> same behavior as #1 and #2

This also makes perfect sense from the docs.

FLAG_ACTIVITY_CLEAR_TOP: If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.

The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent.

and

If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent().

Community
  • 1
  • 1
zehawk
  • 210
  • 1
  • 10