25

I have a task stack of A > B > C. I am currently on C, and then I press the home button. I get a notification with the intent to take me to Activity A. I press the notification, and I'm at A but if I press back, I go to C, then B, then A.

I am setting up my PendingIntent like so. Anything clearly wrong with it?

final Intent notificationIntent = new Intent(mContext, ActivityA.class);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);


        PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0);

EDIT 1:

I tried the suggestion here: Clear all activities in a task?

notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);

but I still get the same result. My activity A starts in my application task stack, I press back and go to C, then B, then A again.

I am starting to think that this is either not possible in Android or it's not possible when using a pending intent.

EDIT 2: This is not a matter of what flags are needed. More of an issue of what could be going wrong that the flags seem to have no effect.

Community
  • 1
  • 1
  • To be clear you want to return to A with B and C removed from the stack? – Dan S Jul 21 '14 at 19:50
  • I want to return to A with B and C removed, but I'm not sure if I just want A to resume or A to be removed as well, and a new instance of A to start. –  Jul 21 '14 at 19:57
  • @DanS either way. I NEED B and C to be cleared. I will inspect the behavior of A when I can get the clearing to happen. –  Jul 21 '14 at 20:05
  • In `ActivityA` do you have `onNewIntent()` implemented? – Dan S Jul 21 '14 at 20:22
  • @DanS Nope. I haven't heard of that method, but I'd like to focus on clearing B and C properly from the stack. –  Jul 21 '14 at 20:26
  • That method will be called to handle the formerly pending `Intent`. Try a simple implementation to see if that will fix your stack problem. – Dan S Jul 21 '14 at 20:43
  • @DanS Sorry. Can you elaborate by "try a simple implementation"? –  Jul 21 '14 at 20:50
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57720/discussion-between-dan-s-and-user2676468). – Dan S Jul 22 '14 at 00:56
  • @user2676468 Just to clarify, have you tried the `NEW_TASK | CLEAR_TOP` combination, with no other flags? – matiash Jul 24 '14 at 00:09
  • Yes. I got the same behavior as mentioned in my question. –  Jul 24 '14 at 13:55
  • final Intent notificationIntent = new Intent(mContext, ActivityA.class); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0); – Vaishali Sutariya Jul 28 '14 at 11:37
  • @Jim nope. Still super confused how this doesn't seem to be possible. –  Oct 09 '14 at 15:16
  • Strangely enough, uninstalling and reinstalling the app made the original code work (CLEAR_TOP | NEW_TASK), as suggested by one of the obscure answers to this question below. If you can confirm it, please accept it to save a lot of people a lot of trouble. – user2297550 Feb 15 '19 at 03:07

9 Answers9

23

if u r using solution 1 then Just remove the launchMode="singleTask" from manifest

1)

 final Intent notificationIntent = new Intent(mContext, ActivityA.class);

 notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);

 PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0);

OR

2)

       <activity 
       android:name=".ActivityA" 
       android:theme="@style/theme_noActionBar" 
       android:noHistory="true">

OR

3)

<activity android:name=".ActivityA" android:launchMode="singleTop">
Usman Kurd
  • 7,212
  • 7
  • 57
  • 86
Vaishali Sutariya
  • 5,093
  • 30
  • 32
12

I had loads of problems with this, and thought I was going crazy until I uninstalled/reinstalled my App and hey presto, the code works as per many posts on Stack Overflow. I was doing the following:

Intent i = new Intent(getApplicationContext(), MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);

and was continually getting two versions of the App. After an uninstall on one device, and re-starting the other device everything now behaves as expected with a single instance. Hope this helps you.

Simon Huckett
  • 482
  • 5
  • 13
7

The problem you are encountering has to do with Android creating a new task using your PendingIntent and the required Intent.FLAG_ACTIVITY_NEW_TASK. In other words, the flags you are wanting to use have to do with starting activities within the context of a given task stack. A PendingIntent usually does not have a context, and therefore what you are seeing is a task being created for your Activity A pending intent.

Here's the reference to the docs:

Android PendingIntent

The requirement for the first in a series of PendingIntents to have Intent.FLAG_ACTIVITY_NEW_TASK is the issue. Note that a series of pending intents can have the same context as the initial pending intent using the "getActivities" method.

You are correct that it is not possible to do what you want only using flags. Your code is correct, and to get the behavior that you want you will need too look beyond intent flags.

There are many other ways, such as using the manifest, running a service, using static fields, etc. to try to manage what you are interested in doing. Besides that, you could possibly use TaskStackBuilder to solve your problem if you decide to go the route of using the Manifest. There are also many other solutions not involving flags, but you specifically asked about using flags.

Jim
  • 10,172
  • 1
  • 27
  • 36
5

You can set the launchMode of ActivityA to singleTask in Manifest file. Since ActivityA is the root of your application it works.

From documentation:

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.

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTask" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

