summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-12-14 17:12:26 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-12-14 17:12:26 +0000
commit92160d6e9fb1caa1142599ee252f53b796298be5 (patch)
tree2e1e7cf136f872299ec23efd517175b9dc758f60 /java/src
parentcd6397be02bb9155a295da5e498858c9121ded7e (diff)
parentd5eb50ac083b03edf84c904e2ec16acb6ca50fdd (diff)
Merge "Extract ChooserGridAdapter." into tm-qpr-dev
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 e968f71d..12a054b9 100644
--- a/java/src/com/android/intentresolver/ChooserListAdapter.java
+++ b/java/src/com/android/intentresolver/ChooserListAdapter.java
@@ -412,7 +412,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) {