2

My app has a background service which can run even if the app is destroyed.
The app has two activities: A, which is the main activity, and B, a settings activity.
The notification is created whenever the app goes to background.
If I am in A, go to home screen and launch the activity from the notification (with the intent to A), a new (duplicate) activity A will be created!
I would like to implement this logic:

if (launched from notification with app destroyed) {
    // this is the case where the service is running in background with the app destroyed
    create A;
} else {
    // don't create a new instance since A is already there. If current activity is B, stay in B (as if you pressed the launcher icon)
}

I currently use the following PendingIntent to launch the activity from notification:

PendingIntent launchActivity = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);

What is the correct way to implement the above logic, preferably programmatically. Thanks.

EDIT: FLAG_ACTIVITY_SINGLE_TOP does what I wanted, but there is still one problem: with this flag, the activity will not be created if and only if it's on top of the stack.
So, in the case the current stack is [MainActivity > Settings], since Settings is on top of the stack, the new stack will be [MainActivity > Settings > MainActivity], but what I wanted should be [MainActivity > Settings].
EDIT 2: basically, clicking on the notification should resume the app in the state it was.

aminography
  • 21,986
  • 13
  • 70
  • 74
Oliver
  • 926
  • 2
  • 12
  • 31
  • 1
    You may also be seeing [this nasty Android bug](https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508#16447508), which will cause multiple instances of your root `Activity` to be launched, even though they shouldn't be. To make sure you aren't seeing that, force stop your app and then launch it by tapping the icon on the HOME screen instead of launching it from the installer or from your IDE (Android Studio or whatever) – David Wasser Nov 26 '18 at 18:45
  • Hello thanks for replying. After searching a bit I indeed found the question you linked and now I am using this PendingIntent to do what I couldn't describe very well in the actual question: "resume the application on notification click": `Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED).setComponent(new ComponentName("com.example.app", "com.example.app.MainActivity"));` . My only question is: is this the cleanest way to accomplish what I wanted to do? – Oliver Nov 26 '18 at 22:33
  • Looking at some Android [source code](https://github.com/aosp-mirror/platform_frameworks_base/blob/4d73d87d83211f14f46f5d62d6241755b9e45a8c/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java#L35), `new Intent(this, FrameworkPerfActivity.class) .setAction(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))` was used instead. I tried in my app and this version works as well... which one would you advise to use? – Oliver Nov 26 '18 at 22:57
  • 1
    There are many ways to do this. They all work. You can also use `Intent intent = getPackageManager().getLaunchIntentForPackage("com.example.app");' This is a bit less code and is maybe clearer to the reader. – David Wasser Nov 27 '18 at 08:11

2 Answers2

3

First of all, try to set PendingIntent flag to PendingIntent.FLAG_UPDATE_CURRENT instead of 0:

PendingIntent launchActivity = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

Then set android:launchMode="singleTop" for the Activity A (i.e. MainActivity) to avoid recreating it when it's opened:

<activity
    android:name=".MainActivity"
    ...
    android:launchMode="singleTop">
    ...
</activity>

Finally in the MainActivity, do the Intent handling in onCreate (when the Activity was destroyed and now created by clicking on notification) and onNewIntent method (when the Activity is created and we want to handle notification click).

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    handleIntent(getIntent());
}

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

private void handleIntent(Intent intent) {
    if(intent.hasExtra("KEY_TO_EXTRA")){
        // ...
    }
}
aminography
  • 21,986
  • 13
  • 70
  • 74
  • Thanks, but is it possible to get what I said in the question (I edited with further details) plus what I specified in the [comment ](https://stackoverflow.com/questions/53459111/launch-activity-from-notification-instantiating-activity-even-if-already-there#comment93791116_53459183) using the appropriate combination of Intent.FLAG_xxxs? `PendingIntent.FLAG_UPDATE_CURRENT` doesn't change anything – Oliver Nov 25 '18 at 18:09
  • Do you want to have [MainActivity > Settings] stack and the MainActivity handles the intents? As you know the paused Activity could not handle the intents and still remain paused. I think there are only two choices on stack: [MainActivity > Settings > MainActivity] and [MainActivity] – aminography Nov 25 '18 at 19:12
  • "Do you want to have [MainActivity > Settings] stack and the MainActivity handles the intents?" Yes! That should do it. Basically, I'd like to mimic the behavior of the launcher. If I have [MainActivity > Settings], go to homescreen, and launch the app from the launcher, the stack will be [MainActivity > Settings] (what I wanted!): I tried with the launcher flags: `FLAG_ACTIVITY_NEW_TASK` and `FLAG_ACTIVITY_RESET_TASK_IF_NEEDED` but it still didn't work :( – Oliver Nov 25 '18 at 19:28
1

You can open RedirectActivity instead of your own activity :

public class RedirectAcivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this, HomeActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(intent);
        finish();
    }
}

And your pendingIntent will be like:

PendingIntent launchActivity = PendingIntent.getActivity(this, 0, new Intent(this, RedirectActivity.class), 0);
Marzi Heidari
  • 2,660
  • 4
  • 25
  • 57
  • Thanks, now I edited the PendingIntent and have `PendingIntent launchActivity = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);` but the only problem is, when the stack is [A, B] and I go to homescreen, the new stack will be [A] instead of [A, B]. I also tried with only `FLAG_ACTIVITY_CLEAR_TOP` or `FLAG_ACTIVITY_SINGLE_TOP` but the result is the same – Oliver Nov 24 '18 at 15:39