Start Activity with NFC in a specific task - android

My app contains a number of activities. One of these activities responds to an NFC intent filter, as well as standard intents, however, this activity is launching in it's own task, and not in the same task as the app. The app is not necessarily running when the NFC intent is initiated but if it is, I want the activity to launch in the same task to ensure a seamless user experience. At the moment, the app is behaving as though there are 2 of them running.
Here is the manifest for my NFC activity:
<activity
android:name="name.subname.app.activity.ItemSummaryActivity"
android:label="#string/title_activity_item_summary" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<data android:mimeType="application/vnd.name.nfcdemo" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Is it possible to launch the activity in the existing task, if it exists?

I see two options here:
1) Add android:launchMode="singleTask" to activity tag in the manifest:
<activity
android:name="name.subname.app.activity.ItemSummaryActivity"
android:label="#string/title_activity_item_summary"
android:launchMode="singleTask" >
"singleTask":
The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.
2) Supply Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT flag to startActivity() intent. But, if the activity is triggered by NFC (and using this option is not feasible) consider what #NFC guy has to say here.

Related

Android deep links opens in the app I linked from instead of launching a separate instance of my app

I am using react-native. Deep linking is working great for ios. For Android however, I can't seem to figure out how to open my app properly from deep links.
When opening a deep link from e.g. Firefox, my app is launched inside the firefox window. It is the same when I use a deep link-tester app. Everything works as intended with the app with the exception that it is not launched from the proper app.
What am I doing wrong? Here is an excerpt from my AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges=
"keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<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="myapp" />
</intent-filter>
</activity>
Take a look at https://developer.android.com/guide/components/activities/tasks-and-back-stack
When declaring an activity in your manifest file, you can specify how the activity should associate with a task using the element's launchMode attribute.
The launchMode attribute specifies an instruction on how the activity should be launched into a task. There are four different launch modes you can assign to the launchMode attribute:
Try putting android:launchMode="singleTask" on your activity tag
The system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.
https://developer.android.com/guide/components/activities/tasks-and-back-stack#ManifestForTasks
Further background reading includes (and many other things you can try):
FLAG_ACTIVITY_NEW_TASK clarification needed
Android Task Affinity Explanation

launchMode="singleTask" does not create a new task

I have 2 activities: Activity A and Activity B. Activity A's launch mode is standard, and Activity B's launch mode is singleTask.
<activity
android:name=".AActivity"
android:label="#string/app_name"
android:launchMode="standard">
<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" />
<data android:scheme="dd"></data>
<data android:host="a"></data>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity
android:name=".BActivity"
android:label="#string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="dd"></data>
<data android:host="b"></data>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
I launch the app, and the launcher Activity A starts. Then i press Home Button and return to home screen of the phone. Then i launch browser app and type the following:
dd://b
to open Activity B. The system navigates to my App and starts activity B on top of activity A. At this point if i press back button, activity B is popped and i see activity A.
This is not what i expected because the android documentation states:
For singleTask activities, the system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.
What i understand from these sentences is, in my case since an instance of Activity B does not exist, a new task should have been launched and it should have had only Activity B at its stack(Another instance of my app should still exist in a separate task and it should have Activity A in its stack). Then if i press back while i was at Activity B, since it is the only activity in the backstack, it is popped out and the system returns to the browser.
Why this is not the case? How does android system know that my app is open and navigates to it and starts activity B on top of existing app stack instead of launching another instance of my app and letting two instances of my app have their own stacks? What does it mean to instantiate activity in a new task? Can anyone explain?
Thanks.
In addition to the accepted answer, I found an explanation to this problem. As android documentation states:
The affinity indicates which task an activity prefers to belong to. By default, all the activities from the same application have an affinity for each other. So, by default, all activities in the same application prefer to be in the same task which belongs to that app(The app that has the Activity B). However, you can modify the default affinity for an activity.
So, if your app is running, and you start an activity, which has launchMode:singleTask attribute, unless you explicitly state taskAffinity attribute, it starts the activity in the same task. But if you explicitly state an affinity in your manifest:
<activity
android:name=".BActivity"
android:label="#string/app_name"
android:taskAffinity=""
android:launchMode="singleTask">
</activity>
then it starts the activity in a new task.
You can see which activity belongs to which task by the following adb command:
adb shell dumpsys activity
and also, to better understand launchMode concept, you can see the following app in the Google Play: https://play.google.com/store/apps/details?id=com.novoda.demos.activitylaunchmode
You said that your app navigate to B in passing by A.
So, i think you have two stack. In the first, you have only A, in the second you have only B. If you press back, then B is popped and disapeared but A exists and is showing because A in behind B even if A and B are in separate stack.
If you want that B pop and then you return to home, try to call "finish()" after call B from A

Child Activity gets killed after parent activity is finished

I have 2 apps, one of them is calling an activity from the second one through an intent filter like so:
Call in App1 (Parent app)
Intent openApp = new Intent("com.app.intent.Activity2");
startActivity(openApp );
Intent filter in App2 (child app)
<activity
android:name=".app.activity.Activity2"
android:label="#string/app_name"
android:launchMode="singleInstance"
>
<intent-filter>
<action android:name="com.app.intent.Activity2" />
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
At one point the Parent application has to close but the problem is the Activity2 from the second application gets killed as well. I can see that Activity2 is actually running under the same package, is there any way to have the Activity2 persist and run even after the parent applicaiton is closed ?
Thank you
This discussion seems to have what you need. Seems like you need to get the launch intent from the package you want to launch and use that in your intent.
Launch an application from another application on Android
Android will generally run all components from the same APK in a single process, so if one of those crashes the process then they will all be gone.
You can however tell it to put an activity or service in its own process.
<activity
android:name=".app.activity.Activity2"
android:label="#string/app_name"
android:launchMode="singleInstance"
android:process=":my_unique_process">
Of course if your process is dieing unexpectedly, that's a problem that needs to be understood and fixed - this would just be a temporary workaround.

How can I make sure Android Beam doesn't start a new instance of my 'singleTop' activity?

First, the use case : 2 phones have my app opened on the same screen. I want one user to be able to share the screen content (data) with the other one without necessarily opening a new instance of the activity when beaming using NFC.
(both Android devices are running Ice Cream Sandwich)
So I have a singleTop activity declared like this in the manifest.
<activity android:name=".activity.MyActivity" android:configChanges="orientation|keyboardHidden" android:launchMode="singleTop">
<intent-filter android:label="#string/activityLabel">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="a.b.c/x.y.z" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/x.y.z"/>
</intent-filter>
</activity>
When a VIEW action is fired and the activity is already on top, the onNewIntent() method is called in the same instance if the activity.
When a NDEF_DISCOVERED action is fired and the activity is already on top, the onCreate() method is called in a new instance of the activity.
You describe the case that the app is already open and the proper Activity is in the foreground. In that case, you can make use of the foreground-dispatching of NFC intents by calling NfcAdapter.enableForegroundDispatch() in your Activity's onResume() (and disableForegroudDispatch() in onPause()). This will force all NFC Intents to be delivered to your Activity (via onNewIntent()).
I don't have an answer for you. But I have a workaround: have the NDEF_DISCOVERED start a new activity. Make that activity invisible (Theme.NoBackground) and in the onCreate, make it start the MyActivity with singleTop and finish immediately. MyActivity should now appear with onNewIntent.
Have you looked at the Android Beam sample:
http://developer.android.com/resources/samples/AndroidBeamDemo/index.html
It implements this behavior that you want (minus VIEW intent filtering). I'm not sure if that intent will affect the NDEF_DISCOVERED one, but it would be interesting to maybe modify the Android Beam sample to see if you can cause the same behavior as your app.
You should use: android:launchMode="singleInstance"
Works for me.

How can I start the next matching activity if my app is the default one

I have an activity that works as a hook for various intents. Specifically, the VOICE_COMMAND and CALL_PRIVILEGED. (It is a requirement to use these.)
<activity android:name=".MyActivity"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.VOICE_COMMAND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.CALL_PRIVILEGED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
</activity>
I ask the user to set my app as the default action for these intents so they don't have to select it every time. This is also a requirement.
The problem is that in certain cases I want my activity to work transparently and pass the intent to the dialer or other app. This should be selectable by the user. I achieved this by using getPackageManager().setComponentEnabledSetting(myCompName, isEnabled, PackageManager.DONT_KILL_APP) on my activity in certain places of the code.
Is there a more elegant way to do this? I tried startNextMatchingActivity(getIntent()) but that does not start anything (returns false). Does this mean that if there is a default action, then everything else is ignored from the intent resolution?
Currently (on Android 2.3) there seems to be no other way of forwarding the intent than doing it manually. Additionally, to send CALL_PRIVILEGED intent the app must have special permissions that only system apps have. (The app will receive not allowed exceptions otherwise.)
All in all, the intent must be converted to some other intent that my app can send and start the next activity manually by retrieving the list of applicable activities from the PackageManager.queryIntentActivities() API.

Categories

Resources