Neither onCreate() nor onNewIntent() fires when opening the same URI twice - android

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.

Related

How to confirm whether Android app is launched by DeepLink or normal launch?

I have an application where I need to do different task on different launch types. How to detect that app has been launched by deeplink, when my app was in background.
Assuming you already have an activity ready with the required intents what you'll to do is check your activity's if(getIntent().getAction() != null) which means it was launched via a deep-link. Normal intents used for navigation will return null.
Now the issue is if your activity was already running in background and you wrote this code in onCreate(), and the deep-linked activity was set to android:launchMode="singleTask" or launched with a FLAG_ACTIVITY_SINGLE_TOP it won't trigger again.
For this you will have to override onNewIntent(Intent intent) method of your activity, this way you can know each time your activity is started from an intent.
Again, here you can check if(intent.getAction() != null) and intent.getData() to retrieve the data.
One thing to note is to avoid running the same code twice in onCreate and onNewIntent
In case you haven't implemented deep-linking in your app yet you will first need to make use of <intent-filter> to make an activity be available to handle the intent when clicking on a link etc. as an example
<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.example.com"
android:pathPattern="/.*"
android:scheme="https" />
</intent-filter>
You can read more about on the official Docs here as someone already suggested.
Inside any activity you have specified the intent filter, you can get the URL via the Intent that launched the activity.
Activity:-
Intent appLinkIntent = getIntent();
Uri appLinkData = appLinkIntent.getData();
Fragment:-
Intent appLinkIntent = requireActivity().getIntent();
Uri appLinkData = appLinkIntent.getData();
in MainActivity i use this code
if(getIntent().getDataString()!=null &&
getIntent().getDataString().contains("specialWordInDeepLink")){
// confirm is run by deeplink
}
When a clicked link or programmatic request invokes a web URI intent, the Android system tries each of the following actions, in sequential order, until the request succeeds:
Open the user's preferred app that can handle the URI, if one is designated.
Open the only available app that can handle the URI.
Allow the user to select an app from a dialog.
To create a link to your app content, add an intent filter that contains these elements and attribute values in your manifest:
https://developer.android.com/training/app-links/deep-linking
Check out official android doc to know more about deeplinking

Android intent sometimes "handled" instead of ACTION_SEND

My app is supposed to handle shared texts. For example URLs from the amazon app. So I added the following intent-filter to my main activity:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
In my onCreate function of my activity, I'm processing the intent like so:
intent = getIntent();
if(intent.getAction() != null) {
if (intent.getAction().equals(Intent.ACTION_SEND)) {
if (intent.getType().equals("text/plain")) {
onNavigationDrawerItemSelected(1);
}
}
}
The problem is, that sometimes the onCreate function isn't called following a sharing action.
I checked the onResume method, and indeed that's what is called. The problem is that the intents action isn't "ACTION_SEND", but is packagename.handled and doesn't contain the needed information.
Why is that?
If your activity already exists, depending on Intent flags and <activity> attributes, that existing activity instance may be reused. Override onNewIntent() to get the Intent being delivered to an existing activity instance that is causing it to be brought back to the foreground. Look in there for your ACTION_SEND values.

Intent receiving in deep-linked-activity

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?

get result after intent got from file browser

My app can save and load a custom type of files, let's say .foo.
My manifest tells this :
<activity
android:name=".activity.LoadActivity"
android:label="dummy">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:host="*" />
<data android:pathPattern=".*\\.foo" />
</intent-filter>
</activity>
With this, I can launch my activity when a browser try to open a .foo file.
But in my activity, onCreate() is called, but not onActivityResult(), how can I get the path to the file that get clicked ?
I've been searching the doc for a long time, cannot figure this out.
how can I get the path to the file that get clicked?
You can simply retrieve it from the incoming Intent. Just call:
Intent intent = getIntent();
intent.getData().toString();
To answer your whole question:
But in my activity, onCreate() is called, but not onActivityResult(), why ?
You can find the answer directly from the method Javadoc:
Called when an activity you launched exits [...]
This is not your case. onActivityResult is called only on activities that you launched (from your code). In the scenario that you're describing your activity is called from another application (the browser).
onActivityResult will be called after the ending of an activity you launched with method startActivityForResult
How can I get the file that led to my activity then ?
Call getIntent() in onCreate() to get the Intent that was used to start your activity. On there, getData() will return the Uri pointing to the file.

prevent system from launching single top activity with previous intent

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.

Categories

Resources