Add alternate intents and refinement callbacks to ChooserActivity
Allow a calling app to supply an array of additional Intents to the system ChooserActivity. The chooser will present a merged list of targets that can handle any of the Intents supplied, including both the standard EXTRA_INTENT as well as any of the intents supplied in EXTRA_ALTERNATE_INTENTS. These are treated as ordered; EXTRA_INTENT is considered the first/primary Intent and EXTRA_ALTERNATE_INTENTS are sorted most important first. Targets are queried for all supplied Intents. If the same component is returned for more than one Intent, the target is associated with the most important Intent that matched. This allows calling apps to supply several different payloads for an action depending on what the intended targets are able to support. For example, an app performing ACTION_SEND may supply image/jpeg data to compatible targets or a hosted web link to targets that only support text/plain. The user will have the opportunity to pick from a single merged list of choices using the best available payload, and will not be bothered with the implementation details of how the payload will be delivered to the recipient. If the calling app wishes to provide further disambiguation or refinement after the user makes a choice, for example to let the user choose which of the source intents to send from the primary or alternates, show a progress dialog as a full-resolution version of a photo is downloaded from the server before being sent along or while reticulating splines, the caller can supply an IntentSender to ACTION_CHOOSER including the extra EXTRA_REFINEMENT_INTENT_SENDER. This should be the IntentSender obtained from a PendingIntent pointing at an activity to launch to perform the refinement. The refinement activity should report that it is finished by obtaining the ResultReceiver from EXTRA_RESULT_RECEIVER. Available intents to send to the selected target will be contained in EXTRA_INTENT and EXTRA_ALTERNATE_INTENTS. To complete the refinement and send the result along to the chosen target, the refinement activity should select one of the supplied intents and send it to the ResultReceiver in a Bundle with the key EXTRA_INTENT and the result code RESULT_OK. To cancel the refinement, and let the user select another choice, send RESULT_CANCEL. While refinement activities cannot modify the filterEquals-affecting fields of the Intent they return, they may modify extras to provide additional or altered details to the final recipient. These extras will be filled into the Intent sent to the final target. Change-Id: I7ad4739eadd1a0e307675847ccf47ea948918a3a
This commit is contained in:
@ -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";
|
||||
@ -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";
|
||||
@ -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";
|
||||
|
@ -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";
|
||||
@ -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";
|
||||
@ -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";
|
||||
|
@ -17,6 +17,7 @@
|
||||
package android.content;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.ResultReceiver;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.ArraySet;
|
||||
|
||||
@ -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}.
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
@ -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());
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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 {
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
private int mLastSelected = AbsListView.INVALID_POSITION;
|
||||
private boolean mResolvingHome = false;
|
||||
private int mProfileSwitchMessageId = -1;
|
||||
private Intent mIntent;
|
||||
private final ArrayList<Intent> mIntents = new ArrayList<>();
|
||||
|
||||
private UsageStatsManager mUsm;
|
||||
private Map<String, UsageStats> mStats;
|
||||
@ -229,7 +229,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
|
||||
mIconDpi = am.getLauncherLargeIconDensity();
|
||||
|
||||
mIntent = new Intent(intent);
|
||||
mIntents.add(0, new Intent(intent));
|
||||
mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption);
|
||||
|
||||
final int layoutId;
|
||||
@ -250,7 +250,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
return;
|
||||
}
|
||||
|
||||
int count = mAdapter.mList.size();
|
||||
int count = mAdapter.mDisplayList.size();
|
||||
if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
|
||||
setContentView(layoutId);
|
||||
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() {
|
||||
return mIntent;
|
||||
return mIntents.isEmpty() ? null : mIntents.get(0);
|
||||
}
|
||||
|
||||
private String getReferrerPackageName() {
|
||||
@ -630,8 +638,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
}
|
||||
|
||||
TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
|
||||
onTargetSelected(target, always);
|
||||
finish();
|
||||
if (onTargetSelected(target, always)) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -641,7 +650,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
return defIntent;
|
||||
}
|
||||
|
||||
protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) {
|
||||
protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
|
||||
final ResolveInfo ri = target.getResolveInfo();
|
||||
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];
|
||||
int bestMatch = 0;
|
||||
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,
|
||||
r.activityInfo.name);
|
||||
if (r.match > bestMatch) bestMatch = r.match;
|
||||
@ -774,6 +783,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
if (target != null) {
|
||||
safelyStartActivity(target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void safelyStartActivity(TargetInfo cti) {
|
||||
@ -837,15 +847,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
private Drawable mDisplayIcon;
|
||||
private final CharSequence mExtendedInfo;
|
||||
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) {
|
||||
mSourceIntents.add(originalIntent);
|
||||
mResolveInfo = pri;
|
||||
mDisplayLabel = pLabel;
|
||||
mExtendedInfo = pInfo;
|
||||
|
||||
final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent :
|
||||
getReplacementIntent(pri.activityInfo, mIntent));
|
||||
getReplacementIntent(pri.activityInfo, getTargetIntent()));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
|
||||
| Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
|
||||
final ActivityInfo ai = mResolveInfo.activityInfo;
|
||||
@ -854,6 +866,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
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() {
|
||||
return mResolveInfo;
|
||||
}
|
||||
@ -866,6 +888,20 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
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 {
|
||||
@ -998,8 +1044,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
|
||||
protected final LayoutInflater mInflater;
|
||||
|
||||
List<DisplayResolveInfo> mList;
|
||||
List<ResolveInfo> mOrigResolveList;
|
||||
List<DisplayResolveInfo> mDisplayList;
|
||||
List<ResolvedComponentInfo> mOrigResolveList;
|
||||
|
||||
private int mLastChosenPosition = -1;
|
||||
private boolean mFilterLastUsed;
|
||||
@ -1010,7 +1056,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
mBaseResolveList = rList;
|
||||
mLaunchedFromUid = launchedFromUid;
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mList = new ArrayList<>();
|
||||
mDisplayList = new ArrayList<>();
|
||||
mFilterLastUsed = filterLastUsed;
|
||||
rebuildList();
|
||||
}
|
||||
@ -1027,7 +1073,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
public DisplayResolveInfo getFilteredItem() {
|
||||
if (mFilterLastUsed && mLastChosenPosition >= 0) {
|
||||
// Not using getItem since it offsets to dodge this position for the list
|
||||
return mList.get(mLastChosenPosition);
|
||||
return mDisplayList.get(mLastChosenPosition);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -1048,11 +1094,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
}
|
||||
|
||||
private void rebuildList() {
|
||||
List<ResolveInfo> currentResolveList;
|
||||
List<ResolvedComponentInfo> currentResolveList = null;
|
||||
|
||||
try {
|
||||
final Intent primaryIntent = getTargetIntent();
|
||||
mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
|
||||
mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
|
||||
primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
|
||||
PackageManager.MATCH_DEFAULT_ONLY);
|
||||
} catch (RemoteException 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.
|
||||
mOtherProfile = null;
|
||||
mList.clear();
|
||||
mDisplayList.clear();
|
||||
if (mBaseResolveList != null) {
|
||||
currentResolveList = mOrigResolveList = mBaseResolveList;
|
||||
currentResolveList = mOrigResolveList = new ArrayList<>();
|
||||
addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
|
||||
} else {
|
||||
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent,
|
||||
PackageManager.MATCH_DEFAULT_ONLY
|
||||
| (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0)
|
||||
| (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0)
|
||||
);
|
||||
final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
|
||||
final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
|
||||
for (int i = 0, N = mIntents.size(); i < N; i++) {
|
||||
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
|
||||
// have permission for. We don't do this when we have an explicit
|
||||
// list of resolved activities, because that only happens when
|
||||
@ -1076,14 +1135,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
// they gave us.
|
||||
if (currentResolveList != null) {
|
||||
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(
|
||||
ai.permission, mLaunchedFromUid,
|
||||
ai.applicationInfo.uid, ai.exported);
|
||||
if (granted != PackageManager.PERMISSION_GRANTED) {
|
||||
// Access not allowed!
|
||||
if (mOrigResolveList == currentResolveList) {
|
||||
mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
|
||||
mOrigResolveList = new ArrayList<>(mOrigResolveList);
|
||||
}
|
||||
currentResolveList.remove(i);
|
||||
}
|
||||
@ -1094,9 +1154,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
|
||||
// Only display the first matches that are either of equal
|
||||
// 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++) {
|
||||
ResolveInfo ri = currentResolveList.get(i);
|
||||
ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
|
||||
if (DEBUG) Log.v(
|
||||
TAG,
|
||||
r0.activityInfo.name + "=" +
|
||||
@ -1107,7 +1168,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
r0.isDefault != ri.isDefault) {
|
||||
while (i < N) {
|
||||
if (mOrigResolveList == currentResolveList) {
|
||||
mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
|
||||
mOrigResolveList = new ArrayList<>(mOrigResolveList);
|
||||
}
|
||||
currentResolveList.remove(i);
|
||||
N--;
|
||||
@ -1115,9 +1176,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
}
|
||||
}
|
||||
if (N > 1) {
|
||||
Comparator<ResolveInfo> rComparator =
|
||||
new ResolverComparator(ResolverActivity.this, mIntent);
|
||||
Collections.sort(currentResolveList, rComparator);
|
||||
Collections.sort(currentResolveList,
|
||||
new ResolverComparator(ResolverActivity.this, getTargetIntent()));
|
||||
}
|
||||
// First put the initial items at the top.
|
||||
if (mInitialIntents != null) {
|
||||
@ -1146,14 +1206,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
ri.nonLocalizedLabel = li.getNonLocalizedLabel();
|
||||
ri.icon = li.getIconResource();
|
||||
}
|
||||
addResolveInfo(new DisplayResolveInfo(ri,
|
||||
addResolveInfo(new DisplayResolveInfo(ii, ri,
|
||||
ri.loadLabel(getPackageManager()), null, ii));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for applications with same name and use application name or
|
||||
// package name if necessary
|
||||
r0 = currentResolveList.get(0);
|
||||
rci0 = currentResolveList.get(0);
|
||||
r0 = rci0.getResolveInfoAt(0);
|
||||
int start = 0;
|
||||
CharSequence r0Label = r0.loadLabel(mPm);
|
||||
mHasExtendedInfo = false;
|
||||
@ -1161,7 +1222,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
if (r0Label == null) {
|
||||
r0Label = r0.activityInfo.packageName;
|
||||
}
|
||||
ResolveInfo ri = currentResolveList.get(i);
|
||||
ResolvedComponentInfo rci = currentResolveList.get(i);
|
||||
ResolveInfo ri = rci.getResolveInfoAt(0);
|
||||
CharSequence riLabel = ri.loadLabel(mPm);
|
||||
if (riLabel == null) {
|
||||
riLabel = ri.activityInfo.packageName;
|
||||
@ -1169,13 +1231,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
if (riLabel.equals(r0Label)) {
|
||||
continue;
|
||||
}
|
||||
processGroup(currentResolveList, start, (i-1), r0, r0Label);
|
||||
processGroup(currentResolveList, start, (i-1), rci0, r0Label);
|
||||
rci0 = rci;
|
||||
r0 = ri;
|
||||
r0Label = riLabel;
|
||||
start = i;
|
||||
}
|
||||
// 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
|
||||
@ -1188,6 +1251,36 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
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() {
|
||||
// This space for rent
|
||||
}
|
||||
@ -1196,18 +1289,18 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
return mFilterLastUsed;
|
||||
}
|
||||
|
||||
private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
|
||||
CharSequence roLabel) {
|
||||
private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
|
||||
ResolvedComponentInfo ro, CharSequence roLabel) {
|
||||
// Process labels from start to i
|
||||
int num = end - start+1;
|
||||
if (num == 1) {
|
||||
// No duplicate labels. Use label for entry at start
|
||||
addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null));
|
||||
updateLastChosenPosition(ro);
|
||||
addResolveInfoWithAlternates(ro, null, roLabel);
|
||||
} else {
|
||||
mHasExtendedInfo = true;
|
||||
boolean usePkg = false;
|
||||
CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
|
||||
CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo
|
||||
.loadLabel(mPm);
|
||||
if (startApp == null) {
|
||||
usePkg = true;
|
||||
}
|
||||
@ -1217,7 +1310,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
new HashSet<CharSequence>();
|
||||
duplicates.add(startApp);
|
||||
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);
|
||||
if ( (jApp == null) || (duplicates.contains(jApp))) {
|
||||
usePkg = true;
|
||||
@ -1230,26 +1323,46 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
duplicates.clear();
|
||||
}
|
||||
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) {
|
||||
// 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
|
||||
addResolveInfo(new DisplayResolveInfo(add, roLabel,
|
||||
add.activityInfo.applicationInfo.loadLabel(mPm), null));
|
||||
extraInfo = add.activityInfo.packageName;
|
||||
} 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) {
|
||||
if (mLastChosen != null
|
||||
&& mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
|
||||
&& 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.
|
||||
mOtherProfile = dri;
|
||||
} else {
|
||||
mList.add(dri);
|
||||
mDisplayList.add(dri);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return filtered ? getItem(position) : mList.get(position);
|
||||
return filtered ? getItem(position) : mDisplayList.get(position);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
int result = mList.size();
|
||||
int result = mDisplayList.size();
|
||||
if (mFilterLastUsed && mLastChosenPosition >= 0) {
|
||||
result--;
|
||||
}
|
||||
@ -1283,7 +1397,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
|
||||
position++;
|
||||
}
|
||||
return mList.get(position);
|
||||
return mDisplayList.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
@ -1295,8 +1409,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
}
|
||||
|
||||
public boolean hasResolvedTarget(ResolveInfo info) {
|
||||
for (int i = 0, N = mList.size(); i < N; i++) {
|
||||
if (info.equals(mList.get(i).getResolveInfo())) {
|
||||
for (int i = 0, N = mDisplayList.size(); i < N; i++) {
|
||||
if (info.equals(mDisplayList.get(i).getResolveInfo())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1304,11 +1418,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
}
|
||||
|
||||
protected int getDisplayResolveInfoCount() {
|
||||
return mList.size();
|
||||
return mDisplayList.size();
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -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 {
|
||||
public TextView text;
|
||||
public TextView text2;
|
||||
@ -1435,7 +1596,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
&& match <= IntentFilter.MATCH_CATEGORY_PATH;
|
||||
}
|
||||
|
||||
class ResolverComparator implements Comparator<ResolveInfo> {
|
||||
class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
private final Collator mCollator;
|
||||
private final boolean mHttp;
|
||||
|
||||
@ -1446,7 +1607,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
}
|
||||
|
||||
@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.
|
||||
if (lhs.targetUserId != UserHandle.USER_CURRENT) {
|
||||
return 1;
|
||||
@ -1487,7 +1651,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
|
||||
if (stats != null) {
|
||||
return stats.getTotalTimeInForeground();
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ public class UsbResolverActivity extends ResolverActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) {
|
||||
protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
|
||||
final ResolveInfo ri = target.getResolveInfo();
|
||||
try {
|
||||
IBinder b = ServiceManager.getService(USB_SERVICE);
|
||||
@ -129,5 +129,6 @@ public class UsbResolverActivity extends ResolverActivity {
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "onIntentSelected failed", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user