EDIT 1:

Since you don't want to modify Manifest file, only option I can see is using Intent.makeRestartActivityTask method to generate the Intent. However this will relaunch the ActivityA rather than resuming existing instance.

Intent intent = Intent.makeRestartActivityTask(new ComponentName(this, ActivityA.class));
PendingIntent rIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
Manish Mulimani
  • 17,535
  • 2
  • 41
  • 60
  • This could work, but in case I ever wanted to change my pending intent to Activity B, then setting single task would give me an issue. I should be able to set an Intent.FLAG to do this without modifying the manifest. –  Jul 24 '14 at 13:56
  • `makeRestartActivityTask` works like a charm! Thanks – Vlad Jan 15 '19 at 10:52
  • I don't know why but this is the only option that works for me. I have an activity launched by an external app expecting a result, and use NFC + pending intents in my own app which was totally breaking the result setting my app does when I just use NEW_TASK. – Daniel Wilson Sep 26 '19 at 16:33
3

A solution in terms of intent flag would be as following:

  1. In Manifest, add launchMode:singleTask for Activity A
  2. Use the following code block:

    final Intent notificationIntent = new Intent(mContext, ActivityA.class);
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    PendingIntent contentIntent = PendingIntent.getActivity(myContext, 0, notificationIntent, 0);
    

What happens here is that when Activity A is launched using notification, by the definition of Intent.FLAG_ACTIVITY_NEW_TASK, since there exists a task A>B>C containing A, this task is brought forward also destroying the activities B and C and A starts with the onNewIntent --> onRestart --> onStart --> onResume

If a task is already running for the activity you are now starting, that task is brought to the foreground with its last state restored and the activity receives the new intent in onNewIntent()

Now the reason, why it doesn't behave nicely with just the intent flags is that, the Activity A wasn't launched with singleTask mode and Android somehow doesn't maintain the state of it.

Laks
  • 244
  • 2
  • 7
1

Try using the Intent flag FLAG_ACTIVITY_CLEAR_TOP instead of clearing the task. From the Intent documentation:

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.

For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B.

Dan Amato
  • 624
  • 6
  • 5
1

I created a demo project as your description and it works just as what you want.

I don't know how do you get the notification, so in my project I use a BroadcastReceiver to get a Broadcast and then show the Notification in the onReceive() method of the Receiver. I think you can compare my demo with your project to find the problem. BTW, my test is performed under API 16.

Here is my demo: https://github.com/leoguo123/AndroidDemo

In the repository:

Test project contains 3 Activity(A > B > C) and it can show the Notification when receive a proper broadcast. SendBroadcast project is responsible for sending the broadcast.

The code for generating the notification:

    public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        showNotification(context);
    }

    private void showNotification(Context context) {
        Intent intent = new Intent(context, ActivityA.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                | Intent.FLAG_ACTIVITY_SINGLE_TOP
                | Intent.FLAG_ACTIVITY_NEW_TASK);
        NotificationManager nmr = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
        PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
        Notification.Builder builder = new Notification.Builder(context);
        builder.setContentTitle("Open A with B and C being closed");
        builder.setContentText("Open A with B and C being closed");
        builder.setSmallIcon(R.drawable.ic_launcher);
        builder.setContentIntent(pi);
        builder.setAutoCancel(true);
        Notification nt = builder.build();
        nmr.notify(0, nt);
    }
}
Lei Guo
  • 2,550
  • 1
  • 17
  • 22
1

My solution code:

    public class MyApplication extends Application{
    private static int activityCounter;
    public Activity A,B,C;
    public void incrementActivityCounter()
    {
        activityCounter++;
    }
    public void decrementActivityCounter()
    {
        activityCounter--;
        if (activityCounter == 0)
        {
            A.finish();
            B.finish();
            C.finish();
        }
    }
}


public class A extends Activity{
   @Override
    protected void onStart()
    {
        super.onStart();
        application.incrementActivityCounter();
    }

    @Override
    protected void onStop()
    {
        application.decrementActivityCounter();
        super.onStop();
    }
}
0

Tried all suggestions from above and failed and the came up with a solution from the guideline from https://stackoverflow.com/a/24999724/4271334 and https://stackoverflow.com/a/26592562/4271334. Added following 2 lines in the manifest

android:launchMode="singleTask"
android:clearTaskOnLaunch="true"

and set the PendingIntent at NotificationCompat.Builder using

mBuilder.setContentIntent(addBackStack(mContext,intent));

where

public static PendingIntent addBackStack(final Context context, final Intent intent) {
    TaskStackBuilder stackBuilder = TaskStackBuilder.create (context.getApplicationContext ());
    stackBuilder.addNextIntentWithParentStack (intent);
    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
    return stackBuilder.getPendingIntent (0,PendingIntent.FLAG_UPDATE_CURRENT);
}

Hope someone will be helped.