App Shortcut only works once, just resumes the app after that

1.3k views Asked by At

I have a dynamic app shortcut which looks like this

ShortcutInfo composeShortcut = new ShortcutInfo.Builder(App.getInstance(),
                                                                           getString(R.string.compose_shortcut_id))
                    .setShortLabel(getString(R.string.compose_app_shortcut_short_label))
                    .setLongLabel(getString(R.string.compose_app_shortcut_long_label))
                    .setDisabledMessage(getString(R.string.compose_app_shortcut_disabled_message))
                    .setIcon(Icon.createWithResource(App.getInstance(), R.drawable.compose_icon))
                    .setIntent(new Intent(context, ComposeActivity.class))
                                              .setAction(Intent.ACTION_DEFAULT)
                                              .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP))
                    .build();
            shortcutManager.setDynamicShortcuts(Arrays.asList(composeShortcut));

First time when I click this shortcut, it opens ComposeActivity just fine but not after that, I have to kill the app after which it works again for one time.

Desired flow - Whenever I click the app shortcut, the ComposeActivity should open. If the app is already running then ComposeActivity should open on top of the current activity only if the current activity is not already a ComposeActivity.

Current flow - First time I press the app shortcut, Compose activity opens just fine, but not after that. App is only resumed after that. I have to kill the app and again click the shortcut for it to open the ComposeActivity.

More info - Compose Activity in AndroidManifest is defined like this:

...
<activity 
android:name=".shared.publish.compose.view.activity.ComposeActivity"
android:screenOrientation="portrait"
android:theme="@style/compose_theme"
android:windowSoftInputMode="stateVisible|adjustResize"/>

<activity
    android:name=".shared.activity.SplashScreenActivity"
    android:label="@string/app_name"
    android:screenOrientation="portrait"
    android:theme="@style/splash_screen_theme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>
...
2

There are 2 answers

0
Rishabh876 On BEST ANSWER

So I finally found the solution. Thanks to some useful information from David Wasser.

Firstly App shortcut works for Activity with Action MAIN and Category LAUNCHER.

So in manifest I added the following intent filter to my ComposeActivity

 <Activity android:name=".view.activity.ComposeActivity"
     android:relinquishTaskIdentity="true">
     <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
     </intent-filter> 
 </Activity>

If you look closely I have also set android:relinquishTaskIdentity="true". This makes sure that if I have already open the ComposeActivity from shortcut and then I can again use it and open ComposeActivity.

Next I updated the Intent used for Shortcut

new Intent(context, ComposeActivity.class))
.setAction(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)

By doing this, ComposeActivity will always open if it already not on top of the activity stack. Also it will open on top of any existing activity.

But there is one problem. If there is no activity open, then Compose will not have any activity below it. App will close if back button is pressed.

One thing you can do is

@Override
public void onBackPressed() {
    if(isTaskRoot()){
        if (getIntent().hasCategory(Intent.CATEGORY_LAUNCHER) 
              && Intent.ACTION_MAIN.equals(getIntent().getAction())) {
            //launch any activity
        }
    }
    finish();
}

With this I could have any activity to open when ComposeActivity is the only activity in the back stack. In my case I launched my HomeActivity.

2
David Wasser On

As I expected, the problem is due to your use of the ShortcutManager. Here is the important bit from the documentation:

Only main activities—activities that handle the MAIN action and the LAUNCHER category—can have shortcuts. If an app has multiple main activities, these activities have different sets of shortcuts.

What happens is that your shortcut Intent looks like a launcher Intent. When the user clicks on an app icon, this generates a launcher Intent (ACTION=MAIN and CATEGORY=LAUNCHER). Android then looks to see if there is already an active task that has this launcher Activity as the root Activity. If it finds one, all it does is bring that task to the foreground. If it doesn't find one, it creates a new task and launches the launcher Activity into the new task as the root Activity. This is exactly the behaviour that you are seeing, and is exactly what users expect. If you are using Gmail or some other app and then you take a call (pushing that app to the background), then when you return to the HOME screen and click that app icon again, you expect to to brought back to the app exactly the way you left it. This is standard and expected Android behaviour.

Please also read the section in the documentation titled "Shortcut Intents", which includes this gem:

Dynamic shortcuts can be published with any set of Intent flags. Typically, FLAG_ACTIVITY_CLEAR_TASK is specified, possibly along with other flags; otherwise, if the app is already running, the app is simply brought to the foreground, and the target activity may not appear.

You need to rethink your navigation so that it makes sense for the users (and it follows the standard Android patterns).