Merge "Add alternate intents and refinement callbacks to ChooserActivity" into mnc-dev

This commit is contained in:
Adam Powell
2015-05-06 21:08:37 +00:00
committed by Android (Google) Code Review
6 changed files with 524 additions and 78 deletions

View File

@@ -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_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 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 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_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_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"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
@@ -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_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_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_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_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 = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
@@ -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_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_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_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_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 = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";

View File

@@ -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_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 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 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_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_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"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
@@ -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_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_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_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_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 = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
@@ -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_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_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_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_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 = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";

View File

@@ -17,6 +17,7 @@
package android.content; package android.content;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.os.ResultReceiver;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.util.ArraySet; import android.util.ArraySet;
@@ -3291,10 +3292,78 @@ public class Intent implements Parcelable, Cloneable {
/** /**
* An Intent describing the choices you would like shown with * 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"; 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 * A CharSequence dialog title to provide to the user when used with a
* {@link #ACTION_CHOOSER}. * {@link #ACTION_CHOOSER}.

View File

@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -34,6 +35,7 @@ import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle; import android.os.UserHandle;
import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService; import android.service.chooser.ChooserTargetService;
@@ -53,11 +55,13 @@ public class ChooserActivity extends ResolverActivity {
private static final boolean DEBUG = false; 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 static final int WATCHDOG_TIMEOUT_MILLIS = 5000;
private Bundle mReplacementExtras; private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender; private IntentSender mChosenComponentSender;
private IntentSender mRefinementIntentSender;
private RefinementResultReceiver mRefinementResultReceiver;
private ChooserTarget[] mCallerChooserTargets; private ChooserTarget[] mCallerChooserTargets;
@@ -113,6 +117,32 @@ public class ChooserActivity extends ResolverActivity {
if (target != null) { if (target != null) {
modifyTargetIntent(target); 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); mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE); CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
int defaultTitleRes = 0; int defaultTitleRes = 0;
@@ -125,7 +155,7 @@ public class ChooserActivity extends ResolverActivity {
initialIntents = new Intent[pa.length]; initialIntents = new Intent[pa.length];
for (int i=0; i<pa.length; i++) { for (int i=0; i<pa.length; i++) {
if (!(pa[i] instanceof Intent)) { 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(); finish();
super.onCreate(null); super.onCreate(null);
return; return;
@@ -141,8 +171,7 @@ public class ChooserActivity extends ResolverActivity {
final ChooserTarget[] targets = new ChooserTarget[pa.length]; final ChooserTarget[] targets = new ChooserTarget[pa.length];
for (int i = 0; i < pa.length; i++) { for (int i = 0; i < pa.length; i++) {
if (!(pa[i] instanceof ChooserTarget)) { if (!(pa[i] instanceof ChooserTarget)) {
Log.w("ChooserActivity", "Chooser target #" + i + " is not a ChooserTarget: " + Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]);
pa[i]);
finish(); finish();
super.onCreate(null); super.onCreate(null);
return; return;
@@ -153,11 +182,22 @@ public class ChooserActivity extends ResolverActivity {
} }
mChosenComponentSender = intent.getParcelableExtra( mChosenComponentSender = intent.getParcelableExtra(
Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
mRefinementIntentSender = intent.getParcelableExtra(
Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
setSafeForwardingMode(true); setSafeForwardingMode(true);
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false); null, false);
} }
@Override
protected void onDestroy() {
super.onDestroy();
if (mRefinementResultReceiver != null) {
mRefinementResultReceiver.destroy();
mRefinementResultReceiver = null;
}
}
@Override @Override
public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Intent result = defIntent; Intent result = defIntent;
@@ -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) { void queryTargetServices(ChooserListAdapter adapter) {
final PackageManager pm = getPackageManager(); final PackageManager pm = getPackageManager();
int targetsToQuery = 0; int targetsToQuery = 0;
@@ -258,8 +329,9 @@ public class ChooserActivity extends ResolverActivity {
targetsToQuery++; targetsToQuery++;
} }
} }
if (targetsToQuery >= QUERY_TARGET_LIMIT) { if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_LIMIT); if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
+ QUERY_TARGET_SERVICE_LIMIT);
break; break;
} }
} }
@@ -303,6 +375,43 @@ public class ChooserActivity extends ResolverActivity {
mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); 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 @Override
ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, ResolveListAdapter createAdapter(Context context, Intent[] initialIntents,
List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
@@ -313,17 +422,19 @@ public class ChooserActivity extends ResolverActivity {
return adapter; return adapter;
} }
class ChooserTargetInfo implements TargetInfo { final class ChooserTargetInfo implements TargetInfo {
private final TargetInfo mSourceInfo; private final DisplayResolveInfo mSourceInfo;
private final ResolveInfo mBackupResolveInfo; private final ResolveInfo mBackupResolveInfo;
private final ChooserTarget mChooserTarget; private final ChooserTarget mChooserTarget;
private final Drawable mDisplayIcon; private final Drawable mDisplayIcon;
private final Intent mFillInIntent;
private final int mFillInFlags;
public ChooserTargetInfo(ChooserTarget target) { public ChooserTargetInfo(ChooserTarget target) {
this(null, target); this(null, target);
} }
public ChooserTargetInfo(TargetInfo sourceInfo, ChooserTarget chooserTarget) { public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) {
mSourceInfo = sourceInfo; mSourceInfo = sourceInfo;
mChooserTarget = chooserTarget; mChooserTarget = chooserTarget;
mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon());
@@ -333,6 +444,18 @@ public class ChooserActivity extends ResolverActivity {
} else { } else {
mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0); 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 @Override
@@ -358,22 +481,42 @@ public class ChooserActivity extends ResolverActivity {
} }
private Intent getFillInIntent() { 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 @Override
public boolean start(Activity activity, Bundle options) { 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 @Override
public boolean startAsCaller(Activity activity, Bundle options, int userId) { 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 @Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { 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 @Override
@@ -395,6 +538,21 @@ public class ChooserActivity extends ResolverActivity {
public Drawable getDisplayIcon() { public Drawable getDisplayIcon() {
return mDisplayIcon; 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 { public class ChooserListAdapter extends ResolveListAdapter {
@@ -542,4 +700,53 @@ public class ChooserActivity extends ResolverActivity {
connection = c; 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;
}
}
} }

View File

@@ -102,7 +102,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
private int mLastSelected = AbsListView.INVALID_POSITION; private int mLastSelected = AbsListView.INVALID_POSITION;
private boolean mResolvingHome = false; private boolean mResolvingHome = false;
private int mProfileSwitchMessageId = -1; private int mProfileSwitchMessageId = -1;
private Intent mIntent; private final ArrayList<Intent> mIntents = new ArrayList<>();
private UsageStatsManager mUsm; private UsageStatsManager mUsm;
private Map<String, UsageStats> mStats; private Map<String, UsageStats> mStats;
@@ -229,7 +229,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
mIconDpi = am.getLauncherLargeIconDensity(); mIconDpi = am.getLauncherLargeIconDensity();
mIntent = new Intent(intent); mIntents.add(0, new Intent(intent));
mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption); mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption);
final int layoutId; final int layoutId;
@@ -250,7 +250,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return; return;
} }
int count = mAdapter.mList.size(); int count = mAdapter.mDisplayList.size();
if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
setContentView(layoutId); setContentView(layoutId);
mAdapterView = (AbsListView) findViewById(R.id.resolver_list); mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
@@ -376,8 +376,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
} }
protected final void setAdditionalTargets(Intent[] intents) {
if (intents != null) {
for (Intent intent : intents) {
mIntents.add(intent);
}
}
}
public Intent getTargetIntent() { public Intent getTargetIntent() {
return mIntent; return mIntents.isEmpty() ? null : mIntents.get(0);
} }
private String getReferrerPackageName() { private String getReferrerPackageName() {
@@ -630,8 +638,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
TargetInfo target = mAdapter.targetInfoForPosition(which, filtered); TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
onTargetSelected(target, always); if (onTargetSelected(target, always)) {
finish(); finish();
}
} }
/** /**
@@ -641,7 +650,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return defIntent; return defIntent;
} }
protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) { protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
final ResolveInfo ri = target.getResolveInfo(); final ResolveInfo ri = target.getResolveInfo();
final Intent intent = target != null ? target.getResolvedIntent() : null; final Intent intent = target != null ? target.getResolvedIntent() : null;
@@ -728,7 +737,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
ComponentName[] set = new ComponentName[N]; ComponentName[] set = new ComponentName[N];
int bestMatch = 0; int bestMatch = 0;
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
ResolveInfo r = mAdapter.mOrigResolveList.get(i); ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
set[i] = new ComponentName(r.activityInfo.packageName, set[i] = new ComponentName(r.activityInfo.packageName,
r.activityInfo.name); r.activityInfo.name);
if (r.match > bestMatch) bestMatch = r.match; if (r.match > bestMatch) bestMatch = r.match;
@@ -774,6 +783,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (target != null) { if (target != null) {
safelyStartActivity(target); safelyStartActivity(target);
} }
return true;
} }
void safelyStartActivity(TargetInfo cti) { void safelyStartActivity(TargetInfo cti) {
@@ -837,15 +847,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
private Drawable mDisplayIcon; private Drawable mDisplayIcon;
private final CharSequence mExtendedInfo; private final CharSequence mExtendedInfo;
private final Intent mResolvedIntent; private final Intent mResolvedIntent;
private final List<Intent> mSourceIntents = new ArrayList<>();
DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel, DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
CharSequence pInfo, Intent pOrigIntent) { CharSequence pInfo, Intent pOrigIntent) {
mSourceIntents.add(originalIntent);
mResolveInfo = pri; mResolveInfo = pri;
mDisplayLabel = pLabel; mDisplayLabel = pLabel;
mExtendedInfo = pInfo; mExtendedInfo = pInfo;
final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent : final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent :
getReplacementIntent(pri.activityInfo, mIntent)); getReplacementIntent(pri.activityInfo, getTargetIntent()));
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
| Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
final ActivityInfo ai = mResolveInfo.activityInfo; final ActivityInfo ai = mResolveInfo.activityInfo;
@@ -854,6 +866,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
mResolvedIntent = intent; mResolvedIntent = intent;
} }
private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) {
mSourceIntents.addAll(other.getAllSourceIntents());
mResolveInfo = other.mResolveInfo;
mDisplayLabel = other.mDisplayLabel;
mDisplayIcon = other.mDisplayIcon;
mExtendedInfo = other.mExtendedInfo;
mResolvedIntent = new Intent(other.mResolvedIntent);
mResolvedIntent.fillIn(fillInIntent, flags);
}
public ResolveInfo getResolveInfo() { public ResolveInfo getResolveInfo() {
return mResolveInfo; return mResolveInfo;
} }
@@ -866,6 +888,20 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return mDisplayIcon; return mDisplayIcon;
} }
@Override
public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
return new DisplayResolveInfo(this, fillInIntent, flags);
}
@Override
public List<Intent> getAllSourceIntents() {
return mSourceIntents;
}
public void addAlternateSourceIntent(Intent alt) {
mSourceIntents.add(alt);
}
public void setDisplayIcon(Drawable icon) { public void setDisplayIcon(Drawable icon) {
mDisplayIcon = icon; mDisplayIcon = icon;
} }
@@ -986,6 +1022,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
* @return The drawable that should be used to represent this target * @return The drawable that should be used to represent this target
*/ */
public Drawable getDisplayIcon(); public Drawable getDisplayIcon();
/**
* Clone this target with the given fill-in information.
*/
public TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
/**
* @return the list of supported source intents deduped against this single target
*/
public List<Intent> getAllSourceIntents();
} }
class ResolveListAdapter extends BaseAdapter { class ResolveListAdapter extends BaseAdapter {
@@ -998,8 +1044,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
protected final LayoutInflater mInflater; protected final LayoutInflater mInflater;
List<DisplayResolveInfo> mList; List<DisplayResolveInfo> mDisplayList;
List<ResolveInfo> mOrigResolveList; List<ResolvedComponentInfo> mOrigResolveList;
private int mLastChosenPosition = -1; private int mLastChosenPosition = -1;
private boolean mFilterLastUsed; private boolean mFilterLastUsed;
@@ -1010,7 +1056,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
mBaseResolveList = rList; mBaseResolveList = rList;
mLaunchedFromUid = launchedFromUid; mLaunchedFromUid = launchedFromUid;
mInflater = LayoutInflater.from(context); mInflater = LayoutInflater.from(context);
mList = new ArrayList<>(); mDisplayList = new ArrayList<>();
mFilterLastUsed = filterLastUsed; mFilterLastUsed = filterLastUsed;
rebuildList(); rebuildList();
} }
@@ -1027,7 +1073,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
public DisplayResolveInfo getFilteredItem() { public DisplayResolveInfo getFilteredItem() {
if (mFilterLastUsed && mLastChosenPosition >= 0) { if (mFilterLastUsed && mLastChosenPosition >= 0) {
// Not using getItem since it offsets to dodge this position for the list // Not using getItem since it offsets to dodge this position for the list
return mList.get(mLastChosenPosition); return mDisplayList.get(mLastChosenPosition);
} }
return null; return null;
} }
@@ -1048,11 +1094,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
private void rebuildList() { private void rebuildList() {
List<ResolveInfo> currentResolveList; List<ResolvedComponentInfo> currentResolveList = null;
try { try {
final Intent primaryIntent = getTargetIntent();
mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity( mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()), primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
PackageManager.MATCH_DEFAULT_ONLY); PackageManager.MATCH_DEFAULT_ONLY);
} catch (RemoteException re) { } catch (RemoteException re) {
Log.d(TAG, "Error calling setLastChosenActivity\n" + re); Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
@@ -1060,15 +1107,27 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// Clear the value of mOtherProfile from previous call. // Clear the value of mOtherProfile from previous call.
mOtherProfile = null; mOtherProfile = null;
mList.clear(); mDisplayList.clear();
if (mBaseResolveList != null) { if (mBaseResolveList != null) {
currentResolveList = mOrigResolveList = mBaseResolveList; currentResolveList = mOrigResolveList = new ArrayList<>();
addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
} else { } else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent, final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
PackageManager.MATCH_DEFAULT_ONLY final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
| (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0) for (int i = 0, N = mIntents.size(); i < N; i++) {
| (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0) final Intent intent = mIntents.get(i);
); final List<ResolveInfo> infos = mPm.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY
| (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
if (infos != null) {
if (currentResolveList == null) {
currentResolveList = mOrigResolveList = new ArrayList<>();
}
addResolveListDedupe(currentResolveList, intent, infos);
}
}
// Filter out any activities that the launched uid does not // Filter out any activities that the launched uid does not
// have permission for. We don't do this when we have an explicit // have permission for. We don't do this when we have an explicit
// list of resolved activities, because that only happens when // list of resolved activities, because that only happens when
@@ -1076,14 +1135,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// they gave us. // they gave us.
if (currentResolveList != null) { if (currentResolveList != null) {
for (int i=currentResolveList.size()-1; i >= 0; i--) { for (int i=currentResolveList.size()-1; i >= 0; i--) {
ActivityInfo ai = currentResolveList.get(i).activityInfo; ActivityInfo ai = currentResolveList.get(i)
.getResolveInfoAt(0).activityInfo;
int granted = ActivityManager.checkComponentPermission( int granted = ActivityManager.checkComponentPermission(
ai.permission, mLaunchedFromUid, ai.permission, mLaunchedFromUid,
ai.applicationInfo.uid, ai.exported); ai.applicationInfo.uid, ai.exported);
if (granted != PackageManager.PERMISSION_GRANTED) { if (granted != PackageManager.PERMISSION_GRANTED) {
// Access not allowed! // Access not allowed!
if (mOrigResolveList == currentResolveList) { if (mOrigResolveList == currentResolveList) {
mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList); mOrigResolveList = new ArrayList<>(mOrigResolveList);
} }
currentResolveList.remove(i); currentResolveList.remove(i);
} }
@@ -1094,9 +1154,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) { if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
// Only display the first matches that are either of equal // Only display the first matches that are either of equal
// priority or have asked to be default options. // priority or have asked to be default options.
ResolveInfo r0 = currentResolveList.get(0); ResolvedComponentInfo rci0 = currentResolveList.get(0);
ResolveInfo r0 = rci0.getResolveInfoAt(0);
for (int i=1; i<N; i++) { for (int i=1; i<N; i++) {
ResolveInfo ri = currentResolveList.get(i); ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
if (DEBUG) Log.v( if (DEBUG) Log.v(
TAG, TAG,
r0.activityInfo.name + "=" + r0.activityInfo.name + "=" +
@@ -1107,7 +1168,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
r0.isDefault != ri.isDefault) { r0.isDefault != ri.isDefault) {
while (i < N) { while (i < N) {
if (mOrigResolveList == currentResolveList) { if (mOrigResolveList == currentResolveList) {
mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList); mOrigResolveList = new ArrayList<>(mOrigResolveList);
} }
currentResolveList.remove(i); currentResolveList.remove(i);
N--; N--;
@@ -1115,9 +1176,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
} }
if (N > 1) { if (N > 1) {
Comparator<ResolveInfo> rComparator = Collections.sort(currentResolveList,
new ResolverComparator(ResolverActivity.this, mIntent); new ResolverComparator(ResolverActivity.this, getTargetIntent()));
Collections.sort(currentResolveList, rComparator);
} }
// First put the initial items at the top. // First put the initial items at the top.
if (mInitialIntents != null) { if (mInitialIntents != null) {
@@ -1146,14 +1206,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
ri.nonLocalizedLabel = li.getNonLocalizedLabel(); ri.nonLocalizedLabel = li.getNonLocalizedLabel();
ri.icon = li.getIconResource(); ri.icon = li.getIconResource();
} }
addResolveInfo(new DisplayResolveInfo(ri, addResolveInfo(new DisplayResolveInfo(ii, ri,
ri.loadLabel(getPackageManager()), null, ii)); ri.loadLabel(getPackageManager()), null, ii));
} }
} }
// Check for applications with same name and use application name or // Check for applications with same name and use application name or
// package name if necessary // package name if necessary
r0 = currentResolveList.get(0); rci0 = currentResolveList.get(0);
r0 = rci0.getResolveInfoAt(0);
int start = 0; int start = 0;
CharSequence r0Label = r0.loadLabel(mPm); CharSequence r0Label = r0.loadLabel(mPm);
mHasExtendedInfo = false; mHasExtendedInfo = false;
@@ -1161,7 +1222,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (r0Label == null) { if (r0Label == null) {
r0Label = r0.activityInfo.packageName; r0Label = r0.activityInfo.packageName;
} }
ResolveInfo ri = currentResolveList.get(i); ResolvedComponentInfo rci = currentResolveList.get(i);
ResolveInfo ri = rci.getResolveInfoAt(0);
CharSequence riLabel = ri.loadLabel(mPm); CharSequence riLabel = ri.loadLabel(mPm);
if (riLabel == null) { if (riLabel == null) {
riLabel = ri.activityInfo.packageName; riLabel = ri.activityInfo.packageName;
@@ -1169,13 +1231,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (riLabel.equals(r0Label)) { if (riLabel.equals(r0Label)) {
continue; continue;
} }
processGroup(currentResolveList, start, (i-1), r0, r0Label); processGroup(currentResolveList, start, (i-1), rci0, r0Label);
rci0 = rci;
r0 = ri; r0 = ri;
r0Label = riLabel; r0Label = riLabel;
start = i; start = i;
} }
// Process last group // Process last group
processGroup(currentResolveList, start, (N-1), r0, r0Label); processGroup(currentResolveList, start, (N-1), rci0, r0Label);
} }
// Layout doesn't handle both profile button and last chosen // Layout doesn't handle both profile button and last chosen
@@ -1188,6 +1251,36 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
onListRebuilt(); onListRebuilt();
} }
private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent,
List<ResolveInfo> from) {
final int fromCount = from.size();
final int intoCount = into.size();
for (int i = 0; i < fromCount; i++) {
final ResolveInfo newInfo = from.get(i);
boolean found = false;
// Only loop to the end of into as it was before we started; no dupes in from.
for (int j = 0; j < intoCount; j++) {
final ResolvedComponentInfo rci = into.get(i);
if (isSameResolvedComponent(newInfo, rci)) {
found = true;
rci.add(intent, newInfo);
break;
}
}
if (!found) {
into.add(new ResolvedComponentInfo(new ComponentName(
newInfo.activityInfo.packageName, newInfo.activityInfo.name),
intent, newInfo));
}
}
}
private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) {
final ActivityInfo ai = a.activityInfo;
return ai.packageName.equals(b.name.getPackageName())
&& ai.name.equals(b.name.getClassName());
}
public void onListRebuilt() { public void onListRebuilt() {
// This space for rent // This space for rent
} }
@@ -1196,18 +1289,18 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
return mFilterLastUsed; return mFilterLastUsed;
} }
private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro, private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
CharSequence roLabel) { ResolvedComponentInfo ro, CharSequence roLabel) {
// Process labels from start to i // Process labels from start to i
int num = end - start+1; int num = end - start+1;
if (num == 1) { if (num == 1) {
// No duplicate labels. Use label for entry at start // No duplicate labels. Use label for entry at start
addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null)); addResolveInfoWithAlternates(ro, null, roLabel);
updateLastChosenPosition(ro);
} else { } else {
mHasExtendedInfo = true; mHasExtendedInfo = true;
boolean usePkg = false; boolean usePkg = false;
CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm); CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo
.loadLabel(mPm);
if (startApp == null) { if (startApp == null) {
usePkg = true; usePkg = true;
} }
@@ -1217,7 +1310,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
new HashSet<CharSequence>(); new HashSet<CharSequence>();
duplicates.add(startApp); duplicates.add(startApp);
for (int j = start+1; j <= end ; j++) { for (int j = start+1; j <= end ; j++) {
ResolveInfo jRi = rList.get(j); ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm); CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
if ( (jApp == null) || (duplicates.contains(jApp))) { if ( (jApp == null) || (duplicates.contains(jApp))) {
usePkg = true; usePkg = true;
@@ -1230,26 +1323,46 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
duplicates.clear(); duplicates.clear();
} }
for (int k = start; k <= end; k++) { for (int k = start; k <= end; k++) {
ResolveInfo add = rList.get(k); final ResolvedComponentInfo rci = rList.get(k);
final ResolveInfo add = rci.getResolveInfoAt(0);
final CharSequence extraInfo;
if (usePkg) { if (usePkg) {
// Use application name for all entries from start to end-1
addResolveInfo(new DisplayResolveInfo(add, roLabel,
add.activityInfo.packageName, null));
} else {
// Use package name for all entries from start to end-1 // Use package name for all entries from start to end-1
addResolveInfo(new DisplayResolveInfo(add, roLabel, extraInfo = add.activityInfo.packageName;
add.activityInfo.applicationInfo.loadLabel(mPm), null)); } else {
// Use application name for all entries from start to end-1
extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
} }
updateLastChosenPosition(add); addResolveInfoWithAlternates(rci, extraInfo, roLabel);
} }
} }
} }
private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
CharSequence extraInfo, CharSequence roLabel) {
final int count = rci.getCount();
final Intent intent = rci.getIntentAt(0);
final ResolveInfo add = rci.getResolveInfoAt(0);
final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
extraInfo, replaceIntent);
addResolveInfo(dri);
if (replaceIntent == intent) {
// Only add alternates if we didn't get a specific replacement from
// the caller. If we have one it trumps potential alternates.
for (int i = 1, N = count; i < N; i++) {
final Intent altIntent = rci.getIntentAt(i);
dri.addAlternateSourceIntent(altIntent);
}
}
updateLastChosenPosition(add);
}
private void updateLastChosenPosition(ResolveInfo info) { private void updateLastChosenPosition(ResolveInfo info) {
if (mLastChosen != null if (mLastChosen != null
&& mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName) && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
&& mLastChosen.activityInfo.name.equals(info.activityInfo.name)) { && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
mLastChosenPosition = mList.size() - 1; mLastChosenPosition = mDisplayList.size() - 1;
} }
} }
@@ -1259,20 +1372,21 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// The first one we see gets special treatment. // The first one we see gets special treatment.
mOtherProfile = dri; mOtherProfile = dri;
} else { } else {
mList.add(dri); mDisplayList.add(dri);
} }
} }
public ResolveInfo resolveInfoForPosition(int position, boolean filtered) { public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
return (filtered ? getItem(position) : mList.get(position)).getResolveInfo(); return (filtered ? getItem(position) : mDisplayList.get(position))
.getResolveInfo();
} }
public TargetInfo targetInfoForPosition(int position, boolean filtered) { public TargetInfo targetInfoForPosition(int position, boolean filtered) {
return filtered ? getItem(position) : mList.get(position); return filtered ? getItem(position) : mDisplayList.get(position);
} }
public int getCount() { public int getCount() {
int result = mList.size(); int result = mDisplayList.size();
if (mFilterLastUsed && mLastChosenPosition >= 0) { if (mFilterLastUsed && mLastChosenPosition >= 0) {
result--; result--;
} }
@@ -1283,7 +1397,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) { if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
position++; position++;
} }
return mList.get(position); return mDisplayList.get(position);
} }
public long getItemId(int position) { public long getItemId(int position) {
@@ -1295,8 +1409,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
public boolean hasResolvedTarget(ResolveInfo info) { public boolean hasResolvedTarget(ResolveInfo info) {
for (int i = 0, N = mList.size(); i < N; i++) { for (int i = 0, N = mDisplayList.size(); i < N; i++) {
if (info.equals(mList.get(i).getResolveInfo())) { if (info.equals(mDisplayList.get(i).getResolveInfo())) {
return true; return true;
} }
} }
@@ -1304,11 +1418,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
protected int getDisplayResolveInfoCount() { protected int getDisplayResolveInfoCount() {
return mList.size(); return mDisplayList.size();
} }
protected DisplayResolveInfo getDisplayResolveInfo(int index) { protected DisplayResolveInfo getDisplayResolveInfo(int index) {
return mList.get(index); // Used to query services. We only query services for primary targets, not alternates.
return mDisplayList.get(index);
} }
public final View getView(int position, View convertView, ViewGroup parent) { public final View getView(int position, View convertView, ViewGroup parent) {
@@ -1349,6 +1464,52 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
} }
static final class ResolvedComponentInfo {
public final ComponentName name;
private final List<Intent> mIntents = new ArrayList<>();
private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
this.name = name;
add(intent, info);
}
public void add(Intent intent, ResolveInfo info) {
mIntents.add(intent);
mResolveInfos.add(info);
}
public int getCount() {
return mIntents.size();
}
public Intent getIntentAt(int index) {
return index >= 0 ? mIntents.get(index) : null;
}
public ResolveInfo getResolveInfoAt(int index) {
return index >= 0 ? mResolveInfos.get(index) : null;
}
public int findIntent(Intent intent) {
for (int i = 0, N = mIntents.size(); i < N; i++) {
if (intent.equals(mIntents.get(i))) {
return i;
}
}
return -1;
}
public int findResolveInfo(ResolveInfo info) {
for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
if (info.equals(mResolveInfos.get(i))) {
return i;
}
}
return -1;
}
}
static class ViewHolder { static class ViewHolder {
public TextView text; public TextView text;
public TextView text2; public TextView text2;
@@ -1435,7 +1596,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
&& match <= IntentFilter.MATCH_CATEGORY_PATH; && match <= IntentFilter.MATCH_CATEGORY_PATH;
} }
class ResolverComparator implements Comparator<ResolveInfo> { class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private final Collator mCollator; private final Collator mCollator;
private final boolean mHttp; private final boolean mHttp;
@@ -1446,7 +1607,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} }
@Override @Override
public int compare(ResolveInfo lhs, ResolveInfo rhs) { public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) {
final ResolveInfo lhs = lhsp.getResolveInfoAt(0);
final ResolveInfo rhs = rhsp.getResolveInfoAt(0);
// We want to put the one targeted to another user at the end of the dialog. // We want to put the one targeted to another user at the end of the dialog.
if (lhs.targetUserId != UserHandle.USER_CURRENT) { if (lhs.targetUserId != UserHandle.USER_CURRENT) {
return 1; return 1;
@@ -1487,7 +1651,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
if (stats != null) { if (stats != null) {
return stats.getTotalTimeInForeground(); return stats.getTotalTimeInForeground();
} }
} }
return 0; return 0;
} }

View File

@@ -93,7 +93,7 @@ public class UsbResolverActivity extends ResolverActivity {
} }
@Override @Override
protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) { protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
final ResolveInfo ri = target.getResolveInfo(); final ResolveInfo ri = target.getResolveInfo();
try { try {
IBinder b = ServiceManager.getService(USB_SERVICE); IBinder b = ServiceManager.getService(USB_SERVICE);
@@ -129,5 +129,6 @@ public class UsbResolverActivity extends ResolverActivity {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "onIntentSelected failed", e); Log.e(TAG, "onIntentSelected failed", e);
} }
return true;
} }
} }