summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-12-08 15:31:49 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-12-08 15:31:49 +0000
commit92269439a35e7bc3b81c7e7efdef7ae0b63a6637 (patch)
treed25208a953868141bd637af649e5443802bf4c70 /java/src
parent93587561b4515fb1a96b0763fb730d4940292d0c (diff)
parent85257d68d79fe1d1188b7d4365f35f7d72801355 (diff)
Merge "SelectableTargetInfo "immutability"/other cleanup" into tm-qpr-dev
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java9
-rw-r--r--java/src/com/android/intentresolver/ChooserListAdapter.java2
-rw-r--r--java/src/com/android/intentresolver/ResolverActivity.java2
-rw-r--r--java/src/com/android/intentresolver/ResolverListAdapter.java4
-rw-r--r--java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java18
-rw-r--r--java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java37
-rw-r--r--java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java331
-rw-r--r--java/src/com/android/intentresolver/chooser/TargetInfo.java49
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.