Loading api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -8282,6 +8282,7 @@ package android.content { field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE"; field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE"; field public static final java.lang.String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; Loading @@ -8293,6 +8294,7 @@ package android.content { field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list"; field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; Loading Loading @@ -8325,6 +8327,7 @@ package android.content { field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle"; field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list"; field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER"; field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; api/system-current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -8508,6 +8508,7 @@ package android.content { field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE"; field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE"; field public static final java.lang.String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; Loading @@ -8519,6 +8520,7 @@ package android.content { field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list"; field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; Loading Loading @@ -8554,6 +8556,7 @@ package android.content { field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle"; field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list"; field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER"; field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; core/java/android/content/Intent.java +70 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.content; import android.content.pm.ApplicationInfo; import android.os.ResultReceiver; import android.provider.MediaStore; import android.util.ArraySet; Loading Loading @@ -3291,10 +3292,78 @@ public class Intent implements Parcelable, Cloneable { /** * An Intent describing the choices you would like shown with * {@link #ACTION_PICK_ACTIVITY}. * {@link #ACTION_PICK_ACTIVITY} or {@link #ACTION_CHOOSER}. */ public static final String EXTRA_INTENT = "android.intent.extra.INTENT"; /** * An Intent[] describing additional, alternate choices you would like shown with * {@link #ACTION_CHOOSER}. * * <p>An app may be capable of providing several different payload types to complete a * user's intended action. For example, an app invoking {@link #ACTION_SEND} to share photos * with another app may use EXTRA_ALTERNATE_INTENTS to have the chooser transparently offer * several different supported sending mechanisms for sharing, such as the actual "image/*" * photo data or a hosted link where the photos can be viewed.</p> * * <p>The intent present in {@link #EXTRA_INTENT} will be treated as the * first/primary/preferred intent in the set. Additional intents specified in * this extra are ordered; by default intents that appear earlier in the array will be * preferred over intents that appear later in the array as matches for the same * target component. To alter this preference, a calling app may also supply * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p> */ public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; /** * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection * from the chooser activity presented by {@link #ACTION_CHOOSER}. * * <p>An app preparing an action for another app to complete may wish to allow the user to * disambiguate between several options for completing the action based on the chosen target * or otherwise refine the action before it is invoked. * </p> * * <p>When sent, this IntentSender may be filled in with the following extras:</p> * <ul> * <li>{@link #EXTRA_INTENT} The first intent that matched the user's chosen target</li> * <li>{@link #EXTRA_ALTERNATE_INTENTS} Any additional intents that also matched the user's * chosen target beyond the first</li> * <li>{@link #EXTRA_RESULT_RECEIVER} A {@link ResultReceiver} that the refinement activity * should fill in and send once the disambiguation is complete</li> * </ul> */ public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; /** * A {@link ResultReceiver} used to return data back to the sender. * * <p>Used to complete an app-specific * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER refinement} for {@link #ACTION_CHOOSER}.</p> * * <p>If {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} is present in the intent * used to start a {@link #ACTION_CHOOSER} activity this extra will be * {@link #fillIn(Intent, int) filled in} to that {@link IntentSender} and sent * when the user selects a target component from the chooser. It is up to the recipient * to send a result to this ResultReceiver to signal that disambiguation is complete * and that the chooser should invoke the user's choice.</p> * * <p>The disambiguator should provide a Bundle to the ResultReceiver with an intent * assigned to the key {@link #EXTRA_INTENT}. This supplied intent will be used by the chooser * to match and fill in the final Intent or ChooserTarget before starting it. * The supplied intent must {@link #filterEquals(Intent) match} one of the intents from * {@link #EXTRA_INTENT} or {@link #EXTRA_ALTERNATE_INTENTS} passed to * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} to be accepted.</p> * * <p>The result code passed to the ResultReceiver should be * {@link android.app.Activity#RESULT_OK} if the refinement succeeded and the supplied intent's * target in the chooser should be started, or {@link android.app.Activity#RESULT_CANCELED} if * the chooser should finish without starting a target.</p> */ public static final String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER"; /** * A CharSequence dialog title to provide to the user when used with a * {@link #ACTION_CHOOSER}. Loading core/java/com/android/internal/app/ChooserActivity.java +220 −13 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; Loading @@ -34,6 +35,7 @@ import android.os.IBinder; import android.os.Message; import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; Loading @@ -53,11 +55,13 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; private static final int QUERY_TARGET_LIMIT = 5; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; private IntentSender mRefinementIntentSender; private RefinementResultReceiver mRefinementResultReceiver; private ChooserTarget[] mCallerChooserTargets; Loading Loading @@ -113,6 +117,32 @@ public class ChooserActivity extends ResolverActivity { if (target != null) { modifyTargetIntent(target); } Parcelable[] targetsParcelable = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS); if (targetsParcelable != null) { final boolean offset = target == null; Intent[] additionalTargets = new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length]; for (int i = 0; i < targetsParcelable.length; i++) { if (!(targetsParcelable[i] instanceof Intent)) { Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: " + targetsParcelable[i]); finish(); super.onCreate(null); return; } final Intent additionalTarget = (Intent) targetsParcelable[i]; if (i == 0 && target == null) { target = additionalTarget; modifyTargetIntent(target); } else { additionalTargets[offset ? i - 1 : i] = additionalTarget; modifyTargetIntent(additionalTarget); } } setAdditionalTargets(additionalTargets); } mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS); CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE); int defaultTitleRes = 0; Loading @@ -125,7 +155,7 @@ public class ChooserActivity extends ResolverActivity { initialIntents = new Intent[pa.length]; for (int i=0; i<pa.length; i++) { if (!(pa[i] instanceof Intent)) { Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]); Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]); finish(); super.onCreate(null); return; Loading @@ -141,8 +171,7 @@ public class ChooserActivity extends ResolverActivity { final ChooserTarget[] targets = new ChooserTarget[pa.length]; for (int i = 0; i < pa.length; i++) { if (!(pa[i] instanceof ChooserTarget)) { Log.w("ChooserActivity", "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]); Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]); finish(); super.onCreate(null); return; Loading @@ -153,11 +182,22 @@ public class ChooserActivity extends ResolverActivity { } mChosenComponentSender = intent.getParcelableExtra( Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); mRefinementIntentSender = intent.getParcelableExtra( Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); } @Override protected void onDestroy() { super.onDestroy(); if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } } @Override public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { Intent result = defIntent; Loading Loading @@ -211,6 +251,37 @@ public class ChooserActivity extends ResolverActivity { } } @Override protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { if (mRefinementIntentSender != null) { final Intent fillIn = new Intent(); final List<Intent> sourceIntents = target.getAllSourceIntents(); if (!sourceIntents.isEmpty()) { fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0)); if (sourceIntents.size() > 1) { final Intent[] alts = new Intent[sourceIntents.size() - 1]; for (int i = 1, N = sourceIntents.size(); i < N; i++) { alts[i - 1] = sourceIntents.get(i); } fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts); } if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); } mRefinementResultReceiver = new RefinementResultReceiver(this, target, null); fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER, mRefinementResultReceiver); try { mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null); return false; } catch (SendIntentException e) { Log.e(TAG, "Refinement IntentSender failed to send", e); } } } return super.onTargetSelected(target, alwaysCheck); } void queryTargetServices(ChooserListAdapter adapter) { final PackageManager pm = getPackageManager(); int targetsToQuery = 0; Loading Loading @@ -258,8 +329,9 @@ public class ChooserActivity extends ResolverActivity { targetsToQuery++; } } if (targetsToQuery >= QUERY_TARGET_LIMIT) { if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_LIMIT); if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) { if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_SERVICE_LIMIT); break; } } Loading Loading @@ -303,6 +375,43 @@ public class ChooserActivity extends ResolverActivity { mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } if (selectedTarget == null) { Log.e(TAG, "Refinement result intent did not match any known targets; canceling"); } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) { Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget + " cannot match refined source intent " + matchingIntent); } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) { finish(); return; } onRefinementCanceled(); } void onRefinementCanceled() { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } finish(); } boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) { final List<Intent> targetIntents = target.getAllSourceIntents(); for (int i = 0, N = targetIntents.size(); i < N; i++) { final Intent targetIntent = targetIntents.get(i); if (targetIntent.filterEquals(matchingIntent)) { return true; } } return false; } @Override ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { Loading @@ -313,17 +422,19 @@ public class ChooserActivity extends ResolverActivity { return adapter; } class ChooserTargetInfo implements TargetInfo { private final TargetInfo mSourceInfo; final class ChooserTargetInfo implements TargetInfo { private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; private final Drawable mDisplayIcon; private final Intent mFillInIntent; private final int mFillInFlags; public ChooserTargetInfo(ChooserTarget target) { this(null, target); } public ChooserTargetInfo(TargetInfo sourceInfo, ChooserTarget chooserTarget) { public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) { mSourceInfo = sourceInfo; mChooserTarget = chooserTarget; mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); Loading @@ -333,6 +444,18 @@ public class ChooserActivity extends ResolverActivity { } else { mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0); } mFillInIntent = null; mFillInFlags = 0; } private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) { mSourceInfo = other.mSourceInfo; mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; mDisplayIcon = other.mDisplayIcon; mFillInIntent = fillInIntent; mFillInFlags = flags; } @Override Loading @@ -358,22 +481,42 @@ public class ChooserActivity extends ResolverActivity { } private Intent getFillInIntent() { return mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent(); Intent result = mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent(); if (result == null) { Log.e(TAG, "ChooserTargetInfo#getFillInIntent: no fillIn intent available"); } else if (mFillInIntent != null) { result = new Intent(result); result.fillIn(mFillInIntent, mFillInFlags); } return result; } @Override public boolean start(Activity activity, Bundle options) { return mChooserTarget.sendIntent(activity, getFillInIntent()); final Intent intent = getFillInIntent(); if (intent == null) { return false; } return mChooserTarget.sendIntent(activity, intent); } @Override public boolean startAsCaller(Activity activity, Bundle options, int userId) { return mChooserTarget.sendIntentAsCaller(activity, getFillInIntent(), userId); final Intent intent = getFillInIntent(); if (intent == null) { return false; } return mChooserTarget.sendIntentAsCaller(activity, intent, userId); } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return mChooserTarget.sendIntentAsUser(activity, getFillInIntent(), user); final Intent intent = getFillInIntent(); if (intent == null) { return false; } return mChooserTarget.sendIntentAsUser(activity, intent, user); } @Override Loading @@ -395,6 +538,21 @@ public class ChooserActivity extends ResolverActivity { public Drawable getDisplayIcon() { return mDisplayIcon; } @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); } @Override public List<Intent> getAllSourceIntents() { final List<Intent> results = new ArrayList<>(); if (mSourceInfo != null) { // We only queried the service for the first one in our sourceinfo. results.add(mSourceInfo.getAllSourceIntents().get(0)); } return results; } } public class ChooserListAdapter extends ResolveListAdapter { Loading Loading @@ -542,4 +700,53 @@ public class ChooserActivity extends ResolverActivity { connection = c; } } static class RefinementResultReceiver extends ResultReceiver { private ChooserActivity mChooserActivity; private TargetInfo mSelectedTarget; public RefinementResultReceiver(ChooserActivity host, TargetInfo target, Handler handler) { super(handler); mChooserActivity = host; mSelectedTarget = target; } @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (mChooserActivity == null) { Log.e(TAG, "Destroyed RefinementResultReceiver received a result"); return; } if (resultData == null) { Log.e(TAG, "RefinementResultReceiver received null resultData"); return; } switch (resultCode) { case RESULT_CANCELED: mChooserActivity.onRefinementCanceled(); break; case RESULT_OK: Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT); if (intentParcelable instanceof Intent) { mChooserActivity.onRefinementResult(mSelectedTarget, (Intent) intentParcelable); } else { Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent" + " in resultData with key Intent.EXTRA_INTENT"); } break; default: Log.w(TAG, "Unknown result code " + resultCode + " sent to RefinementResultReceiver"); break; } } public void destroy() { mChooserActivity = null; mSelectedTarget = null; } } } core/java/com/android/internal/app/ResolverActivity.java +226 −63 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -8282,6 +8282,7 @@ package android.content { field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE"; field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE"; field public static final java.lang.String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; Loading @@ -8293,6 +8294,7 @@ package android.content { field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list"; field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; Loading Loading @@ -8325,6 +8327,7 @@ package android.content { field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle"; field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list"; field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER"; field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
api/system-current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -8508,6 +8508,7 @@ package android.content { field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE"; field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE"; field public static final java.lang.String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; Loading @@ -8519,6 +8520,7 @@ package android.content { field public static final java.lang.String EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list"; field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; Loading Loading @@ -8554,6 +8556,7 @@ package android.content { field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle"; field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list"; field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER"; field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
core/java/android/content/Intent.java +70 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.content; import android.content.pm.ApplicationInfo; import android.os.ResultReceiver; import android.provider.MediaStore; import android.util.ArraySet; Loading Loading @@ -3291,10 +3292,78 @@ public class Intent implements Parcelable, Cloneable { /** * An Intent describing the choices you would like shown with * {@link #ACTION_PICK_ACTIVITY}. * {@link #ACTION_PICK_ACTIVITY} or {@link #ACTION_CHOOSER}. */ public static final String EXTRA_INTENT = "android.intent.extra.INTENT"; /** * An Intent[] describing additional, alternate choices you would like shown with * {@link #ACTION_CHOOSER}. * * <p>An app may be capable of providing several different payload types to complete a * user's intended action. For example, an app invoking {@link #ACTION_SEND} to share photos * with another app may use EXTRA_ALTERNATE_INTENTS to have the chooser transparently offer * several different supported sending mechanisms for sharing, such as the actual "image/*" * photo data or a hosted link where the photos can be viewed.</p> * * <p>The intent present in {@link #EXTRA_INTENT} will be treated as the * first/primary/preferred intent in the set. Additional intents specified in * this extra are ordered; by default intents that appear earlier in the array will be * preferred over intents that appear later in the array as matches for the same * target component. To alter this preference, a calling app may also supply * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p> */ public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; /** * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection * from the chooser activity presented by {@link #ACTION_CHOOSER}. * * <p>An app preparing an action for another app to complete may wish to allow the user to * disambiguate between several options for completing the action based on the chosen target * or otherwise refine the action before it is invoked. * </p> * * <p>When sent, this IntentSender may be filled in with the following extras:</p> * <ul> * <li>{@link #EXTRA_INTENT} The first intent that matched the user's chosen target</li> * <li>{@link #EXTRA_ALTERNATE_INTENTS} Any additional intents that also matched the user's * chosen target beyond the first</li> * <li>{@link #EXTRA_RESULT_RECEIVER} A {@link ResultReceiver} that the refinement activity * should fill in and send once the disambiguation is complete</li> * </ul> */ public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; /** * A {@link ResultReceiver} used to return data back to the sender. * * <p>Used to complete an app-specific * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER refinement} for {@link #ACTION_CHOOSER}.</p> * * <p>If {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} is present in the intent * used to start a {@link #ACTION_CHOOSER} activity this extra will be * {@link #fillIn(Intent, int) filled in} to that {@link IntentSender} and sent * when the user selects a target component from the chooser. It is up to the recipient * to send a result to this ResultReceiver to signal that disambiguation is complete * and that the chooser should invoke the user's choice.</p> * * <p>The disambiguator should provide a Bundle to the ResultReceiver with an intent * assigned to the key {@link #EXTRA_INTENT}. This supplied intent will be used by the chooser * to match and fill in the final Intent or ChooserTarget before starting it. * The supplied intent must {@link #filterEquals(Intent) match} one of the intents from * {@link #EXTRA_INTENT} or {@link #EXTRA_ALTERNATE_INTENTS} passed to * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} to be accepted.</p> * * <p>The result code passed to the ResultReceiver should be * {@link android.app.Activity#RESULT_OK} if the refinement succeeded and the supplied intent's * target in the chooser should be started, or {@link android.app.Activity#RESULT_CANCELED} if * the chooser should finish without starting a target.</p> */ public static final String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER"; /** * A CharSequence dialog title to provide to the user when used with a * {@link #ACTION_CHOOSER}. Loading
core/java/com/android/internal/app/ChooserActivity.java +220 −13 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; Loading @@ -34,6 +35,7 @@ import android.os.IBinder; import android.os.Message; import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; Loading @@ -53,11 +55,13 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; private static final int QUERY_TARGET_LIMIT = 5; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; private IntentSender mRefinementIntentSender; private RefinementResultReceiver mRefinementResultReceiver; private ChooserTarget[] mCallerChooserTargets; Loading Loading @@ -113,6 +117,32 @@ public class ChooserActivity extends ResolverActivity { if (target != null) { modifyTargetIntent(target); } Parcelable[] targetsParcelable = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS); if (targetsParcelable != null) { final boolean offset = target == null; Intent[] additionalTargets = new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length]; for (int i = 0; i < targetsParcelable.length; i++) { if (!(targetsParcelable[i] instanceof Intent)) { Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: " + targetsParcelable[i]); finish(); super.onCreate(null); return; } final Intent additionalTarget = (Intent) targetsParcelable[i]; if (i == 0 && target == null) { target = additionalTarget; modifyTargetIntent(target); } else { additionalTargets[offset ? i - 1 : i] = additionalTarget; modifyTargetIntent(additionalTarget); } } setAdditionalTargets(additionalTargets); } mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS); CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE); int defaultTitleRes = 0; Loading @@ -125,7 +155,7 @@ public class ChooserActivity extends ResolverActivity { initialIntents = new Intent[pa.length]; for (int i=0; i<pa.length; i++) { if (!(pa[i] instanceof Intent)) { Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]); Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]); finish(); super.onCreate(null); return; Loading @@ -141,8 +171,7 @@ public class ChooserActivity extends ResolverActivity { final ChooserTarget[] targets = new ChooserTarget[pa.length]; for (int i = 0; i < pa.length; i++) { if (!(pa[i] instanceof ChooserTarget)) { Log.w("ChooserActivity", "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]); Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]); finish(); super.onCreate(null); return; Loading @@ -153,11 +182,22 @@ public class ChooserActivity extends ResolverActivity { } mChosenComponentSender = intent.getParcelableExtra( Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); mRefinementIntentSender = intent.getParcelableExtra( Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); } @Override protected void onDestroy() { super.onDestroy(); if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } } @Override public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { Intent result = defIntent; Loading Loading @@ -211,6 +251,37 @@ public class ChooserActivity extends ResolverActivity { } } @Override protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { if (mRefinementIntentSender != null) { final Intent fillIn = new Intent(); final List<Intent> sourceIntents = target.getAllSourceIntents(); if (!sourceIntents.isEmpty()) { fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0)); if (sourceIntents.size() > 1) { final Intent[] alts = new Intent[sourceIntents.size() - 1]; for (int i = 1, N = sourceIntents.size(); i < N; i++) { alts[i - 1] = sourceIntents.get(i); } fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts); } if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); } mRefinementResultReceiver = new RefinementResultReceiver(this, target, null); fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER, mRefinementResultReceiver); try { mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null); return false; } catch (SendIntentException e) { Log.e(TAG, "Refinement IntentSender failed to send", e); } } } return super.onTargetSelected(target, alwaysCheck); } void queryTargetServices(ChooserListAdapter adapter) { final PackageManager pm = getPackageManager(); int targetsToQuery = 0; Loading Loading @@ -258,8 +329,9 @@ public class ChooserActivity extends ResolverActivity { targetsToQuery++; } } if (targetsToQuery >= QUERY_TARGET_LIMIT) { if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_LIMIT); if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) { if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_SERVICE_LIMIT); break; } } Loading Loading @@ -303,6 +375,43 @@ public class ChooserActivity extends ResolverActivity { mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } if (selectedTarget == null) { Log.e(TAG, "Refinement result intent did not match any known targets; canceling"); } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) { Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget + " cannot match refined source intent " + matchingIntent); } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) { finish(); return; } onRefinementCanceled(); } void onRefinementCanceled() { if (mRefinementResultReceiver != null) { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } finish(); } boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) { final List<Intent> targetIntents = target.getAllSourceIntents(); for (int i = 0, N = targetIntents.size(); i < N; i++) { final Intent targetIntent = targetIntents.get(i); if (targetIntent.filterEquals(matchingIntent)) { return true; } } return false; } @Override ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { Loading @@ -313,17 +422,19 @@ public class ChooserActivity extends ResolverActivity { return adapter; } class ChooserTargetInfo implements TargetInfo { private final TargetInfo mSourceInfo; final class ChooserTargetInfo implements TargetInfo { private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; private final Drawable mDisplayIcon; private final Intent mFillInIntent; private final int mFillInFlags; public ChooserTargetInfo(ChooserTarget target) { this(null, target); } public ChooserTargetInfo(TargetInfo sourceInfo, ChooserTarget chooserTarget) { public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) { mSourceInfo = sourceInfo; mChooserTarget = chooserTarget; mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); Loading @@ -333,6 +444,18 @@ public class ChooserActivity extends ResolverActivity { } else { mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0); } mFillInIntent = null; mFillInFlags = 0; } private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) { mSourceInfo = other.mSourceInfo; mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; mDisplayIcon = other.mDisplayIcon; mFillInIntent = fillInIntent; mFillInFlags = flags; } @Override Loading @@ -358,22 +481,42 @@ public class ChooserActivity extends ResolverActivity { } private Intent getFillInIntent() { return mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent(); Intent result = mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent(); if (result == null) { Log.e(TAG, "ChooserTargetInfo#getFillInIntent: no fillIn intent available"); } else if (mFillInIntent != null) { result = new Intent(result); result.fillIn(mFillInIntent, mFillInFlags); } return result; } @Override public boolean start(Activity activity, Bundle options) { return mChooserTarget.sendIntent(activity, getFillInIntent()); final Intent intent = getFillInIntent(); if (intent == null) { return false; } return mChooserTarget.sendIntent(activity, intent); } @Override public boolean startAsCaller(Activity activity, Bundle options, int userId) { return mChooserTarget.sendIntentAsCaller(activity, getFillInIntent(), userId); final Intent intent = getFillInIntent(); if (intent == null) { return false; } return mChooserTarget.sendIntentAsCaller(activity, intent, userId); } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return mChooserTarget.sendIntentAsUser(activity, getFillInIntent(), user); final Intent intent = getFillInIntent(); if (intent == null) { return false; } return mChooserTarget.sendIntentAsUser(activity, intent, user); } @Override Loading @@ -395,6 +538,21 @@ public class ChooserActivity extends ResolverActivity { public Drawable getDisplayIcon() { return mDisplayIcon; } @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); } @Override public List<Intent> getAllSourceIntents() { final List<Intent> results = new ArrayList<>(); if (mSourceInfo != null) { // We only queried the service for the first one in our sourceinfo. results.add(mSourceInfo.getAllSourceIntents().get(0)); } return results; } } public class ChooserListAdapter extends ResolveListAdapter { Loading Loading @@ -542,4 +700,53 @@ public class ChooserActivity extends ResolverActivity { connection = c; } } static class RefinementResultReceiver extends ResultReceiver { private ChooserActivity mChooserActivity; private TargetInfo mSelectedTarget; public RefinementResultReceiver(ChooserActivity host, TargetInfo target, Handler handler) { super(handler); mChooserActivity = host; mSelectedTarget = target; } @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (mChooserActivity == null) { Log.e(TAG, "Destroyed RefinementResultReceiver received a result"); return; } if (resultData == null) { Log.e(TAG, "RefinementResultReceiver received null resultData"); return; } switch (resultCode) { case RESULT_CANCELED: mChooserActivity.onRefinementCanceled(); break; case RESULT_OK: Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT); if (intentParcelable instanceof Intent) { mChooserActivity.onRefinementResult(mSelectedTarget, (Intent) intentParcelable); } else { Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent" + " in resultData with key Intent.EXTRA_INTENT"); } break; default: Log.w(TAG, "Unknown result code " + resultCode + " sent to RefinementResultReceiver"); break; } } public void destroy() { mChooserActivity = null; mSelectedTarget = null; } } }
core/java/com/android/internal/app/ResolverActivity.java +226 −63 File changed.Preview size limit exceeded, changes collapsed. Show changes