14

I want the broadcast receiver to launch my app this way:

  1. If the app is not in the foreground or background, launch it as if it is launched from launcher.

  2. If the app is in the background, bring it to the foreground with the state it was last in.

  3. If the app is in the foreground, do nothing.

Here is my code:

@Override
public void onReceive(Context context, Intent intent) {
        Intent launch_intent = new Intent("android.intent.action.MAIN");
        launch_intent.setComponent(new ComponentName("com.example.helloworld","com.example.helloworld.MainActivity"));
        launch_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        launch_intent.addCategory(Intent.CATEGORY_LAUNCHER);
        launch_intent.putExtra("some_data", "value");
        context.startActivity(launch_intent);
}

MainActivity is my root activity. Like I said, if the app is already running, I just want it brought to the front without changing its state. If there is some activity on top of MainActivity, leave it as it is.

Most of the time the above code worked fine, but sometimes I noticed that the app was restarted (or the state was reset --- the root was back on top) when it was brought from the background.

What code should I be using? Any help is appreciated!

Shawn
  • 2,675
  • 3
  • 25
  • 48
  • [`ActivityManager.getAppTasks()`](https://developer.android.com/reference/android/app/ActivityManager.html#getAppTasks()) and [`AppTask.moveToFront()`](https://developer.android.com/reference/android/app/ActivityManager.AppTask.html#moveToFront()) seem like good candidates, but these are only available in API 21 and later. – Karakuri Aug 19 '15 at 01:10
  • @Karakuri there are much easier ways to do this. In general, if you just call `startActivity` on a "launch Intent" (an `Intent` to launch the root `Activity` in a new task), this will either start the app if it isn't running or bring an existing task from the background to the front. – David Wasser Aug 19 '15 at 19:30

4 Answers4

12

You don't need to write all that code. You can use a "launch Intent", as follows:

PackageManager pm = context.getPackageManager();
Intent launchIntent = pm.getLaunchIntentForPackage("com.example.helloworld");
launchIntent.putExtra("some_data", "value");
context.startActivity(launchIntent);

However, your code should also work.

If you say this works most of the time, you might be seeing a nasty old Android bug in those cases. This occurs when you launch your app for the first time either directly from the installer, or via an IDE (Eclipse, Android Studio, etc.). You should always launch your app for the first time directly from the HOME screen or list of installed apps. For more information about this frustrating Android bug see https://stackoverflow.com/a/16447508/769265

Community
  • 1
  • 1
David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • Without setting any flags? How would I get the extra data? Shouldn't I use FLAG_ACTIVITY_SINGLE_TOP and implement onNewIntent for MainActivity? – Shawn Aug 19 '15 at 19:30
  • 1
    If your app is already running, this will just bring it to the front. It will NOT call `onNewIntent()`. If you need the "extras" in this case you will need to do something different. Look at my answer to [this question](http://stackoverflow.com/questions/6575730/notification-to-restore-a-task-rather-than-a-specific-activity) where I show how to use an "activity that does nothing" to accomplish this. – David Wasser Aug 19 '15 at 19:37
  • Your answer to [this question](http://stackoverflow.com/questions/6575730/notification-to-restore-a-task-rather-than-a-specific-activity) doesn't deal with extra data. It seems just doing the same thing like what you posted here. – Shawn Aug 19 '15 at 22:32
  • Do you mean to send extra data to NotificationActivity and perform some actions there, and then close it? How do I check if the app is running (foreground or background) or not? – Shawn Aug 19 '15 at 22:51
  • Please explain what you wanted do with extras. The problem is not clear. – David Wasser Aug 20 '15 at 07:08
  • I might open a new window or dialog based on the extra value. – Shawn Aug 20 '15 at 17:00
  • If you need extras, then you'll need to start a new `Activity` with those extras. This `Activity` would go on top of the stack. It could then look at the extras and decide what to do. If the app wasn't running at all, this `Activity` would be the only one in the task, so it could redirect to the root activity and pass the extras to that activity. But if the app was already open, then you don't know which activity is on the top of the stack, so you would need to decide what to do there. I don't know enough about your app to suggest something. – David Wasser Aug 20 '15 at 17:16
  • Thanks! That's already helpful. But how do I know if the app is already running or not from NotificationActivity? – Shawn Aug 20 '15 at 17:22
  • in `onCreate()` of `NotificationActivity()`, you can call `isTaskRoot()`. If this returns `true`, then the app was not running (since `NotificationActivity` is the only `Activity` in the task). Otherwise, the app was already running and `NotificationActivity` is on top of whatever other activities are in the task. – David Wasser Aug 20 '15 at 17:47
10

Since you need behaviour like

1) If the app is not in the foreground or background, launch it as if it is launched from the launcher.

2) If the app is in the background, bring it to the foreground with the state it was last in.

3) If the app is in the foreground, do nothing.

