summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Joshua Trask <joshtrask@google.com> 2023-02-07 17:55:27 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2023-02-07 17:55:27 +0000
commit2d6ec2c5793302a8adae46e614eab9d94798c658 (patch)
treeb4a399aa3bc2cdf5cf9a8bb451f340da82ccfb89 /java/src
parent3b090d8398ea604d1ff6056e1788e75b2e944932 (diff)
parent0d66c83c4dd1188a2d425ffa65256af19b90d220 (diff)
Merge "Introduce ImmutableTargetInfo." into tm-qpr-dev am: 0d66c83c4d
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/IntentResolver/+/21161321 Change-Id: I41a43aba4e50a7999cf5407920a4ef2c7b3e4c19 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java596
1 files changed, 596 insertions, 0 deletions
diff --git a/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java b/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java
new file mode 100644
index 00000000..315cea4d
--- /dev/null
+++ b/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.chooser;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.HashedStringCache;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.intentresolver.ResolverActivity;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An implementation of {@link TargetInfo} with immutable data. Any modifications must be made by
+ * creating a new instance (e.g., via {@link ImmutableTargetInfo#toBuilder()}).
+ */
+public final class ImmutableTargetInfo implements TargetInfo {
+ private static final String TAG = "TargetInfo";
+
+ /** Delegate interface to implement {@link TargetInfo#getHashedTargetIdForMetrics()}. */
+ public interface TargetHashProvider {
+ /** Request a hash for the specified {@code target}. */
+ HashedStringCache.HashResult getHashedTargetIdForMetrics(
+ TargetInfo target, Context context);
+ }
+
+ /** Delegate interface to request that the target be launched by a particular API. */
+ public interface TargetActivityStarter {
+ /**
+ * Request that the delegate use the {@link Activity#startActivity()} API to launch the
+ * specified {@code target}.
+ *
+ * @return true if the target was launched successfully.
+ */
+ boolean start(TargetInfo target, Activity activity, Bundle options);
+
+
+ /**
+ * Request that the delegate use the {@link Activity#startAsCaller()} API to launch the
+ * specified {@code target}.
+ *
+ * @return true if the target was launched successfully.
+ */
+ boolean startAsCaller(TargetInfo target, Activity activity, Bundle options, int userId);
+
+ /**
+ * Request that the delegate use the {@link Activity#startAsUser()} API to launch the
+ * specified {@code target}.
+ *
+ * @return true if the target was launched successfully.
+ */
+ boolean startAsUser(TargetInfo target, Activity activity, Bundle options, UserHandle user);
+ }
+
+ enum LegacyTargetType {
+ NOT_LEGACY_TARGET,
+ EMPTY_TARGET_INFO,
+ PLACEHOLDER_TARGET_INFO,
+ SELECTABLE_TARGET_INFO,
+ DISPLAY_RESOLVE_INFO,
+ MULTI_DISPLAY_RESOLVE_INFO
+ };
+
+ /** Builder API to construct {@code ImmutableTargetInfo} instances. */
+ public static class Builder {
+ @Nullable
+ private ComponentName mResolvedComponentName;
+
+ @Nullable
+ private ComponentName mChooserTargetComponentName;
+
+ @Nullable
+ private ShortcutInfo mDirectShareShortcutInfo;
+
+ @Nullable
+ private AppTarget mDirectShareAppTarget;
+
+ @Nullable
+ private DisplayResolveInfo mDisplayResolveInfo;
+
+ @Nullable
+ private TargetHashProvider mHashProvider;
+
+ @Nullable
+ private Intent mReferrerFillInIntent;
+
+ private Intent mResolvedIntent;
+ private Intent mTargetIntent;
+ private TargetActivityStarter mActivityStarter;
+ private ResolveInfo mResolveInfo;
+ private CharSequence mDisplayLabel;
+ private CharSequence mExtendedInfo;
+ private IconHolder mDisplayIconHolder;
+ private List<Intent> mSourceIntents;
+ private List<DisplayResolveInfo> mAllDisplayTargets;
+ private boolean mIsSuspended;
+ private boolean mIsPinned;
+ private float mModifiedScore = -0.1f;
+ private LegacyTargetType mLegacyType = LegacyTargetType.NOT_LEGACY_TARGET;
+
+ /**
+ * Configure an {@link Intent} to be built in to the output target as the resolution for the
+ * requested target data.
+ */
+ public Builder setResolvedIntent(Intent resolvedIntent) {
+ mResolvedIntent = resolvedIntent;
+ return this;
+ }
+
+ /**
+ * Configure an {@link Intent} to be built in to the output as the "target intent."
+ */
+ public Builder setTargetIntent(Intent targetIntent) {
+ mTargetIntent = targetIntent;
+ return this;
+ }
+
+ /**
+ * Configure a fill-in intent provided by the referrer to be used in populating the launch
+ * intent if the output target is ever selected.
+ *
+ * @see android.content.Intent#fillIn(Intent, int)
+ */
+ public Builder setReferrerFillInIntent(@Nullable Intent referrerFillInIntent) {
+ mReferrerFillInIntent = referrerFillInIntent;
+ return this;
+ }
+
+ /**
+ * Configure a {@link ComponentName} to be built in to the output target, as the real
+ * component we were able to resolve on this device given the available target data.
+ */
+ public Builder setResolvedComponentName(@Nullable ComponentName resolvedComponentName) {
+ mResolvedComponentName = resolvedComponentName;
+ return this;
+ }
+
+ /**
+ * Configure a {@link ComponentName} to be built in to the output target, as the component
+ * supposedly associated with a {@link ChooserTarget} from which the builder data is being
+ * derived.
+ */
+ public Builder setChooserTargetComponentName(@Nullable ComponentName componentName) {
+ mChooserTargetComponentName = componentName;
+ return this;
+ }
+
+ /** Configure the {@link TargetActivityStarter} to be built in to the output target. */
+ public Builder setActivityStarter(TargetActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ return this;
+ }
+
+ /** Configure the {@link ResolveInfo} to be built in to the output target. */
+ public Builder setResolveInfo(ResolveInfo resolveInfo) {
+ mResolveInfo = resolveInfo;
+ return this;
+ }
+
+ /** Configure the display label to be built in to the output target. */
+ public Builder setDisplayLabel(CharSequence displayLabel) {
+ mDisplayLabel = displayLabel;
+ return this;
+ }
+
+ /** Configure the extended info to be built in to the output target. */
+ public Builder setExtendedInfo(CharSequence extendedInfo) {
+ mExtendedInfo = extendedInfo;
+ return this;
+ }
+
+ /** Configure the {@link IconHolder} to be built in to the output target. */
+ public Builder setDisplayIconHolder(IconHolder displayIconHolder) {
+ mDisplayIconHolder = displayIconHolder;
+ return this;
+ }
+
+ /** Configure the list of source intents to be built in to the output target. */
+ public Builder setAllSourceIntents(List<Intent> sourceIntents) {
+ mSourceIntents = sourceIntents;
+ return this;
+ }
+
+ /** Configure the list of display targets to be built in to the output target. */
+ public Builder setAllDisplayTargets(List<DisplayResolveInfo> targets) {
+ mAllDisplayTargets = targets;
+ return this;
+ }
+
+ /** Configure the is-suspended status to be built in to the output target. */
+ public Builder setIsSuspended(boolean isSuspended) {
+ mIsSuspended = isSuspended;
+ return this;
+ }
+
+ /** Configure the is-pinned status to be built in to the output target. */
+ public Builder setIsPinned(boolean isPinned) {
+ mIsPinned = isPinned;
+ return this;
+ }
+
+ /** Configure the modified score to be built in to the output target. */
+ public Builder setModifiedScore(float modifiedScore) {
+ mModifiedScore = modifiedScore;
+ return this;
+ }
+
+ /** Configure the {@link ShortcutInfo} to be built in to the output target. */
+ public Builder setDirectShareShortcutInfo(@Nullable ShortcutInfo shortcutInfo) {
+ mDirectShareShortcutInfo = shortcutInfo;
+ return this;
+ }
+
+ /** Configure the {@link AppTarget} to be built in to the output target. */
+ public Builder setDirectShareAppTarget(@Nullable AppTarget appTarget) {
+ mDirectShareAppTarget = appTarget;
+ return this;
+ }
+
+ /** Configure the {@link DisplayResolveInfo} to be built in to the output target. */
+ public Builder setDisplayResolveInfo(@Nullable DisplayResolveInfo displayResolveInfo) {
+ mDisplayResolveInfo = displayResolveInfo;
+ return this;
+ }
+
+ /** Configure the {@link TargetHashProvider} to be built in to the output target. */
+ public Builder setHashProvider(@Nullable TargetHashProvider hashProvider) {
+ mHashProvider = hashProvider;
+ return this;
+ }
+
+ Builder setLegacyType(LegacyTargetType legacyType) {
+ mLegacyType = legacyType;
+ return this;
+ }
+
+ /**
+ * Construct an {@code ImmutableTargetInfo} with the current builder data, where the
+ * provided intent is used to fill in missing values from the resolved intent before the
+ * target is (potentially) ever launched.
+ *
+ * @see android.content.Intent#fillIn(Intent, int)
+ */
+ public ImmutableTargetInfo buildWithFillInIntent(
+ @Nullable Intent fillInIntent, int fillInFlags) {
+ Intent baseIntentToSend = mResolvedIntent;
+ if (baseIntentToSend == null) {
+ Log.w(TAG, "No base intent to send");
+ } else {
+ baseIntentToSend = new Intent(baseIntentToSend);
+ if (fillInIntent != null) {
+ baseIntentToSend.fillIn(fillInIntent, fillInFlags);
+ }
+ if (mReferrerFillInIntent != null) {
+ baseIntentToSend.fillIn(mReferrerFillInIntent, 0);
+ }
+ }
+
+ return new ImmutableTargetInfo(
+ baseIntentToSend,
+ mResolvedIntent,
+ mTargetIntent,
+ mReferrerFillInIntent,
+ mResolvedComponentName,
+ mChooserTargetComponentName,
+ mActivityStarter,
+ mResolveInfo,
+ mDisplayLabel,
+ mExtendedInfo,
+ mDisplayIconHolder,
+ mSourceIntents,
+ mAllDisplayTargets,
+ mIsSuspended,
+ mIsPinned,
+ mModifiedScore,
+ mDirectShareShortcutInfo,
+ mDirectShareAppTarget,
+ mDisplayResolveInfo,
+ mHashProvider,
+ mLegacyType);
+ }
+
+ /** Construct an {@code ImmutableTargetInfo} with the current builder data. */
+ public ImmutableTargetInfo build() {
+ return buildWithFillInIntent(null, 0);
+ }
+ }
+
+ @Nullable
+ private final Intent mReferrerFillInIntent;
+
+ @Nullable
+ private final ComponentName mResolvedComponentName;
+
+ @Nullable
+ private final ComponentName mChooserTargetComponentName;
+
+ @Nullable
+ private final ShortcutInfo mDirectShareShortcutInfo;
+
+ @Nullable
+ private final AppTarget mDirectShareAppTarget;
+
+ @Nullable
+ private final DisplayResolveInfo mDisplayResolveInfo;
+
+ @Nullable
+ private final TargetHashProvider mHashProvider;
+
+ private final Intent mBaseIntentToSend;
+ private final Intent mResolvedIntent;
+ private final Intent mTargetIntent;
+ private final TargetActivityStarter mActivityStarter;
+ private final ResolveInfo mResolveInfo;
+ private final CharSequence mDisplayLabel;
+ private final CharSequence mExtendedInfo;
+ private final IconHolder mDisplayIconHolder;
+ private final ImmutableList<Intent> mSourceIntents;
+ private final ImmutableList<DisplayResolveInfo> mAllDisplayTargets;
+ private final boolean mIsSuspended;
+ private final boolean mIsPinned;
+ private final float mModifiedScore;
+ private final LegacyTargetType mLegacyType;
+
+ /** Construct a {@link Builder}. */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /** Construct a {@link Builder} pre-initialized to match this target. */
+ public Builder toBuilder() {
+ return newBuilder()
+ .setResolvedIntent(getResolvedIntent())
+ .setTargetIntent(getTargetIntent())
+ .setReferrerFillInIntent(getReferrerFillInIntent())
+ .setResolvedComponentName(getResolvedComponentName())
+ .setChooserTargetComponentName(getChooserTargetComponentName())
+ .setActivityStarter(mActivityStarter)
+ .setResolveInfo(getResolveInfo())
+ .setDisplayLabel(getDisplayLabel())
+ .setExtendedInfo(getExtendedInfo())
+ .setDisplayIconHolder(getDisplayIconHolder())
+ .setAllSourceIntents(getAllSourceIntents())
+ .setAllDisplayTargets(getAllDisplayTargets())
+ .setIsSuspended(isSuspended())
+ .setIsPinned(isPinned())
+ .setModifiedScore(getModifiedScore())
+ .setDirectShareShortcutInfo(getDirectShareShortcutInfo())
+ .setDirectShareAppTarget(getDirectShareAppTarget())
+ .setDisplayResolveInfo(getDisplayResolveInfo())
+ .setHashProvider(getHashProvider())
+ .setLegacyType(mLegacyType);
+ }
+
+ @VisibleForTesting
+ Intent getBaseIntentToSend() {
+ return mBaseIntentToSend;
+ }
+
+ @Override
+ public ImmutableTargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
+ return toBuilder().buildWithFillInIntent(fillInIntent, flags);
+ }
+
+ @Override
+ public Intent getResolvedIntent() {
+ return mResolvedIntent;
+ }
+
+ @Override
+ public Intent getTargetIntent() {
+ return mTargetIntent;
+ }
+
+ @Nullable
+ public Intent getReferrerFillInIntent() {
+ return mReferrerFillInIntent;
+ }
+
+ @Override
+ @Nullable
+ public ComponentName getResolvedComponentName() {
+ return mResolvedComponentName;
+ }
+
+ @Override
+ @Nullable
+ public ComponentName getChooserTargetComponentName() {
+ return mChooserTargetComponentName;
+ }
+
+ @Override
+ public boolean start(Activity activity, Bundle options) {
+ return mActivityStarter.start(this, activity, options);
+ }
+
+ @Override
+ public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ return mActivityStarter.startAsCaller(this, activity, options, userId);
+ }
+
+ @Override
+ public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
+ return mActivityStarter.startAsUser(this, activity, options, user);
+ }
+
+ @Override
+ public ResolveInfo getResolveInfo() {
+ return mResolveInfo;
+ }
+
+ @Override
+ public CharSequence getDisplayLabel() {
+ return mDisplayLabel;
+ }
+
+ @Override
+ public CharSequence getExtendedInfo() {
+ return mExtendedInfo;
+ }
+
+ @Override
+ public IconHolder getDisplayIconHolder() {
+ return mDisplayIconHolder;
+ }
+
+ @Override
+ public List<Intent> getAllSourceIntents() {
+ return mSourceIntents;
+ }
+
+ @Override
+ public ArrayList<DisplayResolveInfo> getAllDisplayTargets() {
+ ArrayList<DisplayResolveInfo> targets = new ArrayList<>();
+ targets.addAll(mAllDisplayTargets);
+ return targets;
+ }
+
+ @Override
+ public boolean isSuspended() {
+ return mIsSuspended;
+ }
+
+ @Override
+ public boolean isPinned() {
+ return mIsPinned;
+ }
+
+ @Override
+ public float getModifiedScore() {
+ return mModifiedScore;
+ }
+
+ @Override
+ @Nullable
+ public ShortcutInfo getDirectShareShortcutInfo() {
+ return mDirectShareShortcutInfo;
+ }
+
+ @Override
+ @Nullable
+ public AppTarget getDirectShareAppTarget() {
+ return mDirectShareAppTarget;
+ }
+
+ @Override
+ @Nullable
+ public DisplayResolveInfo getDisplayResolveInfo() {
+ return mDisplayResolveInfo;
+ }
+
+ @Override
+ public HashedStringCache.HashResult getHashedTargetIdForMetrics(Context context) {
+ return (mHashProvider == null)
+ ? null : mHashProvider.getHashedTargetIdForMetrics(this, context);
+ }
+
+ @VisibleForTesting
+ @Nullable
+ TargetHashProvider getHashProvider() {
+ return mHashProvider;
+ }
+
+ @Override
+ public boolean isEmptyTargetInfo() {
+ return mLegacyType == LegacyTargetType.EMPTY_TARGET_INFO;
+ }
+
+ @Override
+ public boolean isPlaceHolderTargetInfo() {
+ return mLegacyType == LegacyTargetType.PLACEHOLDER_TARGET_INFO;
+ }
+
+ @Override
+ public boolean isNotSelectableTargetInfo() {
+ return isEmptyTargetInfo() || isPlaceHolderTargetInfo();
+ }
+
+ @Override
+ public boolean isSelectableTargetInfo() {
+ return mLegacyType == LegacyTargetType.SELECTABLE_TARGET_INFO;
+ }
+
+ @Override
+ public boolean isChooserTargetInfo() {
+ return isNotSelectableTargetInfo() || isSelectableTargetInfo();
+ }
+
+ @Override
+ public boolean isMultiDisplayResolveInfo() {
+ return mLegacyType == LegacyTargetType.MULTI_DISPLAY_RESOLVE_INFO;
+ }
+
+ @Override
+ public boolean isDisplayResolveInfo() {
+ return (mLegacyType == LegacyTargetType.DISPLAY_RESOLVE_INFO)
+ || isMultiDisplayResolveInfo();
+ }
+
+ private ImmutableTargetInfo(
+ Intent baseIntentToSend,
+ Intent resolvedIntent,
+ Intent targetIntent,
+ @Nullable Intent referrerFillInIntent,
+ @Nullable ComponentName resolvedComponentName,
+ @Nullable ComponentName chooserTargetComponentName,
+ TargetActivityStarter activityStarter,
+ ResolveInfo resolveInfo,
+ CharSequence displayLabel,
+ CharSequence extendedInfo,
+ IconHolder iconHolder,
+ @Nullable List<Intent> sourceIntents,
+ @Nullable List<DisplayResolveInfo> allDisplayTargets,
+ boolean isSuspended,
+ boolean isPinned,
+ float modifiedScore,
+ @Nullable ShortcutInfo directShareShortcutInfo,
+ @Nullable AppTarget directShareAppTarget,
+ @Nullable DisplayResolveInfo displayResolveInfo,
+ @Nullable TargetHashProvider hashProvider,
+ LegacyTargetType legacyType) {
+ mBaseIntentToSend = baseIntentToSend;
+ mResolvedIntent = resolvedIntent;
+ mTargetIntent = targetIntent;
+ mReferrerFillInIntent = referrerFillInIntent;
+ mResolvedComponentName = resolvedComponentName;
+ mChooserTargetComponentName = chooserTargetComponentName;
+ mActivityStarter = activityStarter;
+ mResolveInfo = resolveInfo;
+ mDisplayLabel = displayLabel;
+ mExtendedInfo = extendedInfo;
+ mDisplayIconHolder = iconHolder;
+ mSourceIntents = immutableCopyOrEmpty(sourceIntents);
+ mAllDisplayTargets = immutableCopyOrEmpty(allDisplayTargets);
+ mIsSuspended = isSuspended;
+ mIsPinned = isPinned;
+ mModifiedScore = modifiedScore;
+ mDirectShareShortcutInfo = directShareShortcutInfo;
+ mDirectShareAppTarget = directShareAppTarget;
+ mDisplayResolveInfo = displayResolveInfo;
+ mHashProvider = hashProvider;
+ mLegacyType = legacyType;
+ }
+
+ private static <E> ImmutableList<E> immutableCopyOrEmpty(@Nullable List<E> source) {
+ return (source == null) ? ImmutableList.of() : ImmutableList.copyOf(source);
+ }
+}