diff options
Diffstat (limited to 'java')
10 files changed, 180 insertions, 29 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index c4b7874c..5f81b016 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -1724,7 +1724,7 @@ public class ChooserActivity extends ResolverActivity implements ChooserTargetActionsDialogFragment fragment = new ChooserTargetActionsDialogFragment(); Bundle bundle = new Bundle(); - if (targetInfo instanceof SelectableTargetInfo) { + if (targetInfo.isSelectableTargetInfo()) { SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo; if (selectableTargetInfo.getDisplayResolveInfo() == null || selectableTargetInfo.getChooserTarget() == null) { @@ -1744,7 +1744,7 @@ public class ChooserActivity extends ResolverActivity implements bundle.putString(ChooserTargetActionsDialogFragment.SHORTCUT_TITLE_KEY, selectableTargetInfo.getDisplayLabel().toString()); } - } else if (targetInfo instanceof MultiDisplayResolveInfo) { + } else if (targetInfo.isMultiDisplayResolveInfo()) { // For multiple targets, include info on all targets MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo; targetList = mti.getTargets(); @@ -1806,13 +1806,13 @@ public class ChooserActivity extends ResolverActivity implements mChooserMultiProfilePagerAdapter.getActiveListAdapter(); TargetInfo targetInfo = currentListAdapter .targetInfoForPosition(which, filtered); - if (targetInfo != null && targetInfo instanceof NotSelectableTargetInfo) { + if (targetInfo != null && targetInfo.isNotSelectableTargetInfo()) { return; } final long selectionCost = System.currentTimeMillis() - mChooserShownTime; - if (targetInfo instanceof MultiDisplayResolveInfo) { + if (targetInfo.isMultiDisplayResolveInfo()) { MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo; if (!mti.hasSelected()) { ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(); @@ -2135,7 +2135,7 @@ public class ChooserActivity extends ResolverActivity implements } void updateModelAndChooserCounts(TargetInfo info) { - if (info != null && info instanceof MultiDisplayResolveInfo) { + if (info != null && info.isMultiDisplayResolveInfo()) { info = ((MultiDisplayResolveInfo) info).getSelectedTarget(); } if (info != null) { @@ -2169,7 +2169,7 @@ public class ChooserActivity extends ResolverActivity implements return; } // Send DS target impression info to AppPredictor, only when user chooses app share. - if (targetInfo instanceof ChooserTargetInfo) { + if (targetInfo.isChooserTargetInfo()) { return; } List<ChooserTargetInfo> surfacedTargetInfo = adapter.getSurfacedTargetInfo(); @@ -2193,7 +2193,7 @@ public class ChooserActivity extends ResolverActivity implements if (directShareAppPredictor == null) { return; } - if (!(targetInfo instanceof ChooserTargetInfo)) { + if (!targetInfo.isChooserTargetInfo()) { return; } ChooserTarget chooserTarget = ((ChooserTargetInfo) targetInfo).getChooserTarget(); @@ -2446,6 +2446,11 @@ public class ChooserActivity extends ResolverActivity implements } static final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { + @Override + public boolean isPlaceHolderTargetInfo() { + return true; + } + public Drawable getDisplayIcon(Context context) { AnimatedVectorDrawable avd = (AnimatedVectorDrawable) context.getDrawable(R.drawable.chooser_direct_share_icon_placeholder); @@ -2457,6 +2462,11 @@ public class ChooserActivity extends ResolverActivity implements protected static final class EmptyTargetInfo extends NotSelectableTargetInfo { public EmptyTargetInfo() {} + @Override + public boolean isEmptyTargetInfo() { + return true; + } + public Drawable getDisplayIcon(Context context) { return null; } @@ -2954,7 +2964,7 @@ public class ChooserActivity extends ResolverActivity implements .targetInfoForPosition(mListPosition, /* filtered */ true); // This should always be the case for ItemViewHolder, check for validity - if (ti instanceof DisplayResolveInfo && shouldShowTargetDetails(ti)) { + if (ti.isDisplayResolveInfo() && shouldShowTargetDetails(ti)) { showTargetDetails((DisplayResolveInfo) ti); } return true; @@ -2968,8 +2978,8 @@ public class ChooserActivity extends ResolverActivity implements // Suppress target details for nearby share to hide pin/unpin action boolean isNearbyShare = nearbyShare != null && nearbyShare.equals( ti.getResolvedComponentName()) && shouldNearbyShareBeFirstInRankedRow(); - return ti instanceof SelectableTargetInfo - || (ti instanceof DisplayResolveInfo && !isNearbyShare); + return ti.isSelectableTargetInfo() + || (ti.isDisplayResolveInfo() && !isNearbyShare); } /** @@ -3452,7 +3462,7 @@ public class ChooserActivity extends ResolverActivity implements end--; } - if (end == start && mChooserListAdapter.getItem(start) instanceof EmptyTargetInfo) { + if (end == start && mChooserListAdapter.getItem(start).isEmptyTargetInfo()) { final TextView textView = viewGroup.findViewById(com.android.internal.R.id.chooser_row_text_option); if (textView.getVisibility() != View.VISIBLE) { diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index 5e508ed8..0a8b3890 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -268,7 +268,7 @@ public class ChooserListAdapter extends ResolverListAdapter { holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel()); holder.bindIcon(info); - if (info instanceof SelectableTargetInfo) { + if (info.isSelectableTargetInfo()) { // direct share targets should append the application name for a better readout DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo(); CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : ""; @@ -276,7 +276,7 @@ public class ChooserListAdapter extends ResolverListAdapter { String contentDescription = String.join(" ", info.getDisplayLabel(), extendedInfo != null ? extendedInfo : "", appName); holder.updateContentDescription(contentDescription); - } else if (info instanceof DisplayResolveInfo) { + } else if (info.isDisplayResolveInfo()) { DisplayResolveInfo dri = (DisplayResolveInfo) info; if (!dri.hasDisplayIcon()) { loadIcon(dri); @@ -284,7 +284,7 @@ public class ChooserListAdapter extends ResolverListAdapter { } // If target is loading, show a special placeholder shape in the label, make unclickable - if (info instanceof ChooserActivity.PlaceHolderTargetInfo) { + if (info.isPlaceHolderTargetInfo()) { final int maxWidth = mContext.getResources().getDimensionPixelSize( R.dimen.chooser_direct_share_label_placeholder_max_width); holder.text.setMaxWidth(maxWidth); @@ -301,7 +301,7 @@ public class ChooserListAdapter extends ResolverListAdapter { // Always remove the spacing listener, attach as needed to direct share targets below. holder.text.removeOnLayoutChangeListener(mPinTextSpacingListener); - if (info instanceof MultiDisplayResolveInfo) { + if (info.isMultiDisplayResolveInfo()) { // If the target is grouped show an indicator Drawable bkg = mContext.getDrawable(R.drawable.chooser_group_background); holder.text.setPaddingRelative(0, 0, bkg.getIntrinsicWidth() /* end */, 0); @@ -338,7 +338,7 @@ public class ChooserListAdapter extends ResolverListAdapter { DisplayResolveInfo multiDri = consolidated.get(resolvedTarget); if (multiDri == null) { consolidated.put(resolvedTarget, info); - } else if (multiDri instanceof MultiDisplayResolveInfo) { + } else if (multiDri.isMultiDisplayResolveInfo()) { ((MultiDisplayResolveInfo) multiDri).addTarget(info); } else { // create consolidated target from the single DisplayResolveInfo @@ -387,7 +387,7 @@ public class ChooserListAdapter extends ResolverListAdapter { public int getSelectableServiceTargetCount() { int count = 0; for (ChooserTargetInfo info : mServiceTargets) { - if (info instanceof SelectableTargetInfo) { + if (info.isSelectableTargetInfo()) { count++; } } @@ -530,8 +530,7 @@ public class ChooserListAdapter extends ResolverListAdapter { @ChooserActivity.ShareTargetType int targetType, Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos) { // Avoid inserting any potentially late results. - if ((mServiceTargets.size() == 1) - && (mServiceTargets.get(0) instanceof ChooserActivity.EmptyTargetInfo)) { + if ((mServiceTargets.size() == 1) && mServiceTargets.get(0).isEmptyTargetInfo()) { return; } boolean isShortcutResult = targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER @@ -579,7 +578,7 @@ public class ChooserListAdapter extends ResolverListAdapter { * update the direct share area. */ public void completeServiceTargetLoading() { - mServiceTargets.removeIf(o -> o instanceof ChooserActivity.PlaceHolderTargetInfo); + mServiceTargets.removeIf(o -> o.isPlaceHolderTargetInfo()); if (mServiceTargets.isEmpty()) { mServiceTargets.add(new ChooserActivity.EmptyTargetInfo()); mChooserActivityLogger.logSharesheetEmptyDirectShareRow(); diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java index 0733f4fa..26f3b535 100644 --- a/java/src/com/android/intentresolver/ResolverActivity.java +++ b/java/src/com/android/intentresolver/ResolverActivity.java @@ -94,7 +94,6 @@ import androidx.fragment.app.FragmentActivity; import androidx.viewpager.widget.ViewPager; import com.android.intentresolver.AbstractMultiProfilePagerAdapter.Profile; -import com.android.intentresolver.chooser.ChooserTargetInfo; import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; import com.android.intentresolver.widget.ResolverDrawerLayout; @@ -1373,7 +1372,7 @@ public class ResolverActivity extends FragmentActivity implements .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED) .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle())) .setStrings(getMetricsCategory(), - cti instanceof ChooserTargetInfo ? "direct_share" : "other_target") + cti.isInDirectShareMetricsCategory() ? "direct_share" : "other_target") .write(); } diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java index 251b157b..3c8eae3a 100644 --- a/java/src/com/android/intentresolver/ResolverListAdapter.java +++ b/java/src/com/android/intentresolver/ResolverListAdapter.java @@ -644,7 +644,7 @@ public class ResolverListAdapter extends BaseAdapter { return; } - if (info instanceof DisplayResolveInfo) { + if (info.isDisplayResolveInfo()) { DisplayResolveInfo dri = (DisplayResolveInfo) info; boolean hasLabel = dri.hasDisplayLabel(); holder.bindLabel( diff --git a/java/src/com/android/intentresolver/chooser/ChooserTargetInfo.java b/java/src/com/android/intentresolver/chooser/ChooserTargetInfo.java index 1c763071..77c30102 100644 --- a/java/src/com/android/intentresolver/chooser/ChooserTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/ChooserTargetInfo.java @@ -16,6 +16,7 @@ package com.android.intentresolver.chooser; +import android.annotation.Nullable; import android.service.chooser.ChooserTarget; import android.text.TextUtils; @@ -23,16 +24,40 @@ import android.text.TextUtils; * A TargetInfo for Direct Share. Includes a {@link ChooserTarget} representing the * Direct Share deep link into an application. */ -public interface ChooserTargetInfo extends TargetInfo { - float getModifiedScore(); +public abstract class ChooserTargetInfo implements TargetInfo { + /** + * @return the target score, including any Chooser-specific modifications that may have been + * applied (either overriding by special-case for "non-selectable" targets, or by twiddling the + * scores of "selectable" targets in {@link ChooserListAdapter}). Higher scores are "better." + */ + public abstract float getModifiedScore(); + + /** + * @return the {@link ChooserTarget} record that contains additional data about this target, if + * any. This is only non-null for selectable targets (and probably only Direct Share targets?). + * + * @deprecated {@link ChooserTarget} (and any other related {@code ChooserTargetService} APIs) + * got deprecated as part of sunsetting that old system design, but for historical reasons + * Chooser continues to shoehorn data from other sources into this representation to maintain + * compatibility with legacy internal APIs. New clients should avoid taking any further + * dependencies on the {@link ChooserTarget} type; any data they want to query from those + * records should instead be pulled up to new query methods directly on this class (or on the + * root {@link TargetInfo}). + */ + @Deprecated + @Nullable + public abstract ChooserTarget getChooserTarget(); - ChooserTarget getChooserTarget(); + @Override + public final boolean isChooserTargetInfo() { + return true; + } /** * Do not label as 'equals', since this doesn't quite work * as intended with java 8. */ - default boolean isSimilar(ChooserTargetInfo other) { + public boolean isSimilar(ChooserTargetInfo other) { if (other == null) return false; ChooserTarget ct1 = getChooserTarget(); diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java index c4bca266..8f950323 100644 --- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java @@ -100,6 +100,11 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { mResolveInfoPresentationGetter = other.mResolveInfoPresentationGetter; } + @Override + public final boolean isDisplayResolveInfo() { + return true; + } + public ResolveInfo getResolveInfo() { return mResolveInfo; } diff --git a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java index 5133d997..e1fe58fd 100644 --- a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java @@ -45,6 +45,11 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { } @Override + public final boolean isMultiDisplayResolveInfo() { + return true; + } + + @Override public CharSequence getExtendedInfo() { // Never show subtitle for stacked apps return null; diff --git a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java index 220870f2..db03b785 100644 --- a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java @@ -32,7 +32,10 @@ import java.util.List; * Distinguish between targets that selectable by the user, vs those that are * placeholders for the system while information is loading in an async manner. */ -public abstract class NotSelectableTargetInfo implements ChooserTargetInfo { +public abstract class NotSelectableTargetInfo extends ChooserTargetInfo { + public final boolean isNotSelectableTargetInfo() { + return true; + } public Intent getResolvedIntent() { return null; diff --git a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java index 179966ad..0f48cfd1 100644 --- a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java @@ -50,7 +50,7 @@ import java.util.List; * Live target, currently selectable by the user. * @see NotSelectableTargetInfo */ -public final class SelectableTargetInfo implements ChooserTargetInfo { +public final class SelectableTargetInfo extends ChooserTargetInfo { private static final String TAG = "SelectableTargetInfo"; private final Context mContext; @@ -134,6 +134,11 @@ public final class SelectableTargetInfo implements ChooserTargetInfo { mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle()); } + @Override + public boolean isSelectableTargetInfo() { + return true; + } + private String sanitizeDisplayLabel(CharSequence label) { SpannableStringBuilder sb = new SpannableStringBuilder(label); sb.clearSpans(); diff --git a/java/src/com/android/intentresolver/chooser/TargetInfo.java b/java/src/com/android/intentresolver/chooser/TargetInfo.java index e1970354..502a3342 100644 --- a/java/src/com/android/intentresolver/chooser/TargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/TargetInfo.java @@ -132,6 +132,106 @@ public interface TargetInfo { boolean isPinned(); /** + * @return true if this target represents a legacy {@code ChooserTargetInfo}. These objects were + * historically documented as representing "[a] TargetInfo for Direct Share." However, not all + * of these targets are actually *valid* for direct share; e.g. some represent "empty" items + * (although perhaps only for display in the Direct Share UI?). {@link #getChooserTarget()} will + * return null for any of these "invalid" items. In even earlier versions, these targets may + * also have been results from (now-deprecated/unsupported) {@code ChooserTargetService} peers; + * even though we no longer use these services, we're still shoehorning other target data into + * the deprecated {@link ChooserTarget} structure for compatibility with some internal APIs. + * TODO: refactor to clarify the semantics of any target for which this method returns true + * (e.g., are they characterized by their application in the Direct Share UI?), and to remove + * the scaffolding that adapts to and from the {@link ChooserTarget} structure. Eventually, we + * expect to remove this method (and others that strictly indicate legacy subclass roles) in + * favor of a more semantic design that expresses the purpose and distinctions in those roles. + */ + default boolean isChooserTargetInfo() { + return false; + } + + /** + * @return true if this target represents a legacy {@code DisplayResolveInfo}. These objects + * were historically documented as an augmented "TargetInfo plus additional information needed + * to render it (such as icon and label) and resolve it to an activity." That description in no + * way distinguishes from the base {@code TargetInfo} API. At the time of writing, these objects + * are most-clearly defined by their opposite; this returns true for exactly those instances of + * {@code TargetInfo} where {@link #isChooserTargetInfo()} returns false (these conditions are + * complementary because they correspond to the immediate {@code TargetInfo} child types that + * historically partitioned all concrete {@code TargetInfo} implementations). These may(?) + * represent any target displayed somewhere other than the Direct Share UI. + */ + default boolean isDisplayResolveInfo() { + return false; + } + + /** + * @return true if this target represents a legacy {@code MultiDisplayResolveInfo}. These + * objects were historically documented as representing "a 'stack' of chooser targets for + * various activities within the same component." For historical reasons this currently can + * return true only if {@link #isDisplayResolveInfo()} returns true (because the legacy classes + * shared an inheritance relationship), but new code should avoid relying on that relationship + * since these APIs are "in transition." + */ + default boolean isMultiDisplayResolveInfo() { + return false; + } + + /** + * @return true if this target represents a legacy {@code SelectableTargetInfo}. Note that this + * is defined for legacy compatibility and may not conform to other notions of a "selectable" + * target. For historical reasons, this method and {@link #isNotSelectableTargetInfo()} only + * partition the {@code TargetInfo} instances for which {@link #isChooserTargetInfo()} returns + * true; otherwise <em>both</em> methods return false. + * TODO: define selectability for targets not historically from {@code ChooserTargetInfo}, + * then attempt to replace this with a new method like {@code TargetInfo#isSelectable()} that + * actually partitions <em>all</em> target types (after updating client usage as needed). + */ + default boolean isSelectableTargetInfo() { + return false; + } + + /** + * @return true if this target represents a legacy {@code NotSelectableTargetInfo} (i.e., a + * target where {@link #isChooserTargetInfo()} is true but {@link #isSelectableTargetInfo()} is + * false). For more information on how this divides the space of targets, see the Javadoc for + * {@link #isSelectableTargetInfo()}. + */ + default boolean isNotSelectableTargetInfo() { + return false; + } + + /** + * @return true if this target represents a legacy {@code ChooserActivity#EmptyTargetInfo}. Note + * that this is defined for legacy compatibility and may not conform to other notions of an + * "empty" target. + */ + default boolean isEmptyTargetInfo() { + return false; + } + + /** + * @return true if this target represents a legacy {@code ChooserActivity#PlaceHolderTargetInfo} + * (defined only for compatibility with historic use in {@link ChooserListAdapter}). For + * historic reasons (owing to a legacy subclass relationship) this can return true only if + * {@link #isNotSelectableTargetInfo()} also returns true. + */ + default boolean isPlaceHolderTargetInfo() { + return false; + } + + /** + * @return true if this target should be logged with the "direct_share" metrics category in + * {@link ResolverActivity#maybeLogCrossProfileTargetLaunch()}. This is defined for legacy + * compatibility and is <em>not</em> likely to be a good indicator of whether this is actually a + * "direct share" target (e.g. because it historically also applies to "empty" and "placeholder" + * targets). + */ + default boolean isInDirectShareMetricsCategory() { + return isChooserTargetInfo(); + } + + /** * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called * before launching the intent as another user. */ |