Consider:

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

    Intent startIntent = context
            .getPackageManager()
            .getLaunchIntentForPackage(context.getPackageName());

    startIntent.setFlags(
                    Intent.FLAG_ACTIVITY_REORDER_TO_FRONT |
                    Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    );
    context.startActivity(startIntent);
}
Andrew
  • 36,676
  • 11
  • 141
  • 113
3
@Override
public void onReceive(Context context, Intent intent) {
    Intent launch_intent = new  Intent(context, MainActivity.class);
    launch_intent.setComponent(new ComponentName("com.example.helloworld","com.example.helloworld.MainActivity");
    launch_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP |INTENT.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    launch_intent.putExtra("some_data", "value");
    launch_intent.addCategory(Intent.CATEGORY_LAUNCHER);
    context.startActivity(launch_intent);
}

It's this simple but unless you know the proper way when you try to get the extra data from your intent you will usually get a java null pointer exception.

Use the onNewIntent() method to receive the extra data. If you want to know how comment below.

Edit: Try the following. But what I don't understand is the first code which I gave you should work on its own. It has always worked for me. Have you made sure that you have copied the first code I gave you and pasted it without making any changes?

Edit2: If that doesn't work try setting the flag to clearTaskOnLaunch instead of INTENT.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

David Wasser
  • 93,459
  • 16
  • 209
  • 274
KISHORE_ZE
  • 1,466
  • 3
  • 16
  • 26
  • Thanks. It's almost there. With your code and onNewIntent, I can get the extra data only when there is no other activity on top of MainActivity. If I put the app to the background with some activity on top of MainActivity, clicking notification brings back the app (another activity remains on top of MainActivity), but onNewIntent is not called. The activity remaining on top of MainActivity is what I want. How can I still get extra data? – Shawn Aug 19 '15 at 19:20
  • See the edit above. This should work as long as in your Manifest you have set MainActivity intent-filter category as launcher. This is usually done by defaut as long as main activity is your root class. – KISHORE_ZE Aug 19 '15 at 19:34
  • I tried. MainActivity's onNewIntent isn't called when the app is brought to the front with other activities on top. – Shawn Aug 19 '15 at 22:26
  • Isn't MainActivity declared as your category launcher in your Manifest file? – KISHORE_ZE Aug 20 '15 at 05:16
  • Yes, but I don't want to clear activities on top of MainActivity. I want them be there if they were. – Shawn Aug 20 '15 at 16:58
  • No need to reset it but what I don't understand is that is main activity declared as category launcher. If u don't know just post your Manifest file. For me in a similar app I built before it worked without any problems with the first code I gave you long ago. – KISHORE_ZE Aug 20 '15 at 17:43
  • Yes it is delcared as category launcher. I think clearTaskOnLaunch will reset the app to the root activity, but that's not what I want. And if there is some other activity on top of the root activity, the root activity's onNewIntent won't be called right away, right? – Shawn Aug 20 '15 at 17:48
  • In your intent if you specified main activity only main activity should start not some other activity. That's why I don't understand why it is not working. – KISHORE_ZE Aug 20 '15 at 17:50
  • @KISHORE_ZE This is clear. If the task has another `Activity` on top of the `MainActivity`, this code will bring the task to the foreground, but **will not** call `onNewIntent()` on `MainActivity`, because `MainActivity` is not on the top of the task stack. Also, your code is not good. Creating the `Intent` using `new Intent(context, MainActivity.class)` already sets the component, so calling `setComponent()` is redundant. Also, since you've specified the component, the Intent resolution is **explicit** not implicit, so it isn't necessary to call `addCategory()` either. – David Wasser Aug 20 '15 at 17:55
  • Not only that, there are simpler ways to do this. – David Wasser Aug 20 '15 at 17:55
  • I told you for me it worked with what I told first these were just tries to make it work. – KISHORE_ZE Aug 20 '15 at 17:58
  • There could be one way it's pretty complex but it could work. Create a new activity which cannot be summoned in the app by the user. So I guess it starts this new Activity on top right? (This is just a guess). If so onNewIntent() can be called in that and start main activity via the app itself using another intent with the passed extras. I know it's kinda complex but I suppose it's worth a try. – KISHORE_ZE Aug 20 '15 at 17:59
  • Yeah, that's more like what David suggests. Please see his answer above. Thanks. :) – Shawn Aug 20 '15 at 18:21
  • Oh ok I just saw it sorry. – KISHORE_ZE Aug 20 '15 at 18:25
0

Assuming you already know BroadcastReceiver, you just have to do the launching like you usually do on onReceive : Therefore it will look like this

@Override
public void onReceive(Context context, Intent intent) {
    Intent launch_intent = new Intent("android.intent.action.MAIN");
    launch_intent.putExtra("some_data", "value");
    tiapp.startActivity(launch_intent);
}

If it is launched before, it will just resume that Activity, if not, it will start a new

Sheychan
  • 2,415
  • 14
  • 32