diff options
| author | 2022-12-08 15:31:49 +0000 | |
|---|---|---|
| committer | 2022-12-08 15:31:49 +0000 | |
| commit | 92269439a35e7bc3b81c7e7efdef7ae0b63a6637 (patch) | |
| tree | d25208a953868141bd637af649e5443802bf4c70 /java/src | |
| parent | 93587561b4515fb1a96b0763fb730d4940292d0c (diff) | |
| parent | 85257d68d79fe1d1188b7d4365f35f7d72801355 (diff) | |
Merge "SelectableTargetInfo "immutability"/other cleanup" into tm-qpr-dev
Diffstat (limited to 'java/src')
8 files changed, 306 insertions, 146 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index ba121050..c3864480 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -896,7 +896,8 @@ public class ChooserActivity extends ResolverActivity implements "", resolveIntent, null); - dri.setDisplayIcon(getDrawable(com.android.internal.R.drawable.ic_screenshot_edit)); + dri.getDisplayIconHolder().setDisplayIcon( + getDrawable(com.android.internal.R.drawable.ic_screenshot_edit)); return dri; } @@ -940,7 +941,7 @@ public class ChooserActivity extends ResolverActivity implements final DisplayResolveInfo dri = DisplayResolveInfo.newDisplayResolveInfo( originalIntent, ri, name, "", resolveIntent, null); - dri.setDisplayIcon(icon); + dri.getDisplayIconHolder().setDisplayIcon(icon); return dri; } @@ -970,7 +971,7 @@ public class ChooserActivity extends ResolverActivity implements if (ti == null) return null; final Button b = createActionButton( - ti.getDisplayIcon(), + ti.getDisplayIconHolder().getDisplayIcon(), ti.getDisplayLabel(), (View unused) -> { // Log share completion via nearby @@ -993,7 +994,7 @@ public class ChooserActivity extends ResolverActivity implements if (ti == null) return null; final Button b = createActionButton( - ti.getDisplayIcon(), + ti.getDisplayIconHolder().getDisplayIcon(), ti.getDisplayLabel(), (View unused) -> { // Log share completion via edit diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index 59d1a6e3..6d59a680 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -667,7 +667,7 @@ public class ChooserListAdapter extends ResolverListAdapter { @Override protected void onPostExecute(@Nullable Drawable icon) { if (icon != null && !mTargetInfo.hasDisplayIcon()) { - mTargetInfo.setDisplayIcon(icon); + mTargetInfo.getDisplayIconHolder().setDisplayIcon(icon); notifyDataSetChanged(); } } diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java index adcfecef..5573e18a 100644 --- a/java/src/com/android/intentresolver/ResolverActivity.java +++ b/java/src/com/android/intentresolver/ResolverActivity.java @@ -1627,7 +1627,7 @@ public class ResolverActivity extends FragmentActivity implements @Override protected void onPostExecute(Drawable drawable) { if (!isDestroyed()) { - otherProfileResolveInfo.setDisplayIcon(drawable); + otherProfileResolveInfo.getDisplayIconHolder().setDisplayIcon(drawable); new ResolverListAdapter.ViewHolder(icon).bindIcon(otherProfileResolveInfo); } } diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java index 9f654594..46e39fe6 100644 --- a/java/src/com/android/intentresolver/ResolverListAdapter.java +++ b/java/src/com/android/intentresolver/ResolverListAdapter.java @@ -950,7 +950,7 @@ public class ResolverListAdapter extends BaseAdapter { } public void bindIcon(TargetInfo info) { - icon.setImageDrawable(info.getDisplayIcon()); + icon.setImageDrawable(info.getDisplayIconHolder().getDisplayIcon()); if (info.isSuspended()) { icon.setColorFilter(getSuspendedColorMatrix()); } else { @@ -1029,7 +1029,7 @@ public class ResolverListAdapter extends BaseAdapter { if (getOtherProfile() == mDisplayResolveInfo) { mResolverListCommunicator.updateProfileViewButton(); } else if (!mDisplayResolveInfo.hasDisplayIcon()) { - mDisplayResolveInfo.setDisplayIcon(d); + mDisplayResolveInfo.getDisplayIconHolder().setDisplayIcon(d); notifyDataSetChanged(); } } diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java index 16dd28bc..c1b007af 100644 --- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; @@ -42,13 +41,13 @@ import java.util.List; public class DisplayResolveInfo implements TargetInfo { private final ResolveInfo mResolveInfo; private CharSequence mDisplayLabel; - private Drawable mDisplayIcon; private CharSequence mExtendedInfo; private final Intent mResolvedIntent; private final List<Intent> mSourceIntents = new ArrayList<>(); private final boolean mIsSuspended; private ResolveInfoPresentationGetter mResolveInfoPresentationGetter; private boolean mPinned = false; + private final IconHolder mDisplayIconHolder = new SettableIconHolder(); /** Create a new {@code DisplayResolveInfo} instance. */ public static DisplayResolveInfo newDisplayResolveInfo( @@ -103,7 +102,6 @@ public class DisplayResolveInfo implements TargetInfo { | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name)); mResolvedIntent = intent; - } private DisplayResolveInfo( @@ -115,11 +113,12 @@ public class DisplayResolveInfo implements TargetInfo { mResolveInfo = other.mResolveInfo; mIsSuspended = other.mIsSuspended; mDisplayLabel = other.mDisplayLabel; - mDisplayIcon = other.mDisplayIcon; mExtendedInfo = other.mExtendedInfo; mResolvedIntent = new Intent(other.mResolvedIntent); mResolvedIntent.fillIn(fillInIntent, flags); mResolveInfoPresentationGetter = resolveInfoPresentationGetter; + + mDisplayIconHolder.setDisplayIcon(other.mDisplayIconHolder.getDisplayIcon()); } protected DisplayResolveInfo(DisplayResolveInfo other) { @@ -127,10 +126,11 @@ public class DisplayResolveInfo implements TargetInfo { mResolveInfo = other.mResolveInfo; mIsSuspended = other.mIsSuspended; mDisplayLabel = other.mDisplayLabel; - mDisplayIcon = other.mDisplayIcon; mExtendedInfo = other.mExtendedInfo; mResolvedIntent = other.mResolvedIntent; mResolveInfoPresentationGetter = other.mResolveInfoPresentationGetter; + + mDisplayIconHolder.setDisplayIcon(other.mDisplayIconHolder.getDisplayIcon()); } @Override @@ -163,8 +163,8 @@ public class DisplayResolveInfo implements TargetInfo { } @Override - public Drawable getDisplayIcon() { - return mDisplayIcon; + public IconHolder getDisplayIconHolder() { + return mDisplayIconHolder; } @Override @@ -186,10 +186,6 @@ public class DisplayResolveInfo implements TargetInfo { mSourceIntents.add(alt); } - public void setDisplayIcon(Drawable icon) { - mDisplayIcon = icon; - } - public CharSequence getExtendedInfo() { return mExtendedInfo; } diff --git a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java index 3b4b89b1..d6333374 100644 --- a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java @@ -43,11 +43,6 @@ public abstract class NotSelectableTargetInfo extends ChooserTargetInfo { public boolean isEmptyTargetInfo() { return true; } - - @Override - public Drawable getDisplayIcon() { - return null; - } }; } @@ -63,11 +58,20 @@ public abstract class NotSelectableTargetInfo extends ChooserTargetInfo { } @Override - public Drawable getDisplayIcon() { - AnimatedVectorDrawable avd = (AnimatedVectorDrawable) - context.getDrawable(R.drawable.chooser_direct_share_icon_placeholder); - avd.start(); // Start animation after generation. - return avd; + public IconHolder getDisplayIconHolder() { + return new IconHolder() { + @Override + public Drawable getDisplayIcon() { + AnimatedVectorDrawable avd = (AnimatedVectorDrawable) + context.getDrawable( + R.drawable.chooser_direct_share_icon_placeholder); + avd.start(); // Start animation after generation. + return avd; + } + + @Override + public void setDisplayIcon(Drawable icon) {} + }; } @Override @@ -132,4 +136,17 @@ public abstract class NotSelectableTargetInfo extends ChooserTargetInfo { public boolean isPinned() { return false; } + + @Override + public IconHolder getDisplayIconHolder() { + return new IconHolder() { + @Override + public Drawable getDisplayIcon() { + return null; + } + + @Override + public void setDisplayIcon(Drawable icon) {} + }; + } } diff --git a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java index 51a776db..3ab50175 100644 --- a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; -import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; @@ -47,6 +46,16 @@ import java.util.List; public final class SelectableTargetInfo extends ChooserTargetInfo { private static final String TAG = "SelectableTargetInfo"; + private interface TargetHashProvider { + HashedStringCache.HashResult getHashedTargetIdForMetrics(Context context); + } + + private interface TargetActivityStarter { + boolean start(Activity activity, Bundle options); + boolean startAsCaller(Activity activity, Bundle options, int userId); + boolean startAsUser(Activity activity, Bundle options, UserHandle user); + } + private static final String HASHED_STRING_CACHE_TAG = "ChooserActivity"; // For legacy reasons. private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7; @@ -67,9 +76,20 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { private final ShortcutInfo mShortcutInfo; private final ComponentName mChooserTargetComponentName; - private final String mChooserTargetUnsanitizedTitle; + private final CharSequence mChooserTargetUnsanitizedTitle; private final Icon mChooserTargetIcon; private final Bundle mChooserTargetIntentExtras; + private final int mFillInFlags; + private final boolean mIsPinned; + private final float mModifiedScore; + private final boolean mIsSuspended; + private final ComponentName mResolvedComponentName; + private final Intent mBaseIntentToSend; + private final ResolveInfo mResolveInfo; + private final List<Intent> mAllSourceIntents; + private final IconHolder mDisplayIconHolder = new SettableIconHolder(); + private final TargetHashProvider mHashProvider; + private final TargetActivityStarter mActivityStarter; /** * A refinement intent from the caller, if any (see @@ -82,13 +102,14 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { * in its extended data under the key {@link Intent#EXTRA_REFERRER}. */ private final Intent mReferrerFillInIntent; - private final int mFillInFlags; - private final boolean mIsPinned; - private final float mModifiedScore; - private Drawable mDisplayIcon; - - /** Create a new {@link TargetInfo} instance representing a selectable target. */ + /** + * Create a new {@link TargetInfo} instance representing a selectable target. Some target + * parameters are copied over from the (deprecated) legacy {@link ChooserTarget} structure. + * + * @deprecated Use the overload that doesn't call for a {@link ChooserTarget}. + */ + @Deprecated public static TargetInfo newSelectableTargetInfo( @Nullable DisplayResolveInfo sourceInfo, @Nullable ResolveInfo backupResolveInfo, @@ -98,65 +119,175 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { @Nullable ShortcutInfo shortcutInfo, @Nullable AppTarget appTarget, Intent referrerFillInIntent) { - return new SelectableTargetInfo( + return newSelectableTargetInfo( sourceInfo, backupResolveInfo, resolvedIntent, - chooserTarget, + chooserTarget.getComponentName(), + chooserTarget.getTitle(), + chooserTarget.getIcon(), + chooserTarget.getIntentExtras(), modifiedScore, shortcutInfo, appTarget, referrerFillInIntent); } - private SelectableTargetInfo( + /** + * Create a new {@link TargetInfo} instance representing a selectable target. `chooserTarget*` + * parameters were historically retrieved from (now-deprecated) {@link ChooserTarget} structures + * even when the {@link TargetInfo} was a system (internal) synthesized target that never needed + * to be represented as a {@link ChooserTarget}. The values passed here are copied in directly + * as if they had been provided in the legacy representation. + * + * TODO: clarify semantics of how clients use the `getChooserTarget*()` methods; refactor/rename + * to avoid making reference to the legacy type; and reflect the improved semantics in the + * signature (and documentation) of this method. + */ + public static TargetInfo newSelectableTargetInfo( @Nullable DisplayResolveInfo sourceInfo, @Nullable ResolveInfo backupResolveInfo, Intent resolvedIntent, - ChooserTarget chooserTarget, + ComponentName chooserTargetComponentName, + CharSequence chooserTargetUnsanitizedTitle, + Icon chooserTargetIcon, + @Nullable Bundle chooserTargetIntentExtras, float modifiedScore, @Nullable ShortcutInfo shortcutInfo, @Nullable AppTarget appTarget, Intent referrerFillInIntent) { + return new SelectableTargetInfo( + sourceInfo, + backupResolveInfo, + resolvedIntent, + chooserTargetComponentName, + chooserTargetUnsanitizedTitle, + chooserTargetIcon, + chooserTargetIntentExtras, + modifiedScore, + shortcutInfo, + appTarget, + referrerFillInIntent, + /* fillInIntent = */ null, + /* fillInFlags = */ 0); + } + + private SelectableTargetInfo( + @Nullable DisplayResolveInfo sourceInfo, + @Nullable ResolveInfo backupResolveInfo, + Intent resolvedIntent, + ComponentName chooserTargetComponentName, + CharSequence chooserTargetUnsanitizedTitle, + Icon chooserTargetIcon, + Bundle chooserTargetIntentExtras, + float modifiedScore, + @Nullable ShortcutInfo shortcutInfo, + @Nullable AppTarget appTarget, + Intent referrerFillInIntent, + @Nullable Intent fillInIntent, + int fillInFlags) { mSourceInfo = sourceInfo; + mBackupResolveInfo = backupResolveInfo; + mResolvedIntent = resolvedIntent; mModifiedScore = modifiedScore; mShortcutInfo = shortcutInfo; mAppTarget = appTarget; - mIsPinned = shortcutInfo != null && shortcutInfo.isPinned(); - mBackupResolveInfo = backupResolveInfo; - mResolvedIntent = resolvedIntent; mReferrerFillInIntent = referrerFillInIntent; + mFillInIntent = fillInIntent; + mFillInFlags = fillInFlags; + mChooserTargetComponentName = chooserTargetComponentName; + mChooserTargetUnsanitizedTitle = chooserTargetUnsanitizedTitle; + mChooserTargetIcon = chooserTargetIcon; + mChooserTargetIntentExtras = chooserTargetIntentExtras; - mFillInIntent = null; - mFillInFlags = 0; + mIsPinned = (shortcutInfo != null) && shortcutInfo.isPinned(); + mDisplayLabel = sanitizeDisplayLabel(mChooserTargetUnsanitizedTitle); + mIsSuspended = (mSourceInfo != null) && mSourceInfo.isSuspended(); + mResolveInfo = (mSourceInfo != null) ? mSourceInfo.getResolveInfo() : mBackupResolveInfo; + + mResolvedComponentName = getResolvedComponentName(mSourceInfo, mBackupResolveInfo); + + mAllSourceIntents = getAllSourceIntents(sourceInfo); + + mBaseIntentToSend = getBaseIntentToSend( + mResolvedIntent, + mFillInIntent, + mFillInFlags, + mReferrerFillInIntent); + + mHashProvider = context -> { + final String plaintext = + getChooserTargetComponentName().getPackageName() + + mChooserTargetUnsanitizedTitle; + return HashedStringCache.getInstance().hashString( + context, + HASHED_STRING_CACHE_TAG, + plaintext, + mMaxHashSaltDays); + }; + + mActivityStarter = new TargetActivityStarter() { + @Override + public boolean start(Activity activity, Bundle options) { + throw new RuntimeException("ChooserTargets should be started as caller."); + } - mChooserTargetComponentName = chooserTarget.getComponentName(); - mChooserTargetUnsanitizedTitle = chooserTarget.getTitle().toString(); - mChooserTargetIcon = chooserTarget.getIcon(); - mChooserTargetIntentExtras = chooserTarget.getIntentExtras(); + @Override + public boolean startAsCaller(Activity activity, Bundle options, int userId) { + final Intent intent = mBaseIntentToSend; + if (intent == null) { + return false; + } + intent.setComponent(getChooserTargetComponentName()); + intent.putExtras(mChooserTargetIntentExtras); + TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId); + + // Important: we will ignore the target security checks in ActivityManager if and + // only if the ChooserTarget's target package is the same package where we got the + // ChooserTargetService that provided it. This lets a ChooserTargetService provide + // a non-exported or permission-guarded target for the user to pick. + // + // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere + // so we'll obey the caller's normal security checks. + final boolean ignoreTargetSecurity = (mSourceInfo != null) + && mSourceInfo.getResolvedComponentName().getPackageName() + .equals(getChooserTargetComponentName().getPackageName()); + activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId); + return true; + } - mDisplayLabel = sanitizeDisplayLabel(mChooserTargetUnsanitizedTitle); + @Override + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + throw new RuntimeException("ChooserTargets should be started as caller."); + } + }; } private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) { - mSourceInfo = other.mSourceInfo; - mBackupResolveInfo = other.mBackupResolveInfo; - mResolvedIntent = other.mResolvedIntent; - mShortcutInfo = other.mShortcutInfo; - mAppTarget = other.mAppTarget; - mDisplayIcon = other.mDisplayIcon; - mFillInIntent = fillInIntent; - mFillInFlags = flags; - mModifiedScore = other.mModifiedScore; - mIsPinned = other.mIsPinned; - mReferrerFillInIntent = other.mReferrerFillInIntent; + this( + other.mSourceInfo, + other.mBackupResolveInfo, + other.mResolvedIntent, + other.mChooserTargetComponentName, + other.mChooserTargetUnsanitizedTitle, + other.mChooserTargetIcon, + other.mChooserTargetIntentExtras, + other.mModifiedScore, + other.mShortcutInfo, + other.mAppTarget, + other.mReferrerFillInIntent, + fillInIntent, + flags); + } - mChooserTargetComponentName = other.mChooserTargetComponentName; - mChooserTargetUnsanitizedTitle = other.mChooserTargetUnsanitizedTitle; - mChooserTargetIcon = other.mChooserTargetIcon; - mChooserTargetIntentExtras = other.mChooserTargetIntentExtras; + @Override + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { + return new SelectableTargetInfo(this, fillInIntent, flags); + } - mDisplayLabel = sanitizeDisplayLabel(mChooserTargetUnsanitizedTitle); + @Override + public HashedStringCache.HashResult getHashedTargetIdForMetrics(Context context) { + return mHashProvider.getHashedTargetIdForMetrics(context); } @Override @@ -166,7 +297,7 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { @Override public boolean isSuspended() { - return (mSourceInfo != null) && mSourceInfo.isSuspended(); + return mIsSuspended; } @Override @@ -187,13 +318,7 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { @Override public ComponentName getResolvedComponentName() { - if (mSourceInfo != null) { - return mSourceInfo.getResolvedComponentName(); - } else if (mBackupResolveInfo != null) { - return new ComponentName(mBackupResolveInfo.activityInfo.packageName, - mBackupResolveInfo.activityInfo.name); - } - return null; + return mResolvedComponentName; } @Override @@ -206,58 +331,24 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { return mChooserTargetIcon; } - private Intent getBaseIntentToSend() { - Intent result = getResolvedIntent(); - if (result == null) { - Log.e(TAG, "ChooserTargetInfo: no base intent available to send"); - } else { - result = new Intent(result); - if (mFillInIntent != null) { - result.fillIn(mFillInIntent, mFillInFlags); - } - result.fillIn(mReferrerFillInIntent, 0); - } - return result; - } - @Override public boolean start(Activity activity, Bundle options) { - throw new RuntimeException("ChooserTargets should be started as caller."); + return mActivityStarter.start(activity, options); } @Override public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - final Intent intent = getBaseIntentToSend(); - if (intent == null) { - return false; - } - intent.setComponent(getChooserTargetComponentName()); - intent.putExtras(mChooserTargetIntentExtras); - TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId); - - // Important: we will ignore the target security checks in ActivityManager - // if and only if the ChooserTarget's target package is the same package - // where we got the ChooserTargetService that provided it. This lets a - // ChooserTargetService provide a non-exported or permission-guarded target - // to the chooser for the user to pick. - // - // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere - // so we'll obey the caller's normal security checks. - final boolean ignoreTargetSecurity = mSourceInfo != null - && mSourceInfo.getResolvedComponentName().getPackageName() - .equals(getChooserTargetComponentName().getPackageName()); - activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId); - return true; + return mActivityStarter.startAsCaller(activity, options, userId); } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - throw new RuntimeException("ChooserTargets should be started as caller."); + return mActivityStarter.startAsUser(activity, options, user); } @Override public ResolveInfo getResolveInfo() { - return mSourceInfo != null ? mSourceInfo.getResolveInfo() : mBackupResolveInfo; + return mResolveInfo; } @Override @@ -272,12 +363,8 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { } @Override - public Drawable getDisplayIcon() { - return mDisplayIcon; - } - - public void setDisplayIcon(Drawable icon) { - mDisplayIcon = icon; + public IconHolder getDisplayIconHolder() { + return mDisplayIconHolder; } @Override @@ -293,18 +380,8 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { } @Override - public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { - return new SelectableTargetInfo(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; + return mAllSourceIntents; } @Override @@ -312,21 +389,49 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { return mIsPinned; } - @Override - public HashedStringCache.HashResult getHashedTargetIdForMetrics(Context context) { - final String plaintext = - getChooserTargetComponentName().getPackageName() - + mChooserTargetUnsanitizedTitle; - return HashedStringCache.getInstance().hashString( - context, - HASHED_STRING_CACHE_TAG, - plaintext, - mMaxHashSaltDays); - } - private static String sanitizeDisplayLabel(CharSequence label) { SpannableStringBuilder sb = new SpannableStringBuilder(label); sb.clearSpans(); return sb.toString(); } + + private static List<Intent> getAllSourceIntents(@Nullable DisplayResolveInfo sourceInfo) { + final List<Intent> results = new ArrayList<>(); + if (sourceInfo != null) { + // We only queried the service for the first one in our sourceinfo. + results.add(sourceInfo.getAllSourceIntents().get(0)); + } + return results; + } + + private static ComponentName getResolvedComponentName( + @Nullable DisplayResolveInfo sourceInfo, ResolveInfo backupResolveInfo) { + if (sourceInfo != null) { + return sourceInfo.getResolvedComponentName(); + } else if (backupResolveInfo != null) { + return new ComponentName( + backupResolveInfo.activityInfo.packageName, + backupResolveInfo.activityInfo.name); + } + return null; + } + + @Nullable + private static Intent getBaseIntentToSend( + @Nullable Intent resolvedIntent, + Intent fillInIntent, + int fillInFlags, + Intent referrerFillInIntent) { + Intent result = resolvedIntent; + if (result == null) { + Log.e(TAG, "ChooserTargetInfo: no base intent available to send"); + } else { + result = new Intent(result); + if (fillInIntent != null) { + result.fillIn(fillInIntent, fillInFlags); + } + result.fillIn(referrerFillInIntent, 0); + } + return result; + } } diff --git a/java/src/com/android/intentresolver/chooser/TargetInfo.java b/java/src/com/android/intentresolver/chooser/TargetInfo.java index 0e100d4f..72dd1b0b 100644 --- a/java/src/com/android/intentresolver/chooser/TargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/TargetInfo.java @@ -42,6 +42,42 @@ import java.util.Objects; * A single target as represented in the chooser. */ public interface TargetInfo { + + /** + * Container for a {@link TargetInfo}'s (potentially) mutable icon state. This is provided to + * encapsulate the state so that the {@link TargetInfo} itself can be "immutable" (in some + * sense) as long as it always returns the same {@link IconHolder} instance. + * + * TODO: move "stateful" responsibilities out to clients; for more info see the Javadoc comment + * on {@link #getDisplayIconHolder()}. + */ + interface IconHolder { + /** @return the icon (if it's already loaded, or statically available), or null. */ + @Nullable + Drawable getDisplayIcon(); + + /** + * @param icon the icon to return on subsequent calls to {@link #getDisplayIcon()}. + * Implementations may discard this request as a no-op if they don't support setting. + */ + void setDisplayIcon(Drawable icon); + } + + /** A simple mutable-container implementation of {@link IconHolder}. */ + final class SettableIconHolder implements IconHolder { + @Nullable + private Drawable mDisplayIcon; + + @Nullable + public Drawable getDisplayIcon() { + return mDisplayIcon; + } + + public void setDisplayIcon(Drawable icon) { + mDisplayIcon = icon; + } + } + /** * Get the resolved intent that represents this target. Note that this may not be the * intent that will be launched by calling one of the <code>start</code> methods provided; @@ -135,16 +171,21 @@ public interface TargetInfo { CharSequence getExtendedInfo(); /** - * @return The drawable that should be used to represent this target including badge + * @return the {@link IconHolder} for the icon used to represent this target, including badge. + * + * TODO: while the {@link TargetInfo} may be immutable in always returning the same instance of + * {@link IconHolder} here, the holder itself is mutable state, and could become a problem if we + * ever rely on {@link TargetInfo} immutability elsewhere. Ideally, the {@link TargetInfo} + * should provide an immutable "spec" that tells clients <em>how</em> to load the appropriate + * icon, while leaving the load itself to some external component. */ - @Nullable - Drawable getDisplayIcon(); + IconHolder getDisplayIconHolder(); /** * @return true if display icon is available. */ default boolean hasDisplayIcon() { - return getDisplayIcon() != null; + return getDisplayIconHolder().getDisplayIcon() != null; } /** * Clone this target with the given fill-in information. |