diff options
Diffstat (limited to 'java/src')
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) {  |