summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Joshua Trask <joshtrask@google.com> 2022-11-21 12:38:14 -0500
committer Joshua Trask <joshtrask@google.com> 2022-12-13 20:54:04 +0000
commitd5eb50ac083b03edf84c904e2ec16acb6ca50fdd (patch)
tree42079a7fd5391d981b23ed5a84fdc1316cc38d94 /java/src
parent92269439a35e7bc3b81c7e7efdef7ae0b63a6637 (diff)
Extract ChooserGridAdapter.
As of ag/20463973 there's no major design changes required to lift this out of being an inner class, and this combines with ag/20455546 to pull *most* UI considerations out of `ChooserActivity` (namely, any that involve our "grid" except where those considerations are bridged across the `ChooserGridAdapter.ChooserActivityDelegate`). The testing changes were probably part of another CL that got included here accidentally, but they're improvements we can go ahead with anyways (switching some uses of mocks to real objects). Test: atest IntentResolverUnitTests Bug: 202167050 Change-Id: I4948bcd1fa58d4dbe44f7aef009db5f8864882de
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java581
-rw-r--r--java/src/com/android/intentresolver/ChooserListAdapter.java2
-rw-r--r--java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java27
-rw-r--r--java/src/com/android/intentresolver/grid/ChooserGridAdapter.java604
-rw-r--r--java/src/com/android/intentresolver/grid/DirectShareViewHolder.java2
5 files changed, 621 insertions, 595 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index c3864480..4682ec50 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -26,9 +26,6 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -58,7 +55,6 @@ import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
-import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.drawable.Drawable;
@@ -85,18 +81,14 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
-import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.MainThread;
@@ -111,12 +103,8 @@ import com.android.intentresolver.ResolverListAdapter.ViewHolder;
import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.MultiDisplayResolveInfo;
import com.android.intentresolver.chooser.TargetInfo;
+import com.android.intentresolver.grid.ChooserGridAdapter;
import com.android.intentresolver.grid.DirectShareViewHolder;
-import com.android.intentresolver.grid.FooterViewHolder;
-import com.android.intentresolver.grid.ItemGroupViewHolder;
-import com.android.intentresolver.grid.ItemViewHolder;
-import com.android.intentresolver.grid.SingleRowViewHolder;
-import com.android.intentresolver.grid.ViewHolderBase;
import com.android.intentresolver.model.AbstractResolverComparator;
import com.android.intentresolver.model.AppPredictionServiceResolverComparator;
import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;
@@ -130,8 +118,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
-import com.google.android.collect.Lists;
-
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -220,12 +206,6 @@ public class ChooserActivity extends ResolverActivity implements
@Retention(RetentionPolicy.SOURCE)
public @interface ShareTargetType {}
- /**
- * The transition time between placeholders for direct share to a message
- * indicating that non are available.
- */
- public static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200;
-
public static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7;
@@ -2260,565 +2240,6 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- /**
- * Adapter for all types of items and targets in ShareSheet.
- * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
- * row level by this adapter but not on the item level. Individual targets within the row are
- * handled by {@link ChooserListAdapter}
- */
- @VisibleForTesting
- public static final class ChooserGridAdapter extends
- RecyclerView.Adapter<RecyclerView.ViewHolder> {
-
- /**
- * 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;
- private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
- private static final int VIEW_TYPE_PROFILE = 3;
- private static final int VIEW_TYPE_AZ_LABEL = 4;
- private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
- private static final int VIEW_TYPE_FOOTER = 6;
-
- private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
-
- 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(context);
-
- mShouldShowContentPreview = shouldShowContentPreview;
- mMaxTargetsPerRow = maxTargetsPerRow;
-
- 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
- public void onChanged() {
- super.onChanged();
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- notifyDataSetChanged();
- }
- });
- }
-
- public void setFooterHeight(int height) {
- mFooterHeight = height;
- }
-
- /**
- * Calculate the chooser target width to maximize space per item
- *
- * @param width The new row width to use for recalculation
- * @return true if the view width has changed
- */
- public boolean calculateChooserTargetWidth(int width) {
- if (width == 0) {
- return false;
- }
-
- // Limit width to the maximum width of the chooser activity
- int maxWidth = mChooserWidthPixels;
- width = Math.min(maxWidth, width);
-
- int newWidth = width / mMaxTargetsPerRow;
- if (newWidth != mChooserTargetWidth) {
- mChooserTargetWidth = newWidth;
- return true;
- }
-
- return false;
- }
-
- public int getRowCount() {
- return (int) (
- getSystemRowCount()
- + getProfileRowCount()
- + getServiceTargetRowCount()
- + getCallerAndRankedTargetRowCount()
- + getAzLabelRowCount()
- + Math.ceil(
- (float) mChooserListAdapter.getAlphaTargetCount()
- / mMaxTargetsPerRow)
- );
- }
-
- /**
- * Whether the "system" row of targets is displayed.
- * This area includes the content preview (if present) and action row.
- */
- public int getSystemRowCount() {
- // For the tabbed case we show the sticky content preview above the tabs,
- // please refer to shouldShowStickyContentPreview
- if (mChooserActivityDelegate.shouldShowTabs()) {
- return 0;
- }
-
- if (!mShouldShowContentPreview) {
- return 0;
- }
-
- if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) {
- return 0;
- }
-
- return 1;
- }
-
- public int getProfileRowCount() {
- if (mChooserActivityDelegate.shouldShowTabs()) {
- return 0;
- }
- return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
- }
-
- public int getFooterRowCount() {
- return 1;
- }
-
- public int getCallerAndRankedTargetRowCount() {
- return (int) Math.ceil(
- ((float) mChooserListAdapter.getCallerTargetCount()
- + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow);
- }
-
- // There can be at most one row in the listview, that is internally
- // a ViewGroup with 2 rows
- public int getServiceTargetRowCount() {
- if (mShouldShowContentPreview && !ActivityManager.isLowRamDeviceStatic()) {
- return 1;
- }
- return 0;
- }
-
- public int getAzLabelRowCount() {
- // Only show a label if the a-z list is showing
- return (mShowAzLabelIfPoss && mChooserListAdapter.getAlphaTargetCount() > 0) ? 1 : 0;
- }
-
- @Override
- public int getItemCount() {
- return (int) (
- getSystemRowCount()
- + getProfileRowCount()
- + getServiceTargetRowCount()
- + getCallerAndRankedTargetRowCount()
- + getAzLabelRowCount()
- + mChooserListAdapter.getAlphaTargetCount()
- + getFooterRowCount()
- );
- }
-
- @Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- switch (viewType) {
- case VIEW_TYPE_CONTENT_PREVIEW:
- return new ItemViewHolder(
- mChooserActivityDelegate.buildContentPreview(parent),
- viewType,
- null,
- null);
- case VIEW_TYPE_PROFILE:
- return new ItemViewHolder(
- createProfileView(parent),
- viewType,
- null,
- null);
- case VIEW_TYPE_AZ_LABEL:
- return new ItemViewHolder(
- createAzLabelView(parent),
- viewType,
- null,
- null);
- case VIEW_TYPE_NORMAL:
- return new ItemViewHolder(
- mChooserListAdapter.createView(parent),
- viewType,
- mChooserActivityDelegate::onTargetSelected,
- mChooserActivityDelegate::onTargetLongPressed);
- case VIEW_TYPE_DIRECT_SHARE:
- case VIEW_TYPE_CALLER_AND_RANK:
- return createItemGroupViewHolder(viewType, parent);
- case VIEW_TYPE_FOOTER:
- Space sp = new Space(parent.getContext());
- sp.setLayoutParams(new RecyclerView.LayoutParams(
- LayoutParams.MATCH_PARENT, mFooterHeight));
- return new FooterViewHolder(sp, viewType);
- default:
- // Since we catch all possible viewTypes above, no chance this is being called.
- return null;
- }
- }
-
- @Override
- public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
- int viewType = ((ViewHolderBase) holder).getViewType();
- switch (viewType) {
- case VIEW_TYPE_DIRECT_SHARE:
- case VIEW_TYPE_CALLER_AND_RANK:
- bindItemGroupViewHolder(position, (ItemGroupViewHolder) holder);
- break;
- case VIEW_TYPE_NORMAL:
- bindItemViewHolder(position, (ItemViewHolder) holder);
- break;
- default:
- }
- }
-
- @Override
- public int getItemViewType(int position) {
- int count;
-
- int countSum = (count = getSystemRowCount());
- if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
-
- countSum += (count = getProfileRowCount());
- if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
-
- countSum += (count = getServiceTargetRowCount());
- if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE;
-
- countSum += (count = getCallerAndRankedTargetRowCount());
- if (count > 0 && position < countSum) return VIEW_TYPE_CALLER_AND_RANK;
-
- countSum += (count = getAzLabelRowCount());
- if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
-
- if (position == getItemCount() - 1) return VIEW_TYPE_FOOTER;
-
- return VIEW_TYPE_NORMAL;
- }
-
- public int getTargetType(int position) {
- return mChooserListAdapter.getPositionTargetType(getListPosition(position));
- }
-
- private View createProfileView(ViewGroup parent) {
- View profileRow = mLayoutInflater.inflate(R.layout.chooser_profile_row, parent, false);
- mChooserActivityDelegate.updateProfileViewButton(profileRow);
- return profileRow;
- }
-
- private View createAzLabelView(ViewGroup parent) {
- return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false);
- }
-
- private ItemGroupViewHolder loadViewsIntoGroup(ItemGroupViewHolder holder) {
- final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth,
- MeasureSpec.EXACTLY);
- int columnCount = holder.getColumnCount();
-
- final boolean isDirectShare = holder instanceof DirectShareViewHolder;
-
- for (int i = 0; i < columnCount; i++) {
- final View v = mChooserListAdapter.createView(holder.getRowByIndex(i));
- final int column = i;
- v.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mChooserActivityDelegate.onTargetSelected(holder.getItemIndex(column));
- }
- });
-
- // Show menu for both direct share and app share targets after long click.
- v.setOnLongClickListener(v1 -> {
- mChooserActivityDelegate.onTargetLongPressed(holder.getItemIndex(column));
- return true;
- });
-
- holder.addView(i, v);
-
- // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
- // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
- // done before measuring.
- if (isDirectShare) {
- final ViewHolder vh = (ViewHolder) v.getTag();
- vh.text.setLines(2);
- vh.text.setHorizontallyScrolling(false);
- vh.text2.setVisibility(View.GONE);
- }
-
- // Force height to be a given so we don't have visual disruption during scaling.
- v.measure(exactSpec, spec);
- setViewBounds(v, v.getMeasuredWidth(), v.getMeasuredHeight());
- }
-
- final ViewGroup viewGroup = holder.getViewGroup();
-
- // Pre-measure and fix height so we can scale later.
- holder.measure();
- setViewBounds(viewGroup, LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight());
-
- if (isDirectShare) {
- DirectShareViewHolder dsvh = (DirectShareViewHolder) holder;
- setViewBounds(dsvh.getRow(0), LayoutParams.MATCH_PARENT, dsvh.getMinRowHeight());
- setViewBounds(dsvh.getRow(1), LayoutParams.MATCH_PARENT, dsvh.getMinRowHeight());
- }
-
- viewGroup.setTag(holder);
- return holder;
- }
-
- private void setViewBounds(View view, int widthPx, int heightPx) {
- LayoutParams lp = view.getLayoutParams();
- if (lp == null) {
- lp = new LayoutParams(widthPx, heightPx);
- view.setLayoutParams(lp);
- } else {
- lp.height = heightPx;
- lp.width = widthPx;
- }
- }
-
- ItemGroupViewHolder createItemGroupViewHolder(int viewType, ViewGroup parent) {
- if (viewType == VIEW_TYPE_DIRECT_SHARE) {
- ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
- R.layout.chooser_row_direct_share, parent, false);
- ViewGroup row1 = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
- parentGroup, false);
- ViewGroup row2 = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
- parentGroup, false);
- parentGroup.addView(row1);
- parentGroup.addView(row2);
-
- mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
- Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType,
- mChooserActivityDelegate::getValidTargetCount);
- loadViewsIntoGroup(mDirectShareViewHolder);
-
- return mDirectShareViewHolder;
- } else {
- ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
- false);
- ItemGroupViewHolder holder =
- new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType);
- loadViewsIntoGroup(holder);
-
- return holder;
- }
- }
-
- /**
- * Need to merge CALLER + ranked STANDARD into a single row and prevent a separator from
- * showing on top of the AZ list if the AZ label is visible. All other types are placed into
- * their own row as determined by their target type, and dividers are added in the list to
- * separate each type.
- */
- int getRowType(int rowPosition) {
- // Merge caller and ranked standard into a single row
- int positionType = mChooserListAdapter.getPositionTargetType(rowPosition);
- if (positionType == ChooserListAdapter.TARGET_CALLER) {
- return ChooserListAdapter.TARGET_STANDARD;
- }
-
- // If an the A-Z label is shown, prevent a separator from appearing by making the A-Z
- // row type the same as the suggestion row type
- if (getAzLabelRowCount() > 0 && positionType == ChooserListAdapter.TARGET_STANDARD_AZ) {
- return ChooserListAdapter.TARGET_STANDARD;
- }
-
- return positionType;
- }
-
- void bindItemViewHolder(int position, ItemViewHolder holder) {
- View v = holder.itemView;
- int listPosition = getListPosition(position);
- holder.setListPosition(listPosition);
- mChooserListAdapter.bindView(listPosition, v);
- }
-
- void bindItemGroupViewHolder(int position, ItemGroupViewHolder holder) {
- final ViewGroup viewGroup = (ViewGroup) holder.itemView;
- int start = getListPosition(position);
- int startType = getRowType(start);
-
- int columnCount = holder.getColumnCount();
- int end = start + columnCount - 1;
- while (getRowType(end) != startType && end >= start) {
- end--;
- }
-
- 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) {
- textView.setAlpha(0.0f);
- textView.setVisibility(View.VISIBLE);
- textView.setText(R.string.chooser_no_direct_share_targets);
-
- ValueAnimator fadeAnim = ObjectAnimator.ofFloat(textView, "alpha", 0.0f, 1.0f);
- fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f));
-
- textView.setTranslationY(mChooserRowTextOptionTranslatePixelSize);
- ValueAnimator translateAnim = ObjectAnimator.ofFloat(textView, "translationY",
- 0.0f);
- translateAnim.setInterpolator(new DecelerateInterpolator(1.0f));
-
- AnimatorSet animSet = new AnimatorSet();
- animSet.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
- animSet.setStartDelay(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
- animSet.playTogether(fadeAnim, translateAnim);
- animSet.start();
- }
- }
-
- for (int i = 0; i < columnCount; i++) {
- final View v = holder.getView(i);
-
- if (start + i <= end) {
- holder.setViewVisibility(i, View.VISIBLE);
- holder.setItemIndex(i, start + i);
- mChooserListAdapter.bindView(holder.getItemIndex(i), v);
- } else {
- holder.setViewVisibility(i, View.INVISIBLE);
- }
- }
- }
-
- int getListPosition(int position) {
- position -= getSystemRowCount() + getProfileRowCount();
-
- final int serviceCount = mChooserListAdapter.getServiceTargetCount();
- final int serviceRows = (int) Math.ceil((float) serviceCount / mMaxTargetsPerRow);
- if (position < serviceRows) {
- return position * mMaxTargetsPerRow;
- }
-
- position -= serviceRows;
-
- final int callerAndRankedCount = mChooserListAdapter.getCallerTargetCount()
- + mChooserListAdapter.getRankedTargetCount();
- final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
- if (position < callerAndRankedRows) {
- return serviceCount + position * mMaxTargetsPerRow;
- }
-
- position -= getAzLabelRowCount() + callerAndRankedRows;
-
- return callerAndRankedCount + serviceCount + position;
- }
-
- public void handleScroll(View v, int y, int oldy) {
- boolean canExpandDirectShare = canExpandDirectShare();
- if (mDirectShareViewHolder != null && canExpandDirectShare) {
- mChooserActivityDelegate.handleScrollToExpandDirectShare(
- mDirectShareViewHolder, y, oldy);
- }
- }
-
- /**
- * Only expand direct share area if there is a minimum number of targets.
- */
- private boolean canExpandDirectShare() {
- // Do not enable until we have confirmed more apps are using sharing shortcuts
- // Check git history for enablement logic
- return false;
- }
-
- public ChooserListAdapter getListAdapter() {
- return mChooserListAdapter;
- }
-
- boolean shouldCellSpan(int position) {
- return getItemViewType(position) == VIEW_TYPE_NORMAL;
- }
-
- void updateDirectShareExpansion() {
- if (mDirectShareViewHolder == null || !canExpandDirectShare()) {
- return;
- }
- mChooserActivityDelegate.updateDirectShareExpansion(mDirectShareViewHolder);
- }
- }
-
static class ChooserTargetRankingInfo {
public final List<AppTarget> scores;
public final UserHandle userHandle;
diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java
index 6d59a680..91a701a6 100644
--- a/java/src/com/android/intentresolver/ChooserListAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserListAdapter.java
@@ -413,7 +413,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
return 0;
}
- int getAlphaTargetCount() {
+ public int getAlphaTargetCount() {
int groupedCount = mSortedList.size();
int ungroupedCount = mCallerTargets.size() + getDisplayResolveInfoCount();
return (ungroupedCount > mMaxRankedTargets) ? groupedCount : 0;
diff --git a/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java
index d0463fff..93daa299 100644
--- a/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserMultiProfilePagerAdapter.java
@@ -27,6 +27,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter;
+import com.android.intentresolver.grid.ChooserGridAdapter;
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -40,8 +41,9 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private int mBottomOffset;
private int mMaxTargetsPerRow;
- ChooserMultiProfilePagerAdapter(Context context,
- ChooserActivity.ChooserGridAdapter adapter,
+ ChooserMultiProfilePagerAdapter(
+ Context context,
+ ChooserGridAdapter adapter,
EmptyStateProvider emptyStateProvider,
QuietModeManager quietModeManager,
UserHandle workProfileUserHandle,
@@ -54,9 +56,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
mMaxTargetsPerRow = maxTargetsPerRow;
}
- ChooserMultiProfilePagerAdapter(Context context,
- ChooserActivity.ChooserGridAdapter personalAdapter,
- ChooserActivity.ChooserGridAdapter workAdapter,
+ ChooserMultiProfilePagerAdapter(
+ Context context,
+ ChooserGridAdapter personalAdapter,
+ ChooserGridAdapter workAdapter,
EmptyStateProvider emptyStateProvider,
QuietModeManager quietModeManager,
@Profile int defaultProfile,
@@ -71,8 +74,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
mMaxTargetsPerRow = maxTargetsPerRow;
}
- private ChooserProfileDescriptor createProfileDescriptor(
- ChooserActivity.ChooserGridAdapter adapter) {
+ private ChooserProfileDescriptor createProfileDescriptor(ChooserGridAdapter adapter) {
final LayoutInflater inflater = LayoutInflater.from(getContext());
final ViewGroup rootView =
(ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false);
@@ -103,7 +105,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
@VisibleForTesting
- public ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) {
+ public ChooserGridAdapter getAdapterForIndex(int pageIndex) {
return mItems[pageIndex].chooserGridAdapter;
}
@@ -122,8 +124,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
void setupListAdapter(int pageIndex) {
final RecyclerView recyclerView = getItem(pageIndex).recyclerView;
- ChooserActivity.ChooserGridAdapter chooserGridAdapter =
- getItem(pageIndex).chooserGridAdapter;
+ ChooserGridAdapter chooserGridAdapter = getItem(pageIndex).chooserGridAdapter;
GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager();
glm.setSpanCount(mMaxTargetsPerRow);
glm.setSpanSizeLookup(
@@ -164,7 +165,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
}
@Override
- ChooserActivity.ChooserGridAdapter getCurrentRootAdapter() {
+ ChooserGridAdapter getCurrentRootAdapter() {
return getAdapterForIndex(getCurrentPage());
}
@@ -195,9 +196,9 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
}
class ChooserProfileDescriptor extends ProfileDescriptor {
- private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
+ private ChooserGridAdapter chooserGridAdapter;
private RecyclerView recyclerView;
- ChooserProfileDescriptor(ViewGroup rootView, ChooserActivity.ChooserGridAdapter adapter) {
+ ChooserProfileDescriptor(ViewGroup rootView, ChooserGridAdapter adapter) {
super(rootView);
chooserGridAdapter = adapter;
recyclerView = rootView.findViewById(com.android.internal.R.id.resolver_list);
diff --git a/java/src/com/android/intentresolver/grid/ChooserGridAdapter.java b/java/src/com/android/intentresolver/grid/ChooserGridAdapter.java
new file mode 100644
index 00000000..1cf59316
--- /dev/null
+++ b/java/src/com/android/intentresolver/grid/ChooserGridAdapter.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2008 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.grid;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Space;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.intentresolver.ChooserListAdapter;
+import com.android.intentresolver.R;
+import com.android.intentresolver.ResolverListAdapter.ViewHolder;
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.collect.Lists;
+
+/**
+ * Adapter for all types of items and targets in ShareSheet.
+ * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
+ * row level by this adapter but not on the item level. Individual targets within the row are
+ * handled by {@link ChooserListAdapter}
+ */
+@VisibleForTesting
+public final class ChooserGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+ /**
+ * The transition time between placeholders for direct share to a message
+ * indicating that none are available.
+ */
+ public static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200;
+
+ /**
+ * 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.
+ */
+ public 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;
+ private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
+ private static final int VIEW_TYPE_PROFILE = 3;
+ private static final int VIEW_TYPE_AZ_LABEL = 4;
+ private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
+ private static final int VIEW_TYPE_FOOTER = 6;
+
+ private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
+
+ 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;
+
+ public ChooserGridAdapter(
+ Context context,
+ ChooserActivityDelegate chooserActivityDelegate,
+ ChooserListAdapter wrappedAdapter,
+ boolean shouldShowContentPreview,
+ int maxTargetsPerRow,
+ int numSheetExpansions) {
+ super();
+
+ mChooserActivityDelegate = chooserActivityDelegate;
+
+ mChooserListAdapter = wrappedAdapter;
+ mLayoutInflater = LayoutInflater.from(context);
+
+ mShouldShowContentPreview = shouldShowContentPreview;
+ mMaxTargetsPerRow = maxTargetsPerRow;
+
+ 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
+ public void onChanged() {
+ super.onChanged();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onInvalidated() {
+ super.onInvalidated();
+ notifyDataSetChanged();
+ }
+ });
+ }
+
+ public void setFooterHeight(int height) {
+ mFooterHeight = height;
+ }
+
+ /**
+ * Calculate the chooser target width to maximize space per item
+ *
+ * @param width The new row width to use for recalculation
+ * @return true if the view width has changed
+ */
+ public boolean calculateChooserTargetWidth(int width) {
+ if (width == 0) {
+ return false;
+ }
+
+ // Limit width to the maximum width of the chooser activity
+ int maxWidth = mChooserWidthPixels;
+ width = Math.min(maxWidth, width);
+
+ int newWidth = width / mMaxTargetsPerRow;
+ if (newWidth != mChooserTargetWidth) {
+ mChooserTargetWidth = newWidth;
+ return true;
+ }
+
+ return false;
+ }
+
+ public int getRowCount() {
+ return (int) (
+ getSystemRowCount()
+ + getProfileRowCount()
+ + getServiceTargetRowCount()
+ + getCallerAndRankedTargetRowCount()
+ + getAzLabelRowCount()
+ + Math.ceil(
+ (float) mChooserListAdapter.getAlphaTargetCount()
+ / mMaxTargetsPerRow)
+ );
+ }
+
+ /**
+ * Whether the "system" row of targets is displayed.
+ * This area includes the content preview (if present) and action row.
+ */
+ public int getSystemRowCount() {
+ // For the tabbed case we show the sticky content preview above the tabs,
+ // please refer to shouldShowStickyContentPreview
+ if (mChooserActivityDelegate.shouldShowTabs()) {
+ return 0;
+ }
+
+ if (!mShouldShowContentPreview) {
+ return 0;
+ }
+
+ if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ public int getProfileRowCount() {
+ if (mChooserActivityDelegate.shouldShowTabs()) {
+ return 0;
+ }
+ return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
+ }
+
+ public int getFooterRowCount() {
+ return 1;
+ }
+
+ public int getCallerAndRankedTargetRowCount() {
+ return (int) Math.ceil(
+ ((float) mChooserListAdapter.getCallerTargetCount()
+ + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow);
+ }
+
+ // There can be at most one row in the listview, that is internally
+ // a ViewGroup with 2 rows
+ public int getServiceTargetRowCount() {
+ if (mShouldShowContentPreview && !ActivityManager.isLowRamDeviceStatic()) {
+ return 1;
+ }
+ return 0;
+ }
+
+ public int getAzLabelRowCount() {
+ // Only show a label if the a-z list is showing
+ return (mShowAzLabelIfPoss && mChooserListAdapter.getAlphaTargetCount() > 0) ? 1 : 0;
+ }
+
+ @Override
+ public int getItemCount() {
+ return (int) (
+ getSystemRowCount()
+ + getProfileRowCount()
+ + getServiceTargetRowCount()
+ + getCallerAndRankedTargetRowCount()
+ + getAzLabelRowCount()
+ + mChooserListAdapter.getAlphaTargetCount()
+ + getFooterRowCount()
+ );
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ switch (viewType) {
+ case VIEW_TYPE_CONTENT_PREVIEW:
+ return new ItemViewHolder(
+ mChooserActivityDelegate.buildContentPreview(parent),
+ viewType,
+ null,
+ null);
+ case VIEW_TYPE_PROFILE:
+ return new ItemViewHolder(
+ createProfileView(parent),
+ viewType,
+ null,
+ null);
+ case VIEW_TYPE_AZ_LABEL:
+ return new ItemViewHolder(
+ createAzLabelView(parent),
+ viewType,
+ null,
+ null);
+ case VIEW_TYPE_NORMAL:
+ return new ItemViewHolder(
+ mChooserListAdapter.createView(parent),
+ viewType,
+ mChooserActivityDelegate::onTargetSelected,
+ mChooserActivityDelegate::onTargetLongPressed);
+ case VIEW_TYPE_DIRECT_SHARE:
+ case VIEW_TYPE_CALLER_AND_RANK:
+ return createItemGroupViewHolder(viewType, parent);
+ case VIEW_TYPE_FOOTER:
+ Space sp = new Space(parent.getContext());
+ sp.setLayoutParams(new RecyclerView.LayoutParams(
+ LayoutParams.MATCH_PARENT, mFooterHeight));
+ return new FooterViewHolder(sp, viewType);
+ default:
+ // Since we catch all possible viewTypes above, no chance this is being called.
+ return null;
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+ int viewType = ((ViewHolderBase) holder).getViewType();
+ switch (viewType) {
+ case VIEW_TYPE_DIRECT_SHARE:
+ case VIEW_TYPE_CALLER_AND_RANK:
+ bindItemGroupViewHolder(position, (ItemGroupViewHolder) holder);
+ break;
+ case VIEW_TYPE_NORMAL:
+ bindItemViewHolder(position, (ItemViewHolder) holder);
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int count;
+
+ int countSum = (count = getSystemRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
+
+ countSum += (count = getProfileRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
+
+ countSum += (count = getServiceTargetRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE;
+
+ countSum += (count = getCallerAndRankedTargetRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_CALLER_AND_RANK;
+
+ countSum += (count = getAzLabelRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
+
+ if (position == getItemCount() - 1) return VIEW_TYPE_FOOTER;
+
+ return VIEW_TYPE_NORMAL;
+ }
+
+ public int getTargetType(int position) {
+ return mChooserListAdapter.getPositionTargetType(getListPosition(position));
+ }
+
+ private View createProfileView(ViewGroup parent) {
+ View profileRow = mLayoutInflater.inflate(R.layout.chooser_profile_row, parent, false);
+ mChooserActivityDelegate.updateProfileViewButton(profileRow);
+ return profileRow;
+ }
+
+ private View createAzLabelView(ViewGroup parent) {
+ return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false);
+ }
+
+ private ItemGroupViewHolder loadViewsIntoGroup(ItemGroupViewHolder holder) {
+ final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth, MeasureSpec.EXACTLY);
+ int columnCount = holder.getColumnCount();
+
+ final boolean isDirectShare = holder instanceof DirectShareViewHolder;
+
+ for (int i = 0; i < columnCount; i++) {
+ final View v = mChooserListAdapter.createView(holder.getRowByIndex(i));
+ final int column = i;
+ v.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mChooserActivityDelegate.onTargetSelected(holder.getItemIndex(column));
+ }
+ });
+
+ // Show menu for both direct share and app share targets after long click.
+ v.setOnLongClickListener(v1 -> {
+ mChooserActivityDelegate.onTargetLongPressed(holder.getItemIndex(column));
+ return true;
+ });
+
+ holder.addView(i, v);
+
+ // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
+ // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
+ // done before measuring.
+ if (isDirectShare) {
+ final ViewHolder vh = (ViewHolder) v.getTag();
+ vh.text.setLines(2);
+ vh.text.setHorizontallyScrolling(false);
+ vh.text2.setVisibility(View.GONE);
+ }
+
+ // Force height to be a given so we don't have visual disruption during scaling.
+ v.measure(exactSpec, spec);
+ setViewBounds(v, v.getMeasuredWidth(), v.getMeasuredHeight());
+ }
+
+ final ViewGroup viewGroup = holder.getViewGroup();
+
+ // Pre-measure and fix height so we can scale later.
+ holder.measure();
+ setViewBounds(viewGroup, LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight());
+
+ if (isDirectShare) {
+ DirectShareViewHolder dsvh = (DirectShareViewHolder) holder;
+ setViewBounds(dsvh.getRow(0), LayoutParams.MATCH_PARENT, dsvh.getMinRowHeight());
+ setViewBounds(dsvh.getRow(1), LayoutParams.MATCH_PARENT, dsvh.getMinRowHeight());
+ }
+
+ viewGroup.setTag(holder);
+ return holder;
+ }
+
+ private void setViewBounds(View view, int widthPx, int heightPx) {
+ LayoutParams lp = view.getLayoutParams();
+ if (lp == null) {
+ lp = new LayoutParams(widthPx, heightPx);
+ view.setLayoutParams(lp);
+ } else {
+ lp.height = heightPx;
+ lp.width = widthPx;
+ }
+ }
+
+ ItemGroupViewHolder createItemGroupViewHolder(int viewType, ViewGroup parent) {
+ if (viewType == VIEW_TYPE_DIRECT_SHARE) {
+ ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
+ R.layout.chooser_row_direct_share, parent, false);
+ ViewGroup row1 = (ViewGroup) mLayoutInflater.inflate(
+ R.layout.chooser_row, parentGroup, false);
+ ViewGroup row2 = (ViewGroup) mLayoutInflater.inflate(
+ R.layout.chooser_row, parentGroup, false);
+ parentGroup.addView(row1);
+ parentGroup.addView(row2);
+
+ mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
+ Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType,
+ mChooserActivityDelegate::getValidTargetCount);
+ loadViewsIntoGroup(mDirectShareViewHolder);
+
+ return mDirectShareViewHolder;
+ } else {
+ ViewGroup row = (ViewGroup) mLayoutInflater.inflate(
+ R.layout.chooser_row, parent, false);
+ ItemGroupViewHolder holder =
+ new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType);
+ loadViewsIntoGroup(holder);
+
+ return holder;
+ }
+ }
+
+ /**
+ * Need to merge CALLER + ranked STANDARD into a single row and prevent a separator from
+ * showing on top of the AZ list if the AZ label is visible. All other types are placed into
+ * their own row as determined by their target type, and dividers are added in the list to
+ * separate each type.
+ */
+ int getRowType(int rowPosition) {
+ // Merge caller and ranked standard into a single row
+ int positionType = mChooserListAdapter.getPositionTargetType(rowPosition);
+ if (positionType == ChooserListAdapter.TARGET_CALLER) {
+ return ChooserListAdapter.TARGET_STANDARD;
+ }
+
+ // If an A-Z label is shown, prevent a separator from appearing by making the A-Z
+ // row type the same as the suggestion row type
+ if (getAzLabelRowCount() > 0 && positionType == ChooserListAdapter.TARGET_STANDARD_AZ) {
+ return ChooserListAdapter.TARGET_STANDARD;
+ }
+
+ return positionType;
+ }
+
+ void bindItemViewHolder(int position, ItemViewHolder holder) {
+ View v = holder.itemView;
+ int listPosition = getListPosition(position);
+ holder.setListPosition(listPosition);
+ mChooserListAdapter.bindView(listPosition, v);
+ }
+
+ void bindItemGroupViewHolder(int position, ItemGroupViewHolder holder) {
+ final ViewGroup viewGroup = (ViewGroup) holder.itemView;
+ int start = getListPosition(position);
+ int startType = getRowType(start);
+
+ int columnCount = holder.getColumnCount();
+ int end = start + columnCount - 1;
+ while (getRowType(end) != startType && end >= start) {
+ end--;
+ }
+
+ 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) {
+ textView.setAlpha(0.0f);
+ textView.setVisibility(View.VISIBLE);
+ textView.setText(R.string.chooser_no_direct_share_targets);
+
+ ValueAnimator fadeAnim = ObjectAnimator.ofFloat(textView, "alpha", 0.0f, 1.0f);
+ fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f));
+
+ textView.setTranslationY(mChooserRowTextOptionTranslatePixelSize);
+ ValueAnimator translateAnim =
+ ObjectAnimator.ofFloat(textView, "translationY", 0.0f);
+ translateAnim.setInterpolator(new DecelerateInterpolator(1.0f));
+
+ AnimatorSet animSet = new AnimatorSet();
+ animSet.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
+ animSet.setStartDelay(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
+ animSet.playTogether(fadeAnim, translateAnim);
+ animSet.start();
+ }
+ }
+
+ for (int i = 0; i < columnCount; i++) {
+ final View v = holder.getView(i);
+
+ if (start + i <= end) {
+ holder.setViewVisibility(i, View.VISIBLE);
+ holder.setItemIndex(i, start + i);
+ mChooserListAdapter.bindView(holder.getItemIndex(i), v);
+ } else {
+ holder.setViewVisibility(i, View.INVISIBLE);
+ }
+ }
+ }
+
+ int getListPosition(int position) {
+ position -= getSystemRowCount() + getProfileRowCount();
+
+ final int serviceCount = mChooserListAdapter.getServiceTargetCount();
+ final int serviceRows = (int) Math.ceil((float) serviceCount / mMaxTargetsPerRow);
+ if (position < serviceRows) {
+ return position * mMaxTargetsPerRow;
+ }
+
+ position -= serviceRows;
+
+ final int callerAndRankedCount =
+ mChooserListAdapter.getCallerTargetCount()
+ + mChooserListAdapter.getRankedTargetCount();
+ final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
+ if (position < callerAndRankedRows) {
+ return serviceCount + position * mMaxTargetsPerRow;
+ }
+
+ position -= getAzLabelRowCount() + callerAndRankedRows;
+
+ return callerAndRankedCount + serviceCount + position;
+ }
+
+ public void handleScroll(View v, int y, int oldy) {
+ boolean canExpandDirectShare = canExpandDirectShare();
+ if (mDirectShareViewHolder != null && canExpandDirectShare) {
+ mChooserActivityDelegate.handleScrollToExpandDirectShare(
+ mDirectShareViewHolder, y, oldy);
+ }
+ }
+
+ /** Only expand direct share area if there is a minimum number of targets. */
+ private boolean canExpandDirectShare() {
+ // Do not enable until we have confirmed more apps are using sharing shortcuts
+ // Check git history for enablement logic
+ return false;
+ }
+
+ public ChooserListAdapter getListAdapter() {
+ return mChooserListAdapter;
+ }
+
+ public boolean shouldCellSpan(int position) {
+ return getItemViewType(position) == VIEW_TYPE_NORMAL;
+ }
+
+ public void updateDirectShareExpansion() {
+ if (mDirectShareViewHolder == null || !canExpandDirectShare()) {
+ return;
+ }
+ mChooserActivityDelegate.updateDirectShareExpansion(mDirectShareViewHolder);
+ }
+}
diff --git a/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java b/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java
index cfd54697..316c9f07 100644
--- a/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java
+++ b/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java
@@ -113,7 +113,7 @@ public class DirectShareViewHolder extends ItemGroupViewHolder {
mCellVisibility[i] = false;
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0f);
- fadeAnim.setDuration(ChooserActivity.NO_DIRECT_SHARE_ANIM_IN_MILLIS);
+ fadeAnim.setDuration(ChooserGridAdapter.NO_DIRECT_SHARE_ANIM_IN_MILLIS);
fadeAnim.setInterpolator(new AccelerateInterpolator(1.0f));
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {