Android: Recents starts incorrect activity - android

I am developing an android app which has 2 methodes of starting.
One is the normal way by pressing the app icon on the phone.
The other method is with a deep link from a website.
The deeplink also sends some data which the app needs to do some "stuff". However this should only be done once.
When the deeplink activity is finished it start the main activity. However when I press back (on the device) and open the app from recents it opens the deeplink activity again.
I could exclude the deeplink activity from the recents in the manifest. This also excludes the mainactivity from the recent apps, this should not be the case.
How do I prevent the deeplink activity from beeing started from the recent apps?
My Manifest:
<activity
android:name="MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:theme="#style/AppTheme">
</activity>
<activity
android:name="DeeplinkActivity"
android:label="#string/app_name"
android:noHistory="true"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:theme="#style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="app_name" android:host="test" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="test" android:path="/" android:scheme="app_name" />
</intent-filter>
</activity>
To switch to the MainActivity I do the following:
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
EDIT:
This post was marked as duplicate with: Android: Starting app from 'recent applications' starts it with the last set of extras used in an intent
However that post is regarding the same activity. I want to change the root activity so when I start the app from recents it does not start the DeeplinkActivity. Yes as a workaround I could check the Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY flag. But then every time the user starts the app from recents the DeeplinkActivity is opened whilst it is not needed anymore.
Setting and or clearing aditional intent values does not seem to work.
I use information from the getIntent().getData()
If you still feel as if this is a duplicate please explain.

Your problem is that both DeepLinkActivity and MainActivity are in the same task, so when the user selects the app from the list of recent tasks, Android will either bring an existing task to the foreground, or if there is no existing task (with live/active activities in it), start the Activity that was the root Activity in the most recent task. You can't predict which Activity will be the root Activity, since the task could be started with either DeepLinkActivity or MainActivity, depending on which one the user chose first.
You really need to have 2 separate tasks to do this. One task will have your DeepLinkActivity in it, and this task should be excluded from the "recent task list". The other task will have your MainActivity in it.
I assume that MainActivity has the <intent-filter> with ACTION=MAIN and CATEGORY=LAUNCHER, even though your posted manifest does not show this.
Your manifest should look like this:
<activity
android:name="MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="DeeplinkActivity"
android:label="#string/app_name"
android:noHistory="true"
android:screenOrientation="portrait"
android:taskAffinity=""
android:excludeFromRecents="true"
android:theme="#style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="app_name" android:host="test" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="test" android:path="/" android:scheme="app_name" />
</intent-filter>
</activity>
You definitely do not need launchMode="singleTask" for MainActivity and you probably don't need it for DeepLinkActivity either (that depends on what else you do with this).
Specifying taskAffinity="" ensures that DeepLinkActivity is not launched into the same task as MainActivity, and allows you to launch MainActivity from DeepLinkActivity in a separate task. NOTE: Without specifying taskAffinity, both activities will end up in the same task, even though you specify launchMode="singleTask" for both of them.
Specifying excludeFromRecents="true" on DeepLinkActivity tells Android that the task with DeepLinkActivity as its root activity should not appear in the list of recent tasks.
When you launch MainActivity from DeepLinkActivity, you should do this:
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
and make sure that you call finish() on DeepLinkActivity.
If necessary you can also add noHistory="true" to DeepLinkActivity, but I don't think it is necessary. If the user is in DeepLinkActivity and gets an incoming phone call, after the phone call is ended, DeepLinkActivity should be displayed. If you specify noHistory="true", DeepLinkActivity would be finished immediately when the user accepts the incoming phone call.
Let me know if this is clear and works for you.

Related

Activity which launched from sharing intent filter doesn't obey android:taskAffinity rule during onNewIntent

Currently, my app does accept share content from external app.
In order to avoid sharing action from interference with current running main app, I mention android:taskAffinity explicitly.
I also try best effort to ensure there is only one and only one com.xxx.MyActivity in task com.xxx.share by using
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
AndroidManifest.xml
<activity android:name="com.xxx.MyActivity"
android:taskAffinity="com.xxx.share"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:theme="#style/Theme.Transparent"
android:windowSoftInputMode="stateAlwaysHidden" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
When MyActivity is being created for the first time (onCreate being called), I am able to verify it is inside task named "com.xxx.share".
However, if the MyActivity is being "reused" (onNewIntent being called), it is NO longer inside task name "com.xxx.share".
I am using the following way, to get the current task name - Is it possible to get the name taskAffinity which the Activity belongs to during runtime?
I was wondering, why it is so? How can I force the Activity (invoked via sharing intent filter) always
Launch in specified task name
Be the one and only one Activity in the task
p/s
If the Activity is launching within my home widget, or drop down notification, I can easily achieve such by using
<activity android:name="com.xxx.widget.MyActivity"
android:taskAffinity="com.xxx.launcher"
public static int getClearEverythingFlagsForLauncher() {
return Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK;
}
final android.content.Intent openIntent = new Intent(context, MyActivity.class);
openIntent.addFlags(getClearEverythingFlagsForLauncher());
But now, launching Activity via sharing intent is pretty out of my control. I can no longer supply getClearEverythingFlagsForLauncher().

singleTask launch mode is not working

Having this weird problem of multiple instances of my activity getting created even after specifying the launchMode as singleTask. Has anybody faced this issue ? Any pointers will be appreciated.
Below is how i have declared my activity in the manifest:
<activity
android:name="com.test.TestActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
And this is how i am launching my activity while it is still in foreground:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
sendIntent.setType("text/plain");
activity.startActivity(Intent.createChooser(sendIntent, "Share"));
Android version that i am testing it on is 5.1.1
Try using singleInstance if you want to get rid of your activity's multiple instances.
<activity
android:name=".MainActivity"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
EXTRA NOTE:
singleTask :- A new task will always be created and a new instance will be pushed to the task as the root. However, if any activity
instance exists in any tasks, the system routes the intent to that
activity instance through the onNewIntent() method call. In this mode,
activity instances can be pushed to the same task. This mode is useful
for activities that act as the entry points.
singleInstance:- Same as singleTask, except that the no activities instance can be pushed into the same task of the singleInstance’s.
Accordingly, the activity with launch mode is always in a single
activity instance task. This is a very specialized mode and should
only be used in applications that are implemented entirely as one
activity.
Copied from : https://stackoverflow.com/a/36520016/3669559
So it seems the issue is with device. On emulator and other devices with same android version singleTask works just fine.

Android Deep linking and singleInstance/singleTask

Possible Duplicate Deep linking and multiple app instances. I have implemented Deep Linking in my app. I have Splash activity that is launcher and MainActivity that handles the Intent as defined in manifest:
<application
android:name=".MyApplication"
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="#drawable/app_logo"
android:label="#string/app_name"
android:largeHeap="true"
android:theme="#style/AppTheme">
<activity
android:name=".ActivitySplash"
android:configChanges="orientation|screenSize"
android:label="#string/app_name">
<intent-filter>
<!-- Launcher activity -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ActivityMain"
android:alwaysRetainTaskState="true"
android:configChanges="orientation|screenSize"
android:exported="true"
android:label="#string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="www.mywebsite.com"
android:pathPrefix="/something"
android:scheme="http" />
</intent-filter>
</activity>
<activity
android:name=".ActivitySignIn"
android:configChanges="screenSize|orientation" />
<activity android:name=".ActivitySignUp" />
</application>
I have set launch mode singleTask to handle onNewIntent(). Now what i want to achieve is that if user opens activity from DeepLinking and there is already some task going on in MainActivity I prompt user a dialog either he want to cancel current task and start new task (which is from deep linking). The issue is If i open another activity from MainActivity and user comes from DeepLinking Intent. Then it would kill the second activity and directly open MainActivity. What i want to achieve is that if app/activity is not running then Intent from DeepLink open as is. And if activity/app is already running then i prompt user to either close current task and perform DeepLink task/intent.
This doesn't really work the way you think it does. You are trying to use launchMode="singleTask", but since you haven't also set "taskAffinity", Android pretty much ignores your launchMode.
You should not need to use either of the special launch modes "singleTask" or "singleInstance" to get what you want.
Try using singleTop launch mode and see if this solves your problem. If ActivityMain is already open and you launch ActivityMain again using your deep-link, onNewIntent() should be called in ActivityMain.
You can also look at my answer to this question which describes a way to determine what Activity to show based on using a static variable to decide whether another Activity is in the stack or not.

Android Intent with Cordova - New app instance launched for every share. Should be only one

I want to share url's from other apps to mine.
I am using this Phonegap Plugin to implement the intents:
https://github.com/chrisekelley/olutindo-app/tree/master/plugins/com.borismus.webintent
Intents are working, but everytime i share an intent from another app, a new instance of my app is launched, but i want to use one instance. If the app is already opened, it should be reinitialized and brought to front or restarted with the shared intent initialization.
I tryed android:launchMode="singleTop", but the same behavior occurs, app would be opened multiple times.
My manifest entry:
<activity android:configChanges="orientation|keyboardHidden|screenSize"
android:name=".StartActivity"
android:label="#string/app_name"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
These is no initialization in my MainActivity because the intents would be initialized from javascript with a own function (See PG plugin above).
Edit: I tryed to use android:launchMode="singleInstance". Now on every share request my app would be brought to top, but the intent is not initialized. How can i initialize the incoming intent request again with cordova, if the app is already started?
Thank you!
I'm not sure how to workaround this. But i'm pretty sure that even if your code is launchMode="singleInstance" or singleTask, the OS will open an different Instance for each intent.action.
The only way to workaround i did find is creating a empty activity that calls your activity with a unique intent.action.
final Intent myIntent = new Intent(this,
MainActivity.class);
myIntent.setAction("android.intent.action.MAIN"); //myIntent.setAction(Intent.ACTION_MAIN);
startActivity(myIntent);
You can do a listening service. When you try to share the service get launched and it opens your activity if one intent.action aways.

Starting the app from Notification remove it from recent app history

I have a problem in one of my apps when I start it from a notification. It never appears in the "recent app" list.
Without notifications, every thing works as expected: I start the app, navigate into and when I quit it (with home button or back button) after that I can go back into it with a long press on home button => ok.
The problem starts when I receive a notification. If I start the app from the notification, it starts the correct screen and I can use the app => ok. But when I quit the app (with home or back button) it do not appears anymore in the "recent app" list. More precisely:
If the app was present in the "recent app" list before launching the app from the notification, it removes it
If the app was not present in the "recent app" list, it does not add it
Below, my original code to add the notification in the status bar:
mNotification = new Notification(R.drawable.ic_notif, message, System.currentTimeMillis());
mNotification.flags |= Notification.FLAG_AUTO_CANCEL;
Intent notificationIntent = new Intent(Intent.ACTION_VIEW,Uri.parse(link));
notificationIntent.putExtra(...);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
mNotification.setLatestEventInfo(context, context.getString(R.string.app_name), message, contentIntent);
notificationManager.notify(ConfigApp.NOTIFICATION_ID, mNotification);
I tried adding a FLAG_ACTIVITY_NEW_TASK but it did not help:
Intent notificationIntent = new Intent(Intent.ACTION_VIEW,Uri.parse(link));
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
notificationIntent.putExtra(...);
Manifest declaration of the Activity started by the notification:
<activity
android:name=".activity.ActivityMessages"
android:label=""
android:windowSoftInputMode="stateHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="hostapp"
android:pathPrefix="/prefixapp"
android:scheme="schemeapp" />
</intent-filter>
</activity>
Does anyone knows how to keep the app in the "recent app" list after being started form a notification?
Add the action android.intent.action.MAIN and the category android.intent.category.LAUNCHER to the specification of your activity. Having two actions and categories seem like a hack but it's actually supported by Android.
<activity
android:name=".activity.ActivityMessages"
android:label=""
android:windowSoftInputMode="stateHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data
android:host="hostapp"
android:pathPrefix="/prefixapp"
android:scheme="schemeapp" />
</intent-filter>
</activity>
That should add it to the recent applications list. The reason is described in the Android developer documentation:
An intent filter of this kind causes an icon and label for the
activity to be displayed in the application launcher, giving users a
way to launch the activity and to return to the task that it creates
any time after it has been launched.
This second ability is important: Users must be able to leave a task
and then come back to it later using this activity launcher. For this
reason, the two launch modes that mark activities as always initiating
a task, "singleTask" and ""singleInstance", should be used only when
the activity has an ACTION_MAIN and a CATEGORY_LAUNCHER filter.
Imagine, for example, what could happen if the filter is missing: An
intent launches a "singleTask" activity, initiating a new task, and
the user spends some time working in that task. The user then presses
the Home button. The task is now sent to the background and is not
visible. Now the user has no way to return to the task, because it is
not represented in the application launcher.
Try adding to the manifest the following:
android:excludeFromRecents = "false"
Good luck,
Luis
You need to use separate Intent Filters for each action/category pair:
<activity
android:name=".activity.ActivityMessages"
android:label=""
android:windowSoftInputMode="stateHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="hostapp"
android:pathPrefix="/prefixapp"
android:scheme="schemeapp" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data
android:host="hostapp"
android:pathPrefix="/prefixapp"
android:scheme="schemeapp" />
</intent-filter>
</activity>
I bumped into same issue. The problem was that my activity had android:label="". When I open Activity from the Notification then it replaces root activity in the app task with current one. And Android excludes activities with empty label from Recents (see source code).
To hide title of activity you should use:
getActionBar().setDisplayShowTitleEnabled(false);
Old topic but I discover solution (and I can't find in web anyone), maybe it will be helpful for sb. It doesn't give possibility to set any activity as a parent but I think (not sure) that you can change it programmatically (in OpenFromNotificationActivity) if it is necessary.
In manifest
<activity android:name=".OpenFromNotificationActivity"
android:label="#string/label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".StartSplashActivity" />
</activity>
<activity android:name=".StartSplashActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Categories

Resources