summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/res/values/attrs.xml6
-rw-r--r--java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java4
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java265
-rw-r--r--java/src/com/android/intentresolver/ChooserListAdapter.java29
-rw-r--r--java/src/com/android/intentresolver/ResolverActivity.java69
-rw-r--r--java/src/com/android/intentresolver/ResolverListAdapter.java76
-rw-r--r--java/src/com/android/intentresolver/grid/DirectShareViewHolder.java10
-rw-r--r--java/src/com/android/intentresolver/widget/ResolverDrawerLayout.java60
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt2
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java4
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java12
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverWrapperAdapter.java22
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java85
13 files changed, 499 insertions, 145 deletions
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 3ec7c2f3..2f2bbda2 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -26,6 +26,12 @@
<attr name="maxCollapsedHeightSmall" format="dimension" />
<!-- Whether the Drawer should be positioned at the top rather than at the bottom. -->
<attr name="showAtTop" format="boolean" />
+ <!-- By default `ResolverDrawerLayout`’s children views with `layout_ignoreOffset` property
+ set to true have a fixed position in the layout that won’t be affected by the drawer’s
+ movements. This property alternates that behavior. It specifies a child view’s id that
+ will push all ignoreOffset siblings below it when the drawer is moved i.e. setting the
+ top limit the ignoreOffset elements. -->
+ <attr name="ignoreOffsetTopLimit" format="reference" />
</declare-styleable>
<declare-styleable name="ResolverDrawerLayout_LayoutParams">
diff --git a/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java
index 3e1084f4..8b0b10b0 100644
--- a/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/AbstractMultiProfilePagerAdapter.java
@@ -148,7 +148,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
@VisibleForTesting
public UserHandle getCurrentUserHandle() {
- return getActiveListAdapter().mResolverListController.getUserHandle();
+ return getActiveListAdapter().getUserHandle();
}
@Override
@@ -263,7 +263,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private int userHandleToPageIndex(UserHandle userHandle) {
- if (userHandle.equals(getPersonalListAdapter().mResolverListController.getUserHandle())) {
+ if (userHandle.equals(getPersonalListAdapter().getUserHandle())) {
return PROFILE_PERSONAL;
} else {
return PROFILE_WORK;
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index afc3b4bd..ba121050 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -147,7 +147,6 @@ import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -350,6 +349,12 @@ public class ChooserActivity extends ResolverActivity implements
mChooserRequest.getTargetIntentFilter()),
mChooserRequest.getTargetIntentFilter());
+ mPreviewCoordinator = new ChooserContentPreviewCoordinator(
+ mBackgroundThreadPoolExecutor,
+ this,
+ this::hideContentPreview,
+ this::setupPreDrawForSharedElementTransition);
+
super.onCreate(
savedInstanceState,
mChooserRequest.getTargetIntent(),
@@ -359,12 +364,6 @@ public class ChooserActivity extends ResolverActivity implements
/* rList: List<ResolveInfo> = */ null,
/* supportsAlwaysUseOption = */ false);
- mPreviewCoordinator = new ChooserContentPreviewCoordinator(
- mBackgroundThreadPoolExecutor,
- this,
- this::hideContentPreview,
- this::setupPreDrawForSharedElementTransition);
-
mChooserShownTime = System.currentTimeMillis();
final long systemCost = mChooserShownTime - intentReceivedTime;
@@ -1152,7 +1151,7 @@ public class ChooserActivity extends ResolverActivity implements
if (mChooserRequest.getCallerChooserTargets().size() > 0) {
mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
/* origTarget */ null,
- mChooserRequest.getCallerChooserTargets(),
+ new ArrayList<>(mChooserRequest.getCallerChooserTargets()),
TARGET_TYPE_DEFAULT,
/* directShareShortcutInfoCache */ Collections.emptyMap(),
/* directShareAppTargetCache */ Collections.emptyMap());
@@ -1350,11 +1349,11 @@ public class ChooserActivity extends ResolverActivity implements
targetInfo.getChooserTargetComponentName().getPackageName();
ChooserListAdapter currentListAdapter =
mChooserMultiProfilePagerAdapter.getActiveListAdapter();
- int maxRankedResults = Math.min(currentListAdapter.mDisplayList.size(),
- MAX_LOG_RANK_POSITION);
+ int maxRankedResults = Math.min(
+ currentListAdapter.getDisplayResolveInfoCount(), MAX_LOG_RANK_POSITION);
for (int i = 0; i < maxRankedResults; i++) {
- if (currentListAdapter.mDisplayList.get(i)
+ if (currentListAdapter.getDisplayResolveInfo(i)
.getResolveInfo().activityInfo.packageName.equals(targetPackageName)) {
return i;
}
@@ -1618,9 +1617,82 @@ public class ChooserActivity extends ResolverActivity implements
rList,
filterLastUsed,
createListController(userHandle),
+ userHandle,
+ getTargetIntent(),
mChooserRequest,
mMaxTargetsPerRow);
- return new ChooserGridAdapter(chooserListAdapter);
+
+ return new ChooserGridAdapter(
+ context,
+ new ChooserGridAdapter.ChooserActivityDelegate() {
+ @Override
+ public boolean shouldShowTabs() {
+ return ChooserActivity.this.shouldShowTabs();
+ }
+
+ @Override
+ public View buildContentPreview(ViewGroup parent) {
+ return createContentPreviewView(parent, mPreviewCoordinator);
+ }
+
+ @Override
+ public void onTargetSelected(int itemIndex) {
+ startSelected(itemIndex, false, true);
+ }
+
+ @Override
+ public void onTargetLongPressed(int selectedPosition) {
+ final TargetInfo longPressedTargetInfo =
+ mChooserMultiProfilePagerAdapter
+ .getActiveListAdapter()
+ .targetInfoForPosition(
+ selectedPosition, /* filtered= */ true);
+ // ItemViewHolder contents should always be "display resolve info"
+ // targets, but check just to make sure.
+ if (longPressedTargetInfo.isDisplayResolveInfo()) {
+ showTargetDetails(longPressedTargetInfo);
+ }
+ }
+
+ @Override
+ public void updateProfileViewButton(View newButtonFromProfileRow) {
+ mProfileView = newButtonFromProfileRow;
+ mProfileView.setOnClickListener(ChooserActivity.this::onProfileClick);
+ ChooserActivity.this.updateProfileViewButton();
+ }
+
+ @Override
+ public int getValidTargetCount() {
+ return mChooserMultiProfilePagerAdapter
+ .getActiveListAdapter()
+ .getSelectableServiceTargetCount();
+ }
+
+ @Override
+ public void updateDirectShareExpansion(DirectShareViewHolder directShareGroup) {
+ RecyclerView activeAdapterView =
+ mChooserMultiProfilePagerAdapter.getActiveAdapterView();
+ if (mResolverDrawerLayout.isCollapsed()) {
+ directShareGroup.collapse(activeAdapterView);
+ } else {
+ directShareGroup.expand(activeAdapterView);
+ }
+ }
+
+ @Override
+ public void handleScrollToExpandDirectShare(
+ DirectShareViewHolder directShareGroup, int y, int oldy) {
+ directShareGroup.handleScroll(
+ mChooserMultiProfilePagerAdapter.getActiveAdapterView(),
+ y,
+ oldy,
+ mMaxTargetsPerRow);
+ }
+ },
+ chooserListAdapter,
+ shouldShowContentPreview(),
+ mMaxTargetsPerRow,
+ getNumSheetExpansions());
}
@VisibleForTesting
@@ -1631,6 +1703,8 @@ public class ChooserActivity extends ResolverActivity implements
List<ResolveInfo> rList,
boolean filterLastUsed,
ResolverListController resolverListController,
+ UserHandle userHandle,
+ Intent targetIntent,
ChooserRequestParameters chooserRequest,
int maxTargetsPerRow) {
return new ChooserListAdapter(
@@ -1640,6 +1714,8 @@ public class ChooserActivity extends ResolverActivity implements
rList,
filterLastUsed,
resolverListController,
+ userHandle,
+ targetIntent,
this,
context.getPackageManager(),
getChooserActivityLogger(),
@@ -1889,8 +1965,7 @@ public class ChooserActivity extends ResolverActivity implements
.setupListAdapter(mChooserMultiProfilePagerAdapter.getCurrentPage());
}
- if (chooserListAdapter.mDisplayList == null
- || chooserListAdapter.mDisplayList.isEmpty()) {
+ if (chooserListAdapter.getDisplayResolveInfoCount() == 0) {
chooserListAdapter.notifyDataSetChanged();
} else {
chooserListAdapter.updateAlphabeticalList();
@@ -2191,15 +2266,63 @@ public class ChooserActivity extends ResolverActivity implements
* handled by {@link ChooserListAdapter}
*/
@VisibleForTesting
- public final class ChooserGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
- private final ChooserListAdapter mChooserListAdapter;
- private final LayoutInflater mLayoutInflater;
- private final boolean mShowAzLabelIfPoss;
-
- private DirectShareViewHolder mDirectShareViewHolder;
- private int mChooserTargetWidth = 0;
+ public static final class ChooserGridAdapter extends
+ RecyclerView.Adapter<RecyclerView.ViewHolder> {
- private int mFooterHeight = 0;
+ /**
+ * Injectable interface for any considerations that should be delegated to other components
+ * in the {@link ChooserActivity}.
+ * TODO: determine whether any of these methods return parameters that can safely be
+ * precomputed; whether any should be converted to `ChooserGridAdapter` setters to be
+ * invoked by external callbacks; and whether any reflect requirements that should be moved
+ * out of `ChooserGridAdapter` altogether.
+ */
+ interface ChooserActivityDelegate {
+ /** @return whether we're showing a tabbed (multi-profile) UI. */
+ boolean shouldShowTabs();
+
+ /**
+ * @return a content preview {@link View} that's appropriate for the caller's share
+ * content, constructed for display in the provided {@code parent} group.
+ */
+ View buildContentPreview(ViewGroup parent);
+
+ /** Notify the client that the item with the selected {@code itemIndex} was selected. */
+ void onTargetSelected(int itemIndex);
+
+ /**
+ * Notify the client that the item with the selected {@code itemIndex} was
+ * long-pressed.
+ */
+ void onTargetLongPressed(int itemIndex);
+
+ /**
+ * Notify the client that the provided {@code View} should be configured as the new
+ * "profile view" button. Callers should attach their own click listeners to implement
+ * behaviors on this view.
+ */
+ void updateProfileViewButton(View newButtonFromProfileRow);
+
+ /**
+ * @return the number of "valid" targets in the active list adapter.
+ * TODO: define "valid."
+ */
+ int getValidTargetCount();
+
+ /**
+ * Request that the client update our {@code directShareGroup} to match their desired
+ * state for the "expansion" UI.
+ */
+ void updateDirectShareExpansion(DirectShareViewHolder directShareGroup);
+
+ /**
+ * Request that the client handle a scroll event that should be taken as expanding the
+ * provided {@code directShareGroup}. Note that this currently never happens due to a
+ * hard-coded condition in {@link #canExpandDirectShare()}.
+ */
+ void handleScrollToExpandDirectShare(
+ DirectShareViewHolder directShareGroup, int y, int oldy);
+ }
private static final int VIEW_TYPE_DIRECT_SHARE = 0;
private static final int VIEW_TYPE_NORMAL = 1;
@@ -2211,12 +2334,44 @@ public class ChooserActivity extends ResolverActivity implements
private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
- ChooserGridAdapter(ChooserListAdapter wrappedAdapter) {
+ private final ChooserActivityDelegate mChooserActivityDelegate;
+ private final ChooserListAdapter mChooserListAdapter;
+ private final LayoutInflater mLayoutInflater;
+
+ private final int mMaxTargetsPerRow;
+ private final boolean mShouldShowContentPreview;
+ private final int mChooserWidthPixels;
+ private final int mChooserRowTextOptionTranslatePixelSize;
+ private final boolean mShowAzLabelIfPoss;
+
+ private DirectShareViewHolder mDirectShareViewHolder;
+ private int mChooserTargetWidth = 0;
+
+ private int mFooterHeight = 0;
+
+ ChooserGridAdapter(
+ Context context,
+ ChooserActivityDelegate chooserActivityDelegate,
+ ChooserListAdapter wrappedAdapter,
+ boolean shouldShowContentPreview,
+ int maxTargetsPerRow,
+ int numSheetExpansions) {
super();
+
+ mChooserActivityDelegate = chooserActivityDelegate;
+
mChooserListAdapter = wrappedAdapter;
- mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
+ mLayoutInflater = LayoutInflater.from(context);
+
+ mShouldShowContentPreview = shouldShowContentPreview;
+ mMaxTargetsPerRow = maxTargetsPerRow;
- mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
+ mChooserWidthPixels = context.getResources().getDimensionPixelSize(
+ R.dimen.chooser_width);
+ mChooserRowTextOptionTranslatePixelSize = context.getResources().getDimensionPixelSize(
+ R.dimen.chooser_row_text_option_translate);
+
+ mShowAzLabelIfPoss = numSheetExpansions < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
@@ -2249,7 +2404,7 @@ public class ChooserActivity extends ResolverActivity implements
}
// Limit width to the maximum width of the chooser activity
- int maxWidth = getResources().getDimensionPixelSize(R.dimen.chooser_width);
+ int maxWidth = mChooserWidthPixels;
width = Math.min(maxWidth, width);
int newWidth = width / mMaxTargetsPerRow;
@@ -2281,11 +2436,11 @@ public class ChooserActivity extends ResolverActivity implements
public int getSystemRowCount() {
// For the tabbed case we show the sticky content preview above the tabs,
// please refer to shouldShowStickyContentPreview
- if (shouldShowTabs()) {
+ if (mChooserActivityDelegate.shouldShowTabs()) {
return 0;
}
- if (!shouldShowContentPreview()) {
+ if (!mShouldShowContentPreview) {
return 0;
}
@@ -2297,7 +2452,7 @@ public class ChooserActivity extends ResolverActivity implements
}
public int getProfileRowCount() {
- if (shouldShowTabs()) {
+ if (mChooserActivityDelegate.shouldShowTabs()) {
return 0;
}
return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
@@ -2316,8 +2471,7 @@ public class ChooserActivity extends ResolverActivity implements
// There can be at most one row in the listview, that is internally
// a ViewGroup with 2 rows
public int getServiceTargetRowCount() {
- if (shouldShowContentPreview()
- && !ActivityManager.isLowRamDeviceStatic()) {
+ if (mShouldShowContentPreview && !ActivityManager.isLowRamDeviceStatic()) {
return 1;
}
return 0;
@@ -2346,7 +2500,7 @@ public class ChooserActivity extends ResolverActivity implements
switch (viewType) {
case VIEW_TYPE_CONTENT_PREVIEW:
return new ItemViewHolder(
- createContentPreviewView(parent, mPreviewCoordinator),
+ mChooserActivityDelegate.buildContentPreview(parent),
viewType,
null,
null);
@@ -2366,22 +2520,8 @@ public class ChooserActivity extends ResolverActivity implements
return new ItemViewHolder(
mChooserListAdapter.createView(parent),
viewType,
- selectedPosition -> startSelected(
- selectedPosition,
- /* always= */ false,
- /* filtered= */ true),
- selectedPosition -> {
- final TargetInfo longPressedTargetInfo =
- mChooserMultiProfilePagerAdapter
- .getActiveListAdapter()
- .targetInfoForPosition(
- selectedPosition, /* filtered= */ true);
- // ItemViewHolder contents should always be "display resolve info"
- // targets, but check just to make sure.
- if (longPressedTargetInfo.isDisplayResolveInfo()) {
- showTargetDetails(longPressedTargetInfo);
- }
- });
+ mChooserActivityDelegate::onTargetSelected,
+ mChooserActivityDelegate::onTargetLongPressed);
case VIEW_TYPE_DIRECT_SHARE:
case VIEW_TYPE_CALLER_AND_RANK:
return createItemGroupViewHolder(viewType, parent);
@@ -2441,9 +2581,7 @@ public class ChooserActivity extends ResolverActivity implements
private View createProfileView(ViewGroup parent) {
View profileRow = mLayoutInflater.inflate(R.layout.chooser_profile_row, parent, false);
- mProfileView = profileRow.findViewById(com.android.internal.R.id.profile_button);
- mProfileView.setOnClickListener(ChooserActivity.this::onProfileClick);
- updateProfileViewButton();
+ mChooserActivityDelegate.updateProfileViewButton(profileRow);
return profileRow;
}
@@ -2465,15 +2603,13 @@ public class ChooserActivity extends ResolverActivity implements
v.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- startSelected(holder.getItemIndex(column), false, true);
+ mChooserActivityDelegate.onTargetSelected(holder.getItemIndex(column));
}
});
// Show menu for both direct share and app share targets after long click.
v.setOnLongClickListener(v1 -> {
- TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
- holder.getItemIndex(column), true);
- showTargetDetails(ti);
+ mChooserActivityDelegate.onTargetLongPressed(holder.getItemIndex(column));
return true;
});
@@ -2534,7 +2670,7 @@ public class ChooserActivity extends ResolverActivity implements
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType,
- mChooserMultiProfilePagerAdapter::getActiveListAdapter);
+ mChooserActivityDelegate::getValidTargetCount);
loadViewsIntoGroup(mDirectShareViewHolder);
return mDirectShareViewHolder;
@@ -2600,9 +2736,7 @@ public class ChooserActivity extends ResolverActivity implements
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(textView, "alpha", 0.0f, 1.0f);
fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f));
- float translationInPx = getResources().getDimensionPixelSize(
- R.dimen.chooser_row_text_option_translate);
- textView.setTranslationY(translationInPx);
+ textView.setTranslationY(mChooserRowTextOptionTranslatePixelSize);
ValueAnimator translateAnim = ObjectAnimator.ofFloat(textView, "translationY",
0.0f);
translateAnim.setInterpolator(new DecelerateInterpolator(1.0f));
@@ -2654,9 +2788,8 @@ public class ChooserActivity extends ResolverActivity implements
public void handleScroll(View v, int y, int oldy) {
boolean canExpandDirectShare = canExpandDirectShare();
if (mDirectShareViewHolder != null && canExpandDirectShare) {
- mDirectShareViewHolder.handleScroll(
- mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
- mMaxTargetsPerRow);
+ mChooserActivityDelegate.handleScrollToExpandDirectShare(
+ mDirectShareViewHolder, y, oldy);
}
}
@@ -2681,13 +2814,7 @@ public class ChooserActivity extends ResolverActivity implements
if (mDirectShareViewHolder == null || !canExpandDirectShare()) {
return;
}
- RecyclerView activeAdapterView =
- mChooserMultiProfilePagerAdapter.getActiveAdapterView();
- if (mResolverDrawerLayout.isCollapsed()) {
- mDirectShareViewHolder.collapse(activeAdapterView);
- } else {
- mDirectShareViewHolder.expand(activeAdapterView);
- }
+ mChooserActivityDelegate.updateDirectShareExpansion(mDirectShareViewHolder);
}
}
diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java
index b18d2718..59d1a6e3 100644
--- a/java/src/com/android/intentresolver/ChooserListAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserListAdapter.java
@@ -37,6 +37,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Trace;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
@@ -67,8 +68,6 @@ public class ChooserListAdapter extends ResolverListAdapter {
private static final String TAG = "ChooserListAdapter";
private static final boolean DEBUG = false;
- private boolean mEnableStackedApps = true;
-
public static final int NO_POSITION = -1;
public static final int TARGET_BAD = -1;
public static final int TARGET_CALLER = 0;
@@ -95,11 +94,11 @@ public class ChooserListAdapter extends ResolverListAdapter {
private final List<TargetInfo> mServiceTargets = new ArrayList<>();
private final List<DisplayResolveInfo> mCallerTargets = new ArrayList<>();
+ private final ShortcutSelectionLogic mShortcutSelectionLogic;
+
// Sorted list of DisplayResolveInfos for the alphabetical app section.
private List<DisplayResolveInfo> mSortedList = new ArrayList<>();
- private final ShortcutSelectionLogic mShortcutSelectionLogic;
-
// For pinned direct share labels, if the text spans multiple lines, the TextView will consume
// the full width, even if the characters actually take up less than that. Measure the actual
// line widths and constrain the View's width based upon that so that the pin doesn't end up
@@ -138,6 +137,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
List<ResolveInfo> rList,
boolean filterLastUsed,
ResolverListController resolverListController,
+ UserHandle userHandle,
+ Intent targetIntent,
ResolverListCommunicator resolverListCommunicator,
PackageManager packageManager,
ChooserActivityLogger chooserActivityLogger,
@@ -145,8 +146,17 @@ public class ChooserListAdapter extends ResolverListAdapter {
int maxRankedTargets) {
// Don't send the initial intents through the shared ResolverActivity path,
// we want to separate them into a different section.
- super(context, payloadIntents, null, rList, filterLastUsed,
- resolverListController, resolverListCommunicator, false);
+ super(
+ context,
+ payloadIntents,
+ null,
+ rList,
+ filterLastUsed,
+ resolverListController,
+ userHandle,
+ targetIntent,
+ resolverListCommunicator,
+ false);
mChooserRequest = chooserRequest;
mMaxRankedTargets = maxRankedTargets;
@@ -334,11 +344,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
@Override
protected List<DisplayResolveInfo> doInBackground(Void... voids) {
List<DisplayResolveInfo> allTargets = new ArrayList<>();
- allTargets.addAll(mDisplayList);
+ allTargets.addAll(getTargetsInCurrentDisplayList());
allTargets.addAll(mCallerTargets);
- if (!mEnableStackedApps) {
- return allTargets;
- }
// Consolidate multiple targets from same app.
return allTargets
@@ -408,7 +415,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
int getAlphaTargetCount() {
int groupedCount = mSortedList.size();
- int ungroupedCount = mCallerTargets.size() + mDisplayList.size();
+ int ungroupedCount = mCallerTargets.size() + getDisplayResolveInfoCount();
return (ungroupedCount > mMaxRankedTargets) ? groupedCount : 0;
}
diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java
index fece8d3d..adcfecef 100644
--- a/java/src/com/android/intentresolver/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/ResolverActivity.java
@@ -107,7 +107,6 @@ import com.android.intentresolver.AbstractMultiProfilePagerAdapter.OnSwitchOnWor
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.Profile;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.QuietModeManager;
import com.android.intentresolver.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
-import com.android.intentresolver.chooser.ChooserTargetInfo;
import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.TargetInfo;
import com.android.intentresolver.widget.ResolverDrawerLayout;
@@ -123,6 +122,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Supplier;
/**
* This activity is displayed when the system attempts to start an Intent for
@@ -223,7 +223,11 @@ public class ResolverActivity extends FragmentActivity implements
private BroadcastReceiver mWorkProfileStateReceiver;
private UserHandle mHeaderCreatorUser;
- private UserHandle mWorkProfileUserHandle;
+ private Supplier<UserHandle> mLazyWorkProfileUserHandle = () -> {
+ final UserHandle result = fetchWorkProfileUserProfile();
+ mLazyWorkProfileUserHandle = () -> result;
+ return result;
+ };
@Nullable
private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
@@ -408,7 +412,6 @@ public class ResolverActivity extends FragmentActivity implements
mDefaultTitleResId = defaultTitleRes;
mSupportsAlwaysUseOption = supportsAlwaysUseOption;
- mWorkProfileUserHandle = fetchWorkProfileUserProfile();
// The last argument of createResolverListAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
@@ -699,19 +702,25 @@ public class ResolverActivity extends FragmentActivity implements
protected UserHandle getPersonalProfileUserHandle() {
return UserHandle.of(ActivityManager.getCurrentUser());
}
- protected @Nullable UserHandle getWorkProfileUserHandle() {
- return mWorkProfileUserHandle;
+
+ @Nullable
+ protected UserHandle getWorkProfileUserHandle() {
+ return mLazyWorkProfileUserHandle.get();
}
- protected @Nullable UserHandle fetchWorkProfileUserProfile() {
- mWorkProfileUserHandle = null;
+ @Nullable
+ private UserHandle fetchWorkProfileUserProfile() {
UserManager userManager = getSystemService(UserManager.class);
+ if (userManager == null) {
+ return null;
+ }
+ UserHandle result = null;
for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) {
if (userInfo.isManagedProfile()) {
- mWorkProfileUserHandle = userInfo.getUserHandle();
+ result = userInfo.getUserHandle();
}
}
- return mWorkProfileUserHandle;
+ return result;
}
private boolean hasWorkProfile() {
@@ -851,7 +860,6 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- @Override // SelectableTargetInfoCommunicator ResolverListCommunicator
public Intent getTargetIntent() {
return mIntents.isEmpty() ? null : mIntents.get(0);
}
@@ -1532,8 +1540,16 @@ public class ResolverActivity extends FragmentActivity implements
Intent startIntent = getIntent();
boolean isAudioCaptureDevice =
startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
- return new ResolverListAdapter(context, payloadIntents, initialIntents, rList,
- filterLastUsed, createListController(userHandle), this,
+ return new ResolverListAdapter(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ createListController(userHandle),
+ userHandle,
+ getTargetIntent(),
+ this,
isAudioCaptureDevice);
}
@@ -1597,12 +1613,13 @@ public class ResolverActivity extends FragmentActivity implements
setContentView(mLayoutId);
DisplayResolveInfo sameProfileResolveInfo =
- mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0);
+ mMultiProfilePagerAdapter.getActiveListAdapter().getFirstDisplayResolveInfo();
boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK;
final ResolverListAdapter inactiveAdapter =
mMultiProfilePagerAdapter.getInactiveListAdapter();
- final DisplayResolveInfo otherProfileResolveInfo = inactiveAdapter.mDisplayList.get(0);
+ final DisplayResolveInfo otherProfileResolveInfo =
+ inactiveAdapter.getFirstDisplayResolveInfo();
// Load the icon asynchronously
ImageView icon = findViewById(com.android.internal.R.id.icon);
@@ -1653,31 +1670,29 @@ public class ResolverActivity extends FragmentActivity implements
|| mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
return false;
}
- List<DisplayResolveInfo> sameProfileList =
- mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList;
- List<DisplayResolveInfo> otherProfileList =
- mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList;
+ ResolverListAdapter sameProfileAdapter =
+ mMultiProfilePagerAdapter.getActiveListAdapter();
+ ResolverListAdapter otherProfileAdapter =
+ mMultiProfilePagerAdapter.getInactiveListAdapter();
- if (sameProfileList.isEmpty()) {
+ if (sameProfileAdapter.getDisplayResolveInfoCount() == 0) {
Log.d(TAG, "No targets in the current profile");
return false;
}
- if (otherProfileList.size() != 1) {
- Log.d(TAG, "Found " + otherProfileList.size() + " resolvers in the other profile");
+ if (otherProfileAdapter.getDisplayResolveInfoCount() != 1) {
+ Log.d(TAG, "Other-profile count: " + otherProfileAdapter.getDisplayResolveInfoCount());
return false;
}
- if (otherProfileList.get(0).getResolveInfo().handleAllWebDataURI) {
+ if (otherProfileAdapter.allResolveInfosHandleAllWebDataUri()) {
Log.d(TAG, "Other profile is a web browser");
return false;
}
- for (DisplayResolveInfo info : sameProfileList) {
- if (!info.getResolveInfo().handleAllWebDataURI) {
- Log.d(TAG, "Non-browser found in this profile");
- return false;
- }
+ if (!sameProfileAdapter.allResolveInfosHandleAllWebDataUri()) {
+ Log.d(TAG, "Non-browser found in this profile");
+ return false;
}
return true;
diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java
index d97191c6..9f654594 100644
--- a/java/src/com/android/intentresolver/ResolverListAdapter.java
+++ b/java/src/com/android/intentresolver/ResolverListAdapter.java
@@ -56,6 +56,8 @@ import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.TargetInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -65,37 +67,48 @@ import java.util.Map;
public class ResolverListAdapter extends BaseAdapter {
private static final String TAG = "ResolverListAdapter";
+ @Nullable // TODO: other model for lazy computation? Or just precompute?
+ private static ColorMatrixColorFilter sSuspendedMatrixColorFilter;
+
+ protected final Context mContext;
+ protected final LayoutInflater mInflater;
+ protected final ResolverListCommunicator mResolverListCommunicator;
+ protected final ResolverListController mResolverListController;
+
private final List<Intent> mIntents;
private final Intent[] mInitialIntents;
private final List<ResolveInfo> mBaseResolveList;
private final PackageManager mPm;
- protected final Context mContext;
- private static ColorMatrixColorFilter sSuspendedMatrixColorFilter;
private final int mIconDpi;
- protected ResolveInfo mLastChosen;
+ private final boolean mIsAudioCaptureDevice;
+ private final UserHandle mUserHandle;
+ private final Intent mTargetIntent;
+
+ private final Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
+ private final Map<DisplayResolveInfo, LoadLabelTask> mLabelLoaders = new HashMap<>();
+
+ private ResolveInfo mLastChosen;
private DisplayResolveInfo mOtherProfile;
- ResolverListController mResolverListController;
private int mPlaceholderCount;
- protected final LayoutInflater mInflater;
-
// This one is the list that the Adapter will actually present.
- List<DisplayResolveInfo> mDisplayList;
+ private List<DisplayResolveInfo> mDisplayList;
private List<ResolvedComponentInfo> mUnfilteredResolveList;
private int mLastChosenPosition = -1;
private boolean mFilterLastUsed;
- final ResolverListCommunicator mResolverListCommunicator;
private Runnable mPostListReadyRunnable;
- private final boolean mIsAudioCaptureDevice;
private boolean mIsTabLoaded;
- private final Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
- private final Map<DisplayResolveInfo, LoadLabelTask> mLabelLoaders = new HashMap<>();
- public ResolverListAdapter(Context context, List<Intent> payloadIntents,
- Intent[] initialIntents, List<ResolveInfo> rList,
+ public ResolverListAdapter(
+ Context context,
+ List<Intent> payloadIntents,
+ Intent[] initialIntents,
+ List<ResolveInfo> rList,
boolean filterLastUsed,
ResolverListController resolverListController,
+ UserHandle userHandle,
+ Intent targetIntent,
ResolverListCommunicator resolverListCommunicator,
boolean isAudioCaptureDevice) {
mContext = context;
@@ -107,12 +120,22 @@ public class ResolverListAdapter extends BaseAdapter {
mDisplayList = new ArrayList<>();
mFilterLastUsed = filterLastUsed;
mResolverListController = resolverListController;
+ mUserHandle = userHandle;
+ mTargetIntent = targetIntent;
mResolverListCommunicator = resolverListCommunicator;
mIsAudioCaptureDevice = isAudioCaptureDevice;
final ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
mIconDpi = am.getLauncherLargeIconDensity();
}
+ public final DisplayResolveInfo getFirstDisplayResolveInfo() {
+ return mDisplayList.get(0);
+ }
+
+ public final ImmutableList<DisplayResolveInfo> getTargetsInCurrentDisplayList() {
+ return ImmutableList.copyOf(mDisplayList);
+ }
+
public void handlePackagesChanged() {
mResolverListCommunicator.onHandlePackagesChanged(this);
}
@@ -262,7 +285,7 @@ public class ResolverListAdapter extends BaseAdapter {
if (mBaseResolveList != null) {
List<ResolvedComponentInfo> currentResolveList = new ArrayList<>();
mResolverListController.addResolveListDedupe(currentResolveList,
- mResolverListCommunicator.getTargetIntent(),
+ mTargetIntent,
mBaseResolveList);
return currentResolveList;
} else {
@@ -338,7 +361,12 @@ public class ResolverListAdapter extends BaseAdapter {
if (otherProfileInfo != null) {
mOtherProfile = makeOtherProfileDisplayResolveInfo(
- mContext, otherProfileInfo, mPm, mResolverListCommunicator, mIconDpi);
+ mContext,
+ otherProfileInfo,
+ mPm,
+ mTargetIntent,
+ mResolverListCommunicator,
+ mIconDpi);
} else {
mOtherProfile = null;
try {
@@ -499,7 +527,7 @@ public class ResolverListAdapter extends BaseAdapter {
final Intent replaceIntent =
mResolverListCommunicator.getReplacementIntent(add.activityInfo, intent);
final Intent defaultIntent = mResolverListCommunicator.getReplacementIntent(
- add.activityInfo, mResolverListCommunicator.getTargetIntent());
+ add.activityInfo, mTargetIntent);
final DisplayResolveInfo dri = DisplayResolveInfo.newDisplayResolveInfo(
intent,
add,
@@ -608,11 +636,15 @@ public class ResolverListAdapter extends BaseAdapter {
return position;
}
- public int getDisplayResolveInfoCount() {
+ public final int getDisplayResolveInfoCount() {
return mDisplayList.size();
}
- public DisplayResolveInfo getDisplayResolveInfo(int index) {
+ public final boolean allResolveInfosHandleAllWebDataUri() {
+ return mDisplayList.stream().allMatch(t -> t.getResolveInfo().handleAllWebDataURI);
+ }
+
+ public final DisplayResolveInfo getDisplayResolveInfo(int index) {
// Used to query services. We only query services for primary targets, not alternates.
return mDisplayList.get(index);
}
@@ -765,7 +797,7 @@ public class ResolverListAdapter extends BaseAdapter {
}
public UserHandle getUserHandle() {
- return mResolverListController.getUserHandle();
+ return mUserHandle;
}
protected List<ResolvedComponentInfo> getResolversForUser(UserHandle userHandle) {
@@ -821,6 +853,7 @@ public class ResolverListAdapter extends BaseAdapter {
Context context,
ResolvedComponentInfo resolvedComponentInfo,
PackageManager pm,
+ Intent targetIntent,
ResolverListCommunicator resolverListCommunicator,
int iconDpi) {
ResolveInfo resolveInfo = resolvedComponentInfo.getResolveInfoAt(0);
@@ -829,8 +862,7 @@ public class ResolverListAdapter extends BaseAdapter {
resolveInfo.activityInfo,
resolvedComponentInfo.getIntentAt(0));
Intent replacementIntent = resolverListCommunicator.getReplacementIntent(
- resolveInfo.activityInfo,
- resolverListCommunicator.getTargetIntent());
+ resolveInfo.activityInfo, targetIntent);
ResolveInfoPresentationGetter presentationGetter =
new ResolveInfoPresentationGetter(context, iconDpi, resolveInfo);
@@ -871,8 +903,6 @@ public class ResolverListAdapter extends BaseAdapter {
*/
default boolean shouldGetOnlyDefaultActivities() { return true; };
- Intent getTargetIntent();
-
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
}
diff --git a/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java b/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java
index 95c61e3a..cfd54697 100644
--- a/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java
+++ b/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java
@@ -28,7 +28,6 @@ import android.view.animation.AccelerateInterpolator;
import androidx.recyclerview.widget.RecyclerView;
import com.android.intentresolver.ChooserActivity;
-import com.android.intentresolver.ChooserListAdapter;
import java.util.Arrays;
import java.util.List;
@@ -47,14 +46,14 @@ public class DirectShareViewHolder extends ItemGroupViewHolder {
private final boolean[] mCellVisibility;
- private final Supplier<ChooserListAdapter> mListAdapterSupplier;
+ private final Supplier<Integer> mDeferredTargetCountSupplier;
public DirectShareViewHolder(
ViewGroup parent,
List<ViewGroup> rows,
int cellCountPerRow,
int viewType,
- Supplier<ChooserListAdapter> listAdapterSupplier) {
+ Supplier<Integer> deferredTargetCountSupplier) {
super(rows.size() * cellCountPerRow, parent, viewType);
this.mParent = parent;
@@ -62,7 +61,7 @@ public class DirectShareViewHolder extends ItemGroupViewHolder {
this.mCellCountPerRow = cellCountPerRow;
this.mCellVisibility = new boolean[rows.size() * cellCountPerRow];
Arrays.fill(mCellVisibility, true);
- this.mListAdapterSupplier = listAdapterSupplier;
+ this.mDeferredTargetCountSupplier = deferredTargetCountSupplier;
}
public ViewGroup addView(int index, View v) {
@@ -136,8 +135,7 @@ public class DirectShareViewHolder extends ItemGroupViewHolder {
// only expand if we have more than maxTargetsPerRow, and delay that decision
// until they start to scroll
- ChooserListAdapter adapter = mListAdapterSupplier.get();
- int validTargets = adapter.getSelectableServiceTargetCount();
+ final int validTargets = this.mDeferredTargetCountSupplier.get();
if (validTargets <= maxTargetsPerRow) {
mHideDirectShareExpansion = true;
return;
diff --git a/java/src/com/android/intentresolver/widget/ResolverDrawerLayout.java b/java/src/com/android/intentresolver/widget/ResolverDrawerLayout.java
index 29821e66..a2c5afc6 100644
--- a/java/src/com/android/intentresolver/widget/ResolverDrawerLayout.java
+++ b/java/src/com/android/intentresolver/widget/ResolverDrawerLayout.java
@@ -17,6 +17,9 @@
package com.android.intentresolver.widget;
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.IdRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -98,6 +101,8 @@ public class ResolverDrawerLayout extends ViewGroup {
private int mTopOffset;
private boolean mShowAtTop;
+ @IdRes
+ private int mIgnoreOffsetTopLimitViewId = ID_NULL;
private boolean mIsDragging;
private boolean mOpenOnClick;
@@ -158,6 +163,10 @@ public class ResolverDrawerLayout extends ViewGroup {
mIsMaxCollapsedHeightSmallExplicit =
a.hasValue(R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall);
mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
+ if (a.hasValue(R.styleable.ResolverDrawerLayout_ignoreOffsetTopLimit)) {
+ mIgnoreOffsetTopLimitViewId = a.getResourceId(
+ R.styleable.ResolverDrawerLayout_ignoreOffsetTopLimit, ID_NULL);
+ }
a.recycle();
mScrollIndicatorDrawable = mContext.getDrawable(
@@ -580,12 +589,32 @@ public class ResolverDrawerLayout extends ViewGroup {
dy -= 1.0f;
}
+ boolean isIgnoreOffsetLimitSet = false;
+ int ignoreOffsetLimit = 0;
+ View ignoreOffsetLimitView = findIgnoreOffsetLimitView();
+ if (ignoreOffsetLimitView != null) {
+ LayoutParams lp = (LayoutParams) ignoreOffsetLimitView.getLayoutParams();
+ ignoreOffsetLimit = ignoreOffsetLimitView.getBottom() + lp.bottomMargin;
+ isIgnoreOffsetLimitSet = true;
+ }
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() == View.GONE) {
+ continue;
+ }
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.ignoreOffset) {
child.offsetTopAndBottom((int) dy);
+ } else if (isIgnoreOffsetLimitSet) {
+ int top = child.getTop();
+ int targetTop = Math.max(
+ (int) (ignoreOffsetLimit + lp.topMargin + dy),
+ lp.mFixedTop);
+ if (top != targetTop) {
+ child.offsetTopAndBottom(targetTop - top);
+ }
+ ignoreOffsetLimit = child.getBottom() + lp.bottomMargin;
}
}
final boolean isCollapsedOld = mCollapseOffset != 0;
@@ -1027,6 +1056,8 @@ public class ResolverDrawerLayout extends ViewGroup {
final int rightEdge = width - getPaddingRight();
final int widthAvailable = rightEdge - leftEdge;
+ boolean isIgnoreOffsetLimitSet = false;
+ int ignoreOffsetLimit = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
@@ -1039,9 +1070,24 @@ public class ResolverDrawerLayout extends ViewGroup {
continue;
}
+ if (mIgnoreOffsetTopLimitViewId != ID_NULL && !isIgnoreOffsetLimitSet) {
+ if (mIgnoreOffsetTopLimitViewId == child.getId()) {
+ ignoreOffsetLimit = child.getBottom() + lp.bottomMargin;
+ isIgnoreOffsetLimitSet = true;
+ }
+ }
+
int top = ypos + lp.topMargin;
if (lp.ignoreOffset) {
- top -= mCollapseOffset;
+ if (!isDragging()) {
+ lp.mFixedTop = (int) (top - mCollapseOffset);
+ }
+ if (isIgnoreOffsetLimitSet) {
+ top = Math.max(ignoreOffsetLimit + lp.topMargin, (int) (top - mCollapseOffset));
+ ignoreOffsetLimit = top + child.getMeasuredHeight() + lp.bottomMargin;
+ } else {
+ top -= mCollapseOffset;
+ }
}
final int bottom = top + child.getMeasuredHeight();
@@ -1105,11 +1151,23 @@ public class ResolverDrawerLayout extends ViewGroup {
mCollapsibleHeightReserved = ss.mCollapsibleHeightReserved;
}
+ private View findIgnoreOffsetLimitView() {
+ if (mIgnoreOffsetTopLimitViewId == ID_NULL) {
+ return null;
+ }
+ View v = findViewById(mIgnoreOffsetTopLimitViewId);
+ if (v != null && v != this && v.getParent() == this && v.getVisibility() != View.GONE) {
+ return v;
+ }
+ return null;
+ }
+
public static class LayoutParams extends MarginLayoutParams {
public boolean alwaysShow;
public boolean ignoreOffset;
public boolean hasNestedScrollIndicator;
public int maxHeight;
+ int mFixedTop;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt
index d054e7fa..6b34f8b9 100644
--- a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt
+++ b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt
@@ -56,6 +56,8 @@ class ChooserListAdapterTest {
emptyList(),
false,
resolverListController,
+ null,
+ Intent(),
mock(),
packageManager,
chooserActivityLogger,
diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
index fe448d63..8c842786 100644
--- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
+++ b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
@@ -73,6 +73,8 @@ public class ChooserWrapperActivity
List<ResolveInfo> rList,
boolean filterLastUsed,
ResolverListController resolverListController,
+ UserHandle userHandle,
+ Intent targetIntent,
ChooserRequestParameters chooserRequest,
int maxTargetsPerRow) {
PackageManager packageManager =
@@ -85,6 +87,8 @@ public class ChooserWrapperActivity
rList,
filterLastUsed,
resolverListController,
+ userHandle,
+ targetIntent,
this,
packageManager,
getChooserActivityLogger(),
diff --git a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
index 7d4b07d8..239bffe0 100644
--- a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
+++ b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
@@ -59,8 +59,16 @@ public class ResolverWrapperActivity extends ResolverActivity {
public ResolverListAdapter createResolverListAdapter(Context context,
List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
boolean filterLastUsed, UserHandle userHandle) {
- return new ResolverWrapperAdapter(context, payloadIntents, initialIntents, rList,
- filterLastUsed, createListController(userHandle), this);
+ return new ResolverWrapperAdapter(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ createListController(userHandle),
+ userHandle,
+ payloadIntents.get(0), // TODO: extract upstream
+ this);
}
@Override
diff --git a/java/tests/src/com/android/intentresolver/ResolverWrapperAdapter.java b/java/tests/src/com/android/intentresolver/ResolverWrapperAdapter.java
index 1504a8ab..a53b41d1 100644
--- a/java/tests/src/com/android/intentresolver/ResolverWrapperAdapter.java
+++ b/java/tests/src/com/android/intentresolver/ResolverWrapperAdapter.java
@@ -19,6 +19,7 @@ package com.android.intentresolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
import androidx.test.espresso.idling.CountingIdlingResource;
@@ -31,14 +32,27 @@ public class ResolverWrapperAdapter extends ResolverListAdapter {
private CountingIdlingResource mLabelIdlingResource =
new CountingIdlingResource("LoadLabelTask");
- public ResolverWrapperAdapter(Context context,
+ public ResolverWrapperAdapter(
+ Context context,
List<Intent> payloadIntents,
Intent[] initialIntents,
- List<ResolveInfo> rList, boolean filterLastUsed,
+ List<ResolveInfo> rList,
+ boolean filterLastUsed,
ResolverListController resolverListController,
+ UserHandle userHandle,
+ Intent targetIntent,
ResolverListCommunicator resolverListCommunicator) {
- super(context, payloadIntents, initialIntents, rList, filterLastUsed,
- resolverListController, resolverListCommunicator, false);
+ super(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ resolverListController,
+ userHandle,
+ targetIntent,
+ resolverListCommunicator,
+ false);
}
public CountingIdlingResource getLabelIdlingResource() {
diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
index da72a749..28b68530 100644
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
+++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
@@ -78,6 +78,7 @@ import android.graphics.Paint;
import android.graphics.drawable.Icon;
import android.metrics.LogMaker;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
@@ -1636,6 +1637,90 @@ public class UnbundledChooserActivityTest {
}
@Test
+ public void testLaunchWithCallerProvidedTarget() {
+ setDeviceConfigProperty(
+ SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
+ Boolean.toString(false));
+ // Set up resources
+ ChooserActivityOverrideData.getInstance().resources = Mockito.spy(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources());
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resources
+ .getInteger(R.integer.config_maxShortcutTargetsPerApp))
+ .thenReturn(1);
+
+ // We need app targets for direct targets to get displayed
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
+
+ // set caller-provided target
+ Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null);
+ String callerTargetLabel = "Caller Target";
+ ChooserTarget[] targets = new ChooserTarget[] {
+ new ChooserTarget(
+ callerTargetLabel,
+ Icon.createWithBitmap(createBitmap()),
+ 0.1f,
+ resolvedComponentInfos.get(0).name,
+ new Bundle())
+ };
+ chooserIntent.putExtra(Intent.EXTRA_CHOOSER_TARGETS, targets);
+
+ // create test shortcut loader factory, remember loaders and their callbacks
+ SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
+ createShortcutLoaderFactory();
+
+ // Start activity
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(chooserIntent);
+ waitForIdle();
+
+ // verify that ShortcutLoader was queried
+ ArgumentCaptor<DisplayResolveInfo[]> appTargets =
+ ArgumentCaptor.forClass(DisplayResolveInfo[].class);
+ verify(shortcutLoaders.get(0).first, times(1)).queryShortcuts(appTargets.capture());
+
+ // send shortcuts
+ assertThat(
+ "Wrong number of app targets",
+ appTargets.getValue().length,
+ is(resolvedComponentInfos.size()));
+ ShortcutLoader.Result result = new ShortcutLoader.Result(
+ true,
+ appTargets.getValue(),
+ new ShortcutLoader.ShortcutResultInfo[0],
+ new HashMap<>(),
+ new HashMap<>());
+ activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result));
+ waitForIdle();
+
+ final ChooserListAdapter activeAdapter = activity.getAdapter();
+ assertThat(
+ "Chooser should have 3 targets (2 apps, 1 direct)",
+ activeAdapter.getCount(),
+ is(3));
+ assertThat(
+ "Chooser should have exactly two selectable direct target",
+ activeAdapter.getSelectableServiceTargetCount(),
+ is(1));
+ assertThat(
+ "The display label must match",
+ activeAdapter.getItem(0).getDisplayLabel(),
+ is(callerTargetLabel));
+ }
+
+ @Test
public void testUpdateMaxTargetsPerRow_columnCountIsUpdated() throws InterruptedException {
updateMaxTargetsPerRowResource(/* targetsPerRow= */ 4);
givenAppTargets(/* appCount= */ 16);