Android Deep linking and singleInstance/singleTask - android

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.

Related

Running android apps particular activity independently than rest of activities

THIS QUESTION IS SOLVED, PROVIDING ANSWER FOR FUTURE SO VISITORS BELOW
Context :
I am developing a default phone app. Which handles action.DIAL and action.CALL from My application as well as it handles these both intents from other apps too.
What is problem :
If my CallActivity is running ( if any call is ongoing ) then -
When i again try to open my app, it is presenting me CallActivity only.
And because of this i am unable to make another call by opening my app.
What manifest.xml looks like :
<application
android:allowBackup="true"
android:directBootAware="true"
android:fullBackupContent="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.CALL_BUTTON" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity
android:name=".CallActivity"
android:label="#string/title_activity_call"
android:process=":CallManager"
android:screenOrientation="portrait"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter android:priority="800">
<action android:name="android.intent.action.ACTION_CALL" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
</application>
What i found when searched for the said problem :
How can I launch an independent activity to my application as well as the application of phone launches the activity to make calls?
https://stackoverflow.com/questions/9458300/how-to-create-an-android-activity-and-service-that-use-separate-processes
What are my efforts taken :
You can note i tried android:process=":CallManager" attribute in manifest for my CallActivity
Did that solved the issue :
Nope. I am still unable to open my MainActivity, whenever CallActivity is running. But is caused the problem that now my call is not getting ended as this is completely another process it is unable to reference other calls which remain there in older process.
ANSWER
Did android:process was correct :
No not at all in this case, as it is used when our app memory usage increases over particular limit android starts caching its services and resources in order to avoid it developers generally use android:process which allows them another heap memory available with same heap size.
** What was problem :**
It was related to task and its affinities
Both activities needed to be separated for tasks and their affinities
like :
<activity
android:name="com.example.ActivityA"
android:label="Activity A"
android:launchMode="singleInstance"
android:taskAffinity="com.example.AffinityA" >
</activity>
<activity
android:name="com.example.ActivityB"
android:label="Activity B"
android:launchMode="singleInstance"
android:taskAffinity="com.example.AffinityB" >
</activity>
I must thanks #JayWozz for his answer over SO https://stackoverflow.com/a/45488126/9810130 and must appreciate #gabe-sechan for his sincere help and efforts and for giving his value-able time for this thread.
I think you're basically asking for a new stack of activities. Try launching the intent with FLAG_ACTIVITY_NEW_DOCUMENT| FLAG_ACTIVITY_MULTIPLE_TASKS That will launch the activity as if its a new set of activities, with its own separate listing in the recent apps list. See the definiton of these flags at https://developer.android.com/reference/android/content/Intent

React Native deeplinking and android:launchMode problem

I have been trying deeplinking in a react-native application and trying to directly open a screen inside a navigator. I use react-native-deep-linking and react-navigation as libraries. Also nested navigators are used. Deeplinking works correctly except I have some problems with the android:launchMode property.
This is the results I get for each of the android:launchMode options.
android:launchMode="standard" - App opens using the deeplink but opens up an entirely new application.
android:launchMode="singleTask" - App opens using the deeplink. If I open the app using another link. App comes to the foreground but it directs to the previous link.
android:launchMode="singleTop" - App opens using the deeplink but opens up an entirely new application.
android:launchMode="singleInstance" - App opens using the deeplink but opens up an entirely new application.
If I remove the android:launchMode property, again the same thing happens as the "standard" mode because it is the default.
What option can I use to resolve this problem? Is there any #override that I can do inside the main activity?
Below is my AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".MainApplication"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:roundIcon="#mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<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"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.VIEW"/>
<data android:scheme="https" android:host="*.myapplive.com" android:pathPrefix="/"/>
<data android:scheme="https" android:host="*.myapp.com" android:pathPrefix="/"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
Not sure if still relevant but may help someone in future.
The reason is that when activity is in any mode other than standard, re-opening the app from the background will not replace the original intent used to launch the app from the killed state. Instead, those new intents will be directed to the onNewIntent(Intent intent) method. There, you can call setIntent(Intent intent) in order to replace activity's intent with the new one.
You can override this method in your MainActivity. (Note that when overriding this method in React I had to make it public and not protected as I would usually do in Android. Not sure why yet)
Read more here - https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)

Android: Recents starts incorrect activity

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.

How to make sure that the launcher activity always run when the app is launched from the home

I have 2 activities in my app. The A activity is the launcher one. When I run the app the very first time, the launcher activity runs, but when I press the home button and restart the app from there by clicking the app icon, I always get the B activity running.
I want to make sure that the activity A should always run when starting the app.
This is the manifest code:
<application
android:icon="#drawable/icon"
android:label="#string/app_name" >
<activity
android:name="com.velosys.smsManager.Activities.a"
android:clearTaskOnLaunch="true"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.velosys.smsManager.Activities.b" />
</application>
I have searched a lot for this but did not get any clue.
Maybe android:launchMode="singleInstance" on activity B will get it done?

How can I access to my application which is already running?

I run my application "A" normally with the launch icon in the "Applications" menu.
I press the home button, so my application "A" is still running on my phone.
Now I browse my files presents in my phone, and I use the share action to share this file with my application "A".
The file is sharing perfectly, but instead of using the instance of my application already opened, it opens a new instance of my application "A".
If I quit this new instance, the first instance is still running and it's a problem concerning the security goal of my application.
I try to use the FLAG_ACTIVITY_CLEAR_TOP to use the activity in the first instance, but it doesn't work because it isn't the same application which is launched by the OS.
Is there a way to do this ? And if yes can you give me some hints or some leads to follow ?
My manifest :
<?xml version="1.0" encoding="utf-8"?>
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar" >
<activity android:name=".SplashScreenActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ExplorerActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="#android:style/Theme.Light.NoTitleBar" >
</activity>
<activity
android:name=".ChooseDialogActivity"
android:theme="#android:style/Theme.Translucent.NoTitleBar" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
</application>
Here is my manifest. Usual process : SplashScreenActivity -> ExplorerActivity
Share process : ChooseDialogActivity -> ExplorerActivity
What I want, it's that the second ExplorerActivity has to be the same that the first ExplorerActivity if this activity already exists.
Please don't use launchMode="singleInstance". This is not what you want. singleInstance is only for HOME-screens and similar apps.
Try instead to use Intent.FLAG_ACTIVITY_NEW_TASK when sharing. This will separate your application from the files-browsing application and may get the behaviour you want. You may also need to add FLAG_ACTIVIY_CLEAR_TOP as well, depending on how you've programmed your app.
EDIT
When you launch ExplorerActivity from ChooserActivity, do this (or something similar):
Intent intent = new Intent(this, ChooserActivity.class);
intent.addFlags(Intent.ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
If there is already a task running that contains the ChooserActivity, this should bring that task to the foreground instead of creating a new instace of ChooserActivity.
use the launchmode this will keep one instance from your main activity so make this change for the home activity .
<activity ..
...
..
android:launchMode="singleInstance" />
try android:launchMode="singleInstance"

Categories

Resources