1

This looks very similar to my previous question because it's some sort of follow up. I was not very happy with the only solution given; also, the solution was for a problem slightly different from this one. So let me try to explain the problem again...

  1. A notification is created at boot (with a BroadcastReceiver).
  2. My app main activity is opened and the home button is pressed (the activity will be sent to the back stack).
  3. I pull down the status bar and press on the notification previously created at boot.
  4. That will start some activity, different from the main one.
  5. I press the back button and the main activity is displayed.

This is not very different from my previous question... The thing is, "main activity" was just an example. I could have opened the app main activity and then opened the about activity through a menu option and pressed the home button. The back stack would now be MainActivity » AboutActivity. Which means that when the back button is pressed while in "some activity" (started by pressing the notification), we would be brought to the top of the back stack, that is, the about activity.

What basically want is to prevent any other activity to be opened when I press the back button while in "some activity" (again, started by pressing the notification). I want to be brought exactly where I was, that could be the desktop or some other app's activity, but not my app's MainActivity nor AboutAcitivity cause that's not where I was, those were in the back stack, "sleeping" in the background.

I have come up with a solution, but I don't think it's very elegant and I was looking for something more, well, elegant... If you have any other suggestion, please, let me know.

Anyway, this is my proposed solution:

// I use this class for public static (or public static final) members and
// methods

public final class AppHelper {
    public static final String KEY_RESUME_FROM_NOTIFICATION = "resumeFromNotification";

    private static boolean sResumeFromNotification = false;

    public static boolean getResumeFromNotification() {
        return sResumeFromNotification;
    }

    public static void setResumeFromNotification(boolean resumeFromNotification) {
        sResumeFromNotification = resumeFromNotification;
    }
}

public class MainActivity extends ListActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        (...)
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(AppHelper.getResumeFromNotification()) {
            AppHelper.setResumeFromNotification(false);
            moveTaskToBack(true);
        }
    }

}

public class AboutActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        (...)
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(AppHelper.getResumeFromNotification()) {
            AppHelper.setResumeFromNotification(false);
            moveTaskToBack(true);
        }
    }

}

public class SomeActivity extends Activity {

    // This will be called when the notification is pressed and the activity is
    // not opened yet

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        (...)

        extractIntentExtras(intent);
    }

    // This will be called if the activity is already opened and the
    // notification is pressed

    @Override
    protected void onNewIntent(Intent intent) {
        extractIntentExtras(intent);
        super.onNewIntent(intent);
    }

    private void extractIntentExtras(Intent intent) {
        Bundle bundleExtras = intent.getExtras();

        if(bundleExtras != null) {
            // These intent extras are set on the Intent that starts this activity
            // when the notification is pressed

            AppHelper.setResumeFromNotification(bundleExtras.getBoolean(
                AppHelper.KEY_RESUME_FROM_NOTIFICATION));

            mRowId = bundleExtras.getLong(AgendaNotesAdapter.KEY_ROW_ID);
            populateNoteUpdateFields();
        }
    }

}

I don't know, but this solution doesn't look very elegant to me (but it works as I expect it) and I'm looking for alternatives or for strong opinions on my proposed solution as an acceptable and good solution. Thoughts?

Community
  • 1
  • 1
rfgamaral
  • 16,546
  • 57
  • 163
  • 275
  • Do you have any special flags that you are using for your activities? singleTop / singleInstance or anything like that? I haven't seen the activity you mentioned since notifications should always start your activities in a new task stack (they require the FLAG_ACTIVITY_NEW_TASK flag). – Justin Breitfeller Oct 03 '11 at 23:07
  • I have `singleTop` on `MainActivity` (in the manifest), nothing else. I'm always confused by the `FLAG_ACTIVITY_NEW_TASK` flag. The activities started by the notifications work no matter that flag is set or not. Should I use it or not? – rfgamaral Oct 03 '11 at 23:13
  • From what I understand, it should always be set for activities from notifications. In your situation, you definitely want it to be set since you don't want your notification activity to be part of your normal task's stack. – Justin Breitfeller Oct 03 '11 at 23:22
  • That's what I understand too but I see no different behavior either way... :/ – rfgamaral Oct 03 '11 at 23:25
  • Does this approach work for you? http://stackoverflow.com/questions/4628246/activity-started-from-notification-opened-on-top-of-the-activity-stack – Justin Breitfeller Oct 03 '11 at 23:43

3 Answers3

4

After doing some more reading perhaps this is the combination of flags you need:

Intent intent = new Intent(mContext, SomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(intent);

I think that should force your SomeActivity class to be launched in a completely new task.

Justin Breitfeller
  • 13,737
  • 4
  • 39
  • 47
  • Yes, this works exactly as I expect it. Well, actually, it needs an extra flag to work even better, which is `FLAG_ACTIVITY_SINGLE_TOP`. It's just so that when I open a notification and then open a different one, the activity is reloaded instead of a new one being created. I actually never tried the multiple task flag because of all the bold "warnings" in the [documentation](http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_MULTIPLE_TASK). Should I really use it? I'm not implementing a top-level launcher... – rfgamaral Oct 06 '11 at 00:16
  • FLAG_ACTIVITY_SINGLE_TOP is only for when you don't want SomeActivity to be opened if it is already on top. Is that what you desire? Also, I think you are specifically trying to create a task that is entirely disjoint from your Main and About activity task. Using FLAG_ACTIVITY_MULTIPLE_TASK is the only way to ensure that will happen. – Justin Breitfeller Oct 06 '11 at 14:10
  • Yes, I now realized that flag is the only way to ensure the behavior I was looking for. I guess I was just a little "scared" of the documentation warnings about that flag. – rfgamaral Oct 06 '11 at 14:40
  • Yea.. Well the one downside is that there will be no way for the user to resume their SomeActivity task if they navigate away from it, but I'd imagine you already considered that. In fact, you may want to consider the NO_HISTORY flag so you are sure that SomeActivity is finished once the user navigates away from it. – Justin Breitfeller Oct 06 '11 at 15:04
  • Yes, I did consider that. In this specific situation, there's no point in resuming `SomeActivity`. Just to be clear, you are recommending the `NO_HISTORY` flag along with `NEW_TASK` and `MULTIPLE_TASK` as suggested in your answer, right? – rfgamaral Oct 06 '11 at 16:09
  • Yes. NO_HISTORY will cause SomeActivity to be finished once the user navigates away from it. I would recommend using that along with NEW_TASK and MULTIPLE_TASK. You may also want to add EXCLUDE_FROM_RECENTS in that list so it doesn't show up as a recent activity. – Justin Breitfeller Oct 06 '11 at 17:21
  • The reason FLAG_ACTIVITY_NEW_TASK wasn't working alone is because he's using singleTop. – Christopher Perry Oct 07 '11 at 00:40
  • It could also have not been working because of this unclear part of NEW_TASK's documentation: "When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. See FLAG_ACTIVITY_MULTIPLE_TASK for a flag to disable this behavior. " He wasn't using singleTop on the activity he was starting as far as we have been told. – Justin Breitfeller Oct 07 '11 at 13:31
-1

When launching the Activity from the notification, you can control how the Activity you are about to open is put on the back stack, and what task it's associated with with Intent flags. You can try something like:

Intent intent = new Intent(mContext, SomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

If that doesn't work, try setting a few of the other flags until you get the desired behavior.

Christopher Perry
  • 38,891
  • 43
  • 145
  • 187
-1

Do you ever want your MainActivity to stay in history? If not then my simple, crude solution is to finish the MainActivity when it is paused. (Call this in your MainActivity)

@Override
public void onPause() {
    finish();
}

This will ensure that your MainActivity is removed from history when you navigate away from it, and will never appear when the back button is pressed. This could be used for AboutActivity as well.

Caleb Gomer
  • 121
  • 1
  • 1
  • 5