I have a dictionary-type application that accepts, via an intent filter, arbitrary text shared from other apps, e. g. the browser. The intent filter is attached to a decicated activity ("Lookup") that does some posttranslation of the shared text and invokes my main activity. The lookup activity quietly closes.
I'm testing it with the system browser on Samsung Galaxy with Android 4.0.4, and here's a funny thing I notice. On the first try, it works as expected. I tap "Share", select my app as the target, the lookup activity is invoked, then the main activity is invoked. Now, if I task-switch back to the browser and hit "Share" again on a different piece of text, the menu of sharing targets is not displayed. Instead, my main activity is resumed. Not the lookup one (that'd be reasonable), but the main one. onNewIntent() is not delivered to neither main activity nor Lookup activity.
onResume() is delivered to main, though.
However, if I tap "Back" instead of task-switching back to the browser - that is, explicitly close my main activity - on the next try, the Share operation displays the menu of targets again.
What's up with this behavior? Does the Share functionality remember the target of the last share operation and try to reuse it? If so, why won't it remember the immediate target of the share - my lookup activity? Is this documented?
EDIT: the manifest for the lookup activity goes like this:
<activity android:name=".Lookup"
android:label="#string/IDS_LOOKUPTITLE"
android:theme="#style/Theme.Transparent">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
When I start main activity from lookup activity, I'm trying to reuse an existing one (if any), so the flags are FLAG_ACTIVITY_REORDER_TO_FRONT|FLAG_ACTIVITY_SINGLE_TOP.
Related
I am handling deep links in my app. When a link in the email is clicked, it opens related activity in the app. On back press, it either goes back to email or home (up to intent flags i use).
I need it to go back to latest running activity(if the app was being used before clicking the link in email) or(else) go to first activity to restart the app.
To be clearer: User is on activity C. Email notification comes, checks it and clicks the link inside. It opens up activity E. Here, if user back press, I want to end current task and resume activity C - if activity C task has not been killed by the system. If killed, go to activity A.
Without intent flags, it creates a new task(second app instance) and on back press it goes back to email client.
With NEW_TASK flag, a new tasks starts. If I use CLEAR_TASK flag with this, on back press it goes home.
Manifest
<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="https"
android:host="www.mysite.com"
android:pathPattern="/mypath/*" />
</intent-filter>
FirstActivity
if(getIntent().getData().toString().contains("keyword")){
//intent.setFlags(...);
intent.putExtra("myextra", getIntent().getData().toString());
}
startActivity(intent);
finish();
EmailResultActivity
String data = getIntent().getExtras().getString("myextra");
To achieve this you need to handle deep-link from a common activity. (Eg: If your app has a common activity say XYZ. Then every deep-link should come to XYZ activity and then according to parameters of deep-link, you should move to respective screen) Also you need to make XYZ as singleTask.
“My First App” has an activity that handles a “share” intent. Its activity in AndroidManifest.xml looks like this:
<activity
android:name="com.example.foo.myfirstapp.MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/jpeg"/>
</intent-filter>
</activity>
In KitKat, sharing an image from the album to “My First App” causes MainActivity to be part of the album’s task. This is the desired behavior.
In Lollipop, sharing an image from the album to “My First App” causes a new instance of “My First App” to be launched. If I look at the overview screen, the album task is there...and then there's a separate entry for "My First App". If I share another image, I wind up with two instances of "My First App"...etc.
Question: How do I make Lollipop process the share intent in the same way as KitKat?
Here's what I've done:
I notice that the intents sent from the Album have different flags set depending on the OS. (I got these using getIntent() and looking at mFlags.)
Kitkat: 0x80001 (524289): FLAG_GRANT_READ_URI_PERMISSION, FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
Lollipop: 0x18080001 (403177473): FLAG_GRANT_READ_URI_PERMISSION, FLAG_ACTIVITY_MULTIPLE_TASK, FLAG_ACTIVITY_NEW_DOCUMENT, FLAG_ACTIVITY_NEW_TASK
From reading http://developer.android.com/reference/android/content/Intent.html, it seems that these last three flags are causing the problem. Specifically
When paired with FLAG_ACTIVITY_MULTIPLE_TASK both of these behaviors (FLAG_ACTIVITY_NEW_DOCUMENT or FLAG_ACTIVITY_NEW_TASK) are modified to skip the search for a matching task and unconditionally start a new task.
I’ve been attempting to “override” these flags by specifying android:launchMode and android:documentLaunchMode in the activity in AndroidManifest.xml without success.
From http://developer.android.com/reference/android/R.attr.html#documentLaunchMode, using documentLaunchMode “never” seems promising, since
This activity will not be launched into a new document even if the Intent contains Intent.FLAG_ACTIVITY_NEW_DOCUMENT. This gives the activity writer ultimate control over how their activity is used.
but this didn't work.
I also considered android:taskAffinity, but there doesn’t seem to be a way to say “please prefer whatever task launched you”.
Afraid you can't do anything about this. It isn't under your control. This is a change in the way the "Album" app is launching its "share" Intent. If it doesn't want your Activity in its task, you can't force it in there.
If you have issues with having multiple instances of your "share" activity, you could declare your "share" activity as launchMode="singleTask" or launchMode="singleInstance" (depending on your needs. This may, however, break other things.
I'm trying to implement Facebook's Deep Linking feature on my app and encountered the following scenario:
I have an activity called MainActivity which is declared like so:
<activity
android:name="com.mypackage.android.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
This activity + my package name are also declared in my app's settings on facebook developer website.
Once a link gets clicked on Facebook's app, I'm supposed to handle this event via the onCreate method of my activity.
The following code handle the event:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri target = getIntent().getData();
if (target != null){
// got here via Facebook deep link
// once I'm done parsing the URI and deciding
// which part of my app I should point the client to
// I fire an intent for a new activity and
// call finish() the current activity (MainActivity)
}else{
// activity was created in a normal fashion
}
}
All goes according to plan except for the following scenario:
User launched my app
MainActivity created
SecondaryActivity created
MainActivity finished
App goes to background via the device home button
Deep link gets clicked on Facebook's app
In this case my app goes to foreground again, but MainActivity's onCreate / onNewIntent
don't get called, instead SecondaryActivity's onResume() gets called and restored to it's
last state.
Note: I've tested this issue on a Samsung Nexus with Android 4.2.1 and got to this result, though when tested on Galaxy S1 with Android 2.3.5 it worked as I initially expected.
Any help would be greatly appreciated,
Thank you.
Facebook is starting your app from their own app by explicitly start your "MainActivity" (the one your provided them in the developer page).
by that - Android's default behavior is: if the application already runs, then calling again to startActivity() won't start new task from scratch, but only restore to foreground the already running task.
but the good news are that you can change this default behavior by adding to your MainActivity the android:launchMode="singleTask". it definition is:
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.
from this point you could always respond to the starting intent, and from that point you can always navigate back to the task that already was in background(if exists) by restarting activity with both flags Intent.FLAG_ACTIVITY_SINGLE_TOP && Intent.FLAG_ACTIVITY_CLEAR_TOP combination
See http://developer.android.com/guide/topics/manifest/activity-element.html
You can play with:
android:clearTaskOnLaunch
android:noHistory
android:launchMode
You need to have more information in your intent filter:
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:host="www.yoursite.com" android:scheme="http"></data>
</intent-filter>
This will capture links going to your site (make sure to change the URL), and direct them to whatever Activity you define this intent filter under.
This is my first android app attempt after reading "Android 2 Application Development" and lots of stuff online.
Here is the relevant code:
from MovieRatingsActivity.java [my main]
Intent i = new Intent(MovieRatingsActivity.this, DisplayMovies.class);
startActivity(i);
from Manifest:
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".DisplayMovies"
android:label="Display Movies" >
</activity>
note: i do not have any intent filters for the second activity. Do I need any if it is an explicit intent that I never plan on interacting with another application? I have tried with multiple combinations of different intent filters just out of spite, but its hard to have this answered, as every source I go to jumps to implicit intents and doesn't answer this question.
As for behavior:
Whether in debug mode, or run mode, when I click on the button and create the intent, the emulator switches to the second activity and displays the label at the top, but nothing else. Worse, in debugger mode, when I try to step-into startActivity(i), it just suspends the main thread and goes no where. Do you need a special debug technique for when jumping to next activity?
There is a chance that my intents are fine, my logic to display the list is wrong, but even still I would like to be able to reach the code in the debugger. I also added a System.out.printline at the beginning of the second activities OnCreate method that is not executing.
Do I need any if it is an explicit intent that I never plan on
interacting with another application?
you dont need any explicit intents in that case.
Do you need a special debug technique for when jumping to next
activity?
You could put a breakpoint in onCreate() of second activity.
In my application, I have specified a second activity that can be launched from the launcher, using this manifest entry:
<activity
android:name=".Lists.ListOfListsActivity"
android:icon="#drawable/ic_launcher_lists"
android:launchMode="singleTop"
android:label="#string/lists_activity_name" >
<!-- An Intent filter so that the Lists activity shows in the Launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Let's say I have the app open at the "main" activity then press the home key. My app will still be running, but in background.
Later the user selects the launcher icon I have for "ListsOfListsActivity" from the homescreen.
This will bring the application to the foreground, but NOT at the "ListOfListsActivity", but at whatever it's current activity was when it went to the background (e.g. at the "main"activity).
This is confusing, as the user selected the "ListOfListsActivity" but is shown another one. Then they have to navigate to it.
I had this working better, by specifying launchMode = "singleTask" for the "ListOfListsActivity", but in that mode it cannot be launched from another activity for a result (startActivityForResult() ), and I need to be able to do that to pick a list...
Question:
- how to specify an intent-filter that will force an activity to the foreground and be the selected activity, no matter what the current status of the application and it's current activity??
My final implementation was to define a different taskAfinity string for each activity I wanted to launch independently from the Launcher.
That way, each "shortcut" always launches the activity I want, but the downside that I have not been able to avoid is that the user may have multiple tasks with an activity from my application in it, and maybe the same activity open/active in different tasks....