Intent chooser callback
Let an app specify an IntentSender as an extra for ACTION_CHOOSER that
will be notified of the user's chosen target. Apps can use this to
implement things like one-touch last used affordances without
implementing their own one-off chooser UI.
Bug 17363684
Change-Id: Ic5d1d0e0c7a0bae3eda6b1d6ade993f3ba865cfa
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index af6f181..7676e4b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -876,12 +876,44 @@
* related methods.
*/
public static Intent createChooser(Intent target, CharSequence title) {
+ return createChooser(target, title, null);
+ }
+
+ /**
+ * Convenience function for creating a {@link #ACTION_CHOOSER} Intent.
+ *
+ * <p>Builds a new {@link #ACTION_CHOOSER} Intent that wraps the given
+ * target intent, also optionally supplying a title. If the target
+ * intent has specified {@link #FLAG_GRANT_READ_URI_PERMISSION} or
+ * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be
+ * set in the returned chooser intent, with its ClipData set appropriately:
+ * either a direct reflection of {@link #getClipData()} if that is non-null,
+ * or a new ClipData built from {@link #getData()}.</p>
+ *
+ * <p>The caller may optionally supply an {@link IntentSender} to receive a callback
+ * when the user makes a choice. This can be useful if the calling application wants
+ * to remember the last chosen target and surface it as a more prominent or one-touch
+ * affordance elsewhere in the UI for next time.</p>
+ *
+ * @param target The Intent that the user will be selecting an activity
+ * to perform.
+ * @param title Optional title that will be displayed in the chooser.
+ * @param sender Optional IntentSender to be called when a choice is made.
+ * @return Return a new Intent object that you can hand to
+ * {@link Context#startActivity(Intent) Context.startActivity()} and
+ * related methods.
+ */
+ public static Intent createChooser(Intent target, CharSequence title, IntentSender sender) {
Intent intent = new Intent(ACTION_CHOOSER);
intent.putExtra(EXTRA_INTENT, target);
if (title != null) {
intent.putExtra(EXTRA_TITLE, title);
}
+ if (sender != null) {
+ intent.putExtra(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, sender);
+ }
+
// Migrate any clip data and flags from target.
int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION
| FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION
@@ -3140,6 +3172,26 @@
"android.intent.extra.REPLACEMENT_EXTRAS";
/**
+ * An {@link IntentSender} that will be notified if a user successfully chooses a target
+ * component to handle an action in an {@link #ACTION_CHOOSER} activity. The IntentSender
+ * will have the extra {@link #EXTRA_CHOSEN_COMPONENT} appended to it containing the
+ * {@link ComponentName} of the chosen component.
+ *
+ * <p>In some situations this callback may never come, for example if the user abandons
+ * the chooser, switches to another task or any number of other reasons. Apps should not
+ * be written assuming that this callback will always occur.</p>
+ */
+ public static final String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER =
+ "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+
+ /**
+ * The {@link ComponentName} chosen by the user to complete an action.
+ *
+ * @see #EXTRA_CHOSEN_COMPONENT_INTENT_SENDER
+ */
+ public static final String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
+
+ /**
* A {@link android.view.KeyEvent} object containing the event that
* triggered the creation of the Intent it is in.
*/
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5267811..0bc1a8d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -16,13 +16,20 @@
package com.android.internal.app;
+import android.app.Activity;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
+import android.util.Slog;
public class ChooserActivity extends ResolverActivity {
+ private static final String TAG = "ChooserActivity";
+
private Bundle mReplacementExtras;
+ private IntentSender mChosenComponentSender;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -60,11 +67,14 @@
initialIntents[i] = in;
}
}
+ mChosenComponentSender = intent.getParcelableExtra(
+ Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
setSafeForwardingMode(true);
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
}
+ @Override
public Intent getReplacementIntent(String packageName, Intent defIntent) {
if (mReplacementExtras != null) {
final Bundle replExtras = mReplacementExtras.getBundle(packageName);
@@ -77,6 +87,22 @@
return defIntent;
}
+ @Override
+ public void onActivityStarted(Intent intent) {
+ if (mChosenComponentSender != null) {
+ final ComponentName target = intent.getComponent();
+ if (target != null) {
+ final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target);
+ try {
+ mChosenComponentSender.sendIntent(this, Activity.RESULT_OK, fillIn, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.e(TAG, "Unable to launch supplied IntentSender to report "
+ + "the chosen component: " + e);
+ }
+ }
+ }
+ }
+
private void modifyTargetIntent(Intent in) {
final String action = in.getAction();
if (Intent.ACTION_SEND.equals(action) ||
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index b9cbc62..ccffa19 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -644,10 +644,12 @@
public void safelyStartActivity(Intent intent) {
if (!mSafeForwardingMode) {
startActivity(intent);
+ onActivityStarted(intent);
return;
}
try {
startActivityAsCaller(intent, null, UserHandle.USER_NULL);
+ onActivityStarted(intent);
} catch (RuntimeException e) {
String launchedFromPackage;
try {
@@ -662,6 +664,10 @@
}
}
+ public void onActivityStarted(Intent intent) {
+ // Do nothing
+ }
+
void showAppDetails(ResolveInfo ri) {
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", ri.activityInfo.packageName, null))