I have an activity that exists in the manifest like so:
<activity android:name=".activity.Main" android:launchMode="singleTask">
<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="http" android:host="asdf.com"/>
</intent-filter>
</activity>
So that intercept and handle links to http://asdf.com. After I handle one of these intents, I back out of my main activity.
If I go back to my app using the task manager, it will use the same intent, with the same data that I have already handled. Is there way that I can essentially clear or replace this intent, so that it is not reused by the task manager? If I launch normally, by clicking on the app instead of using the task manager, it does not reuse the data.
Thanks
My problem case is when the activity is launched from the home button, so this seems to be a working solution:
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
//handle intent
};
That flag is true if launched from the task manager, in which case I can ignore the intent data. This will likely not work for some other cases.
When you reenter an app that has been set with the "singleTask" launch mode, it does not create a new instance of the the activity if it already exists, instead it reuses up the old activity and brings it to the top of the stack. You must overwrite the onNewIntent() method of your activity to handle whatever actions you may need. When you bring an activity to the front of the stack this onNewIntent() is called an you will need to reset the data in that function.
More info on Tasks and Back Stack here.
Related
I have this situation in my app: I have these activities
<activity
android:name=".presentation.view.start.view.activity.StartActivity"
android:screenOrientation="sensorPortrait"
android:theme="#style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".presentation.view.main.view.activity.MainActivity"
android:configChanges="locale"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppThemeMultiStep"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".presentation.view.firstScreen.view.activity.FirstScreenActivity"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppThemeMultiStep"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".presentation.view.signup.view.activity.LoginViaPinActivity"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppThemeMultiStep" />
StartActivity acts as a sort of "router" based on the application state:
If I am not logged in, it will launch FirstScreenActivity
If I am logged in, it will launch LoginViaPinActivity, which will login the user based on some logic and then launch MainActivity. At the end of all, MainActivity will be at the root of the activities stack.
At some point the app will receive a notification, and when I tap it I want this:
if the app is running and MainActivity is running, open MainActivity (this is easy, there are planty of ways I can to that with various flags) but if it's not running launch StartActivity (so that I can open the app based on all the startup logics implemented there).
So the options I can think of are:
know if an activity is running in order to fire an intent or another (I don't like static fields solutions like you read in many SO post related to this)
make StartActivity the root of the task and find a combination of intent flags which will act like "launch StartActivity, but if it is running at the root of a task, bring that task to front" (this would be my preferred option if possible)
any suggestion is very appreciated
How do you usually handle this kind of situations? (I don't think I'm the first in the world with this problem :) )
Here is my approach -
Make StartActivity as your router as you've said. Just make launchmode to singleTop in your manifest in order to user onNewIntent() method of an Activity.
You'll generate notification, sending some data with contentIntent - as a result clicking on notification you'll be redirected to StartActivity.
Two cases here -
If StartActivity is in stack
onNewIntent gets called with your new Intent - Choose your activity required to be open - If it is already in stack Bring it to front using FLAG_ACTIVITY_REORDER_TO_FRONT flag
If StartActivity is not running or not in stack
Receive bundle of intent that is being got from the intent via notification, parse data and open an activity accordingly.
I have some problems with proper task stack and bringing to front during deep-linking implementations... I have an Activity declared like this:
<activity
android:name=".activity.ArticleSingleActivity"
android:label="#string/app_name"
android:launchMode="standard">
<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="http"
android:host="site.com"
android:pathPrefix="/article" />
</intent-filter>
</activity>
It has launchMode="standard" and no parent, because this Activity can be launched from many places in app: native buttons or from WebView's. Even on bottom of almost every instance there are placed related articles, so it might call itself (new instance) and every article should appear in new "window"
And now: when my app is running in background and is visible in app manager deep-linking fires ArticleSingleActivity on top of other activities without a problem. Intent contains these flags:
FLAG_ACTIVITY_BROUGHT_TO_FRONT
FLAG_ACTIVITY_NEW_TASK
FLAG_RECEIVER_FOREGROUND
When app isn't in back stack (lets say removed by user) ArticleSingleActivity is launched without brought_to_front flag and everything starts like usual, but I've found small improper behaviour. When I navigate to another ArticleSingleActivity (e.g. from related on bottom or to any other Activity from side menu), then press home and try to open same deep-linked url then apps is just bring to front. It doesn't show linked article, but Activity which was on top when app was "homed". Also breakpoints aren't firing in onCreate, onActivityReenter or even onNewIntent. In my opinion system "remebers" that it have in back stack launched Acticity matching this url so it just brings to front whole app, but not proper Activity from the stack (no bring_to_front flag like above).
What can I do with this behaviour when I can't even set custom flags for this Intent delivered by system or do any Intent check in onCreate because it isn't called at all in any alive Activity on the stack?
I have two applications, call them app 1 and app 2, and am starting an activity in app 2 from app 1 for a result.
The activity in app 2 has an activity-alias applied to it so I can add an action (used to start it), and requires a signature-level permission (shared with app 1).
The code used to start the app 2 activity from app 1 is simple:
Intent startActivity = new Intent(MY_CUSTOM_ACTION);
startActivity.setPackage(APP2_PACKAGE);
startActivity.putExtra(EXTRA_SOME_PARAM, ...);
startActivityForResult(startActivity, 1234);
This results in the activity appearing as expected. I then get some user input, and clean up app 2's activity like so:
setResult(Activity.RESULT_OK, someData);
finish();
What I am expecting to happen, is for the calling activity in app 1 to be restored to the foreground and so onActivityResult to be called, but what is actually happening, is that app 2's main/launch activity is started instead.
Sometimes I get the onActivityResult callback with the right response code and data, but app 1's activity is still stopped.
My understanding of Android led me to believe that app 2's activity would have been part of app 1's task stack (and opening the task switcher bears this out), and so finishing it would just pop back to the caller.
I've tried:
enabling task reparenting on the app 2 activity
zeroing out the flags on the startActivity Intent
ensuring that app 2's activity doesn't try to start any other activities
tracing through the Activity lifecycle to see what's happening, but all trails go cold at NativeActivityManager...
clearing any existing tasks that app 2 has open
finishing app 2's activity with finishAffinity (doesn't work when returning a result)
None of this seems to have an effect. Many of the Intent flags and launch modes aren't allowed when starting for a result, so I'm at a loss for what to try next.
Thanks!
Here's the <activity> definitions from the manifest in case it matters:
<activity
android:name=".activity.App2Activity"
android:windowSoftInputMode="adjustResize"
android:configChanges="locale"
android:theme="#style/Some.Theme"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name=".activity.App2ActivityAlias"
android:permission="x.y.z.MY_CUSTOM_PERMISSION"
android:targetActivity=".activity.App2Activity"
android:exported="true">
<intent-filter>
<action android:name="x.y.z.MY_CUSTOM_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>
I have an app with only two activities, MainActivity and SecondActivity. MainActivity has an intent-filter in order to handle a particular set of URLs for deep-linking:
<activity android:name=".MainActivity">
...
<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="http" android:host="example.com" android:pathPrefix="/link" />
</intent-filter>
When an Intent with ACTION_VIEW and an uri is received, I want to redirect to SecondActivity depending on the full URL. Therefore, in both onCreate() and onNewIntent(), I call this method:
private void handleIntent(Intent intent) {
if (Intent.ACTION_VIEW.equalsIgnoreCase(intent.getAction()) && intent.getData() != null) {
if (intent.getData().toString().contains("/link/")) {
startActivity(new Intent(Intent.ACTION_VIEW, intent.getData(), this, SecondActivity.class));
}
}
}
I then test this code by adding a few links to an e-mail (such as http://example.com/link1/one and clicking on them. The problem I noticed is the following:
When I click on a link, MainActivity.onCreate() is correctly fired, and SecondActivity is called. If I then click the same (or another) link again, then all works well. However, if I go back from SecondActivity to MainActivity, then clicking on the same link does not execute my code -- the application is brought back to the front, and nothing else. Neither onCreate() nor onNewIntent() is called.
This would seem to be the correct behavior only if Android assumed that the Activity that declares the intent-filter is the one showing the content of the URI. However, in this case it's not, and I haven't found a way to work around it.
While this is a special case, a similar situation presents itself if the Activity shows a different Fragment depending on the URI. If I later replace that Fragment while staying in the same Activity, then clicking on the URL should redeliver the Intent so the original Fragment can be shown again -- but this does not happen.
Is there a way to receive this intent again? Due to the way the app is stuctured I cannot place the intent-filters on the child activities themselves (indeed, most times they are Fragments as mentioned above) nor declare any of these as singleTop (because they are reused elsewhere for other purposes).
FWIW, the full application code is available here.
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.