Android intent sometimes "handled" instead of ACTION_SEND - android

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.

Related

Why starting the Activity by an intent (Intent.ACTION_VIEW) doesn't always start a new activity and call onCreate()?

I have two Android applications.
The first application is the "browser". It gets an URL and displays it in a WebView. The corresponding activity is declared as:
<activity android:name=".MainActivity">
<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.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity>
The second application has a few buttons. Tapping each button opens the first application and sends the URL to it using Intent.ACTION_VIEW:
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse(urlString);
intent.setData(uri);
intent.setComponent(new ComponentName("com.custom.browser", "com.custom.browser.MainActivity"));
startActivity(intent);
I expect this code to start a new activity as per https://developer.android.com/reference/android/app/Activity. So the browser application retrieves the URL in onCreate() by using the code:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
Intent intent = getIntent();
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
displayUrl(intent.getDataString());
}
...
}
However I found that sometimes onCreate() is not called. After calling startActivity(intent), the browser application is just brought to the front, therefore displaying a previous URL.
I can override this behavior by moving the intent retrieval code in the onResume().
However I'd like to understand what am I doing wrong? Shouldn't the method startActivity(Intent) always start a new activity and always call onCreate(), as suggested by the Android documentation?
I expect this code to start a new activity
That is not necessarily what will happen.
However I found that sometimes onCreate() is not called. After calling startActivity(intent), the browser application is just brought to the front,
Yes, that will happen if the activity you are starting is already running at the front of a task. See the documentation for tasks.
I can override this behavior by moving the intent retrieval code in the onResume().
That will not work. Override onNewIntent() and get the new Intent there. Or, adjust the flags in your Intent, or adjust the manifest settings for the activity that you are starting, as is discussed in the documentation for tasks.

How to get plain text from NFC Tag?

I have tried to implement the code from Google documentation but it is still not clear for me. What am I missing here?
This is method in MainActivity.java:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
Parcelable[] rawMessages =
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMessages != null) {
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < rawMessages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
Log.i("nfcccc",messages[i].toString());
}
// Process the messages array.
Log.i("from tag: ",messages.toString());
Toast.makeText(this,messages.toString(),Toast.LENGTH_LONG).show();
}
}
}
In onCreated method I have also initialized nfcAdapter:
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
In manifest file I have this:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I spent couple of days trying to implement that, but it is still not working.
What I am doing is that I use NFC Tools android app to save plain text and the I want to read this data in my own app>
What you are missing is the interaction with the Activity's launch mode as NFC handling is done by another System App and then with what Google document as how to use NFC it is just like your application is receiving data from another App.
So onNewIntent
This is called for activities that set launchMode to "singleTop" in their package
https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)
Your application will have launchMode as standard as default therefore onNewIntent is never called
There are 2 standard parts for using the old NFC API's with Intents
Activity is not running (which usually means the App Task is not running), set manifest filters in Activity and then in oncreate use getIntent and then process the Intent for being an NFC based Intent (as per you would do on onNewIntent)
Activity (and App) is running and in the foreground, enableForegroundDispatch https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch which basically tells the NFC system app to restart this Activity when it is the "Top" activity as though it was a "singleTop" Activity, then onNewIntent is called by the incoming Intent (This has a negative side effect of Pausing and Resuming your Activity)
Note there are different ways to setup launchMode and what Activity is top of the stack to get it to tie up sending the Intent to onNewIntent but they are less common.
Best to handle NFC Intents in onCreate and alsoenableForegroundDispatch with the same handling in onNewIntent
An even better way to get more control is use the newer and better enableReaderMode API https://developer.android.com/reference/android/nfc/NfcAdapter#enableReaderMode(android.app.Activity,%20android.nfc.NfcAdapter.ReaderCallback,%20int,%20android.os.Bundle)
Summary
So just add the code for foregroundDispatch as per https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch

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

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.

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

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.

Categories

Resources