From 83128998e50f5bce173638b4b25c5722f8267932 Mon Sep 17 00:00:00 2001 From: Joshua Trask Date: Fri, 11 Nov 2022 16:49:37 -0500 Subject: Extract ChooserActivity inner ViewHolder classes These are all related to the inner ChooserGridAdapter, which I also hope to lift out of the monolith eventually, but that's a more complicated refactoring and this CL gets some low-hanging fruit out of the way first. Most of the classes were marked `static` or could have it trivially added. I only had to (minorly) rework ItemViewHolder to inject the callbacks; IMO this is a reasonable separation of the presentation logic (in the view holder) from the application logic (injected from the ChooserActivity). Test: atest IntentResolverUnitTests Bug: 202167050 Change-Id: Ic9707e079bb472dced270f54984e6e99bea5503a --- .../android/intentresolver/ChooserActivity.java | 354 +++------------------ .../intentresolver/grid/DirectShareViewHolder.java | 199 ++++++++++++ .../intentresolver/grid/FooterViewHolder.java | 28 ++ .../intentresolver/grid/ItemGroupViewHolder.java | 76 +++++ .../intentresolver/grid/ItemViewHolder.java | 63 ++++ .../intentresolver/grid/SingleRowViewHolder.java | 73 +++++ .../intentresolver/grid/ViewHolderBase.java | 35 ++ 7 files changed, 514 insertions(+), 314 deletions(-) create mode 100644 java/src/com/android/intentresolver/grid/DirectShareViewHolder.java create mode 100644 java/src/com/android/intentresolver/grid/FooterViewHolder.java create mode 100644 java/src/com/android/intentresolver/grid/ItemGroupViewHolder.java create mode 100644 java/src/com/android/intentresolver/grid/ItemViewHolder.java create mode 100644 java/src/com/android/intentresolver/grid/SingleRowViewHolder.java create mode 100644 java/src/com/android/intentresolver/grid/ViewHolderBase.java (limited to 'java/src/com') diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index d5a0c32c..cfe9d46a 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -18,8 +18,6 @@ package com.android.intentresolver; import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -85,7 +83,6 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.WindowInsets; -import android.view.animation.AccelerateInterpolator; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; @@ -103,6 +100,12 @@ 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.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; @@ -125,7 +128,6 @@ import java.lang.annotation.RetentionPolicy; import java.net.URISyntaxException; import java.text.Collator; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -221,9 +223,9 @@ public class ChooserActivity extends ResolverActivity implements * The transition time between placeholders for direct share to a message * indicating that non are available. */ - private static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200; + public static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200; - private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f; + public static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f; private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7; private final int mMaxHashSaltDays = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, @@ -2162,60 +2164,6 @@ public class ChooserActivity extends ResolverActivity implements return mContentView; } - abstract static class ViewHolderBase extends RecyclerView.ViewHolder { - private int mViewType; - - ViewHolderBase(View itemView, int viewType) { - super(itemView); - this.mViewType = viewType; - } - - int getViewType() { - return mViewType; - } - } - - /** - * Used to bind types of individual item including - * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL}, - * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW}, - * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE}, - * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}. - */ - final class ItemViewHolder extends ViewHolderBase { - ResolverListAdapter.ViewHolder mWrappedViewHolder; - int mListPosition = ChooserListAdapter.NO_POSITION; - - ItemViewHolder(View itemView, boolean isClickable, int viewType) { - super(itemView, viewType); - mWrappedViewHolder = new ResolverListAdapter.ViewHolder(itemView); - if (isClickable) { - itemView.setOnClickListener(v -> startSelected(mListPosition, - false/* always */, true/* filterd */)); - - itemView.setOnLongClickListener(v -> { - final TargetInfo ti = mChooserMultiProfilePagerAdapter.getActiveListAdapter() - .targetInfoForPosition(mListPosition, /* filtered */ true); - - // This should always be the case for ItemViewHolder, check for validity - if (ti.isDisplayResolveInfo()) { - showTargetDetails(ti); - } - return true; - }); - } - } - } - - /** - * Add a footer to the list, to support scrolling behavior below the navbar. - */ - static final class FooterViewHolder extends ViewHolderBase { - FooterViewHolder(View itemView, int viewType) { - super(itemView, viewType); - } - } - /** * Intentionally override the {@link ResolverActivity} implementation as we only need that * implementation for the intent resolver case. @@ -2455,15 +2403,41 @@ public class ChooserActivity extends ResolverActivity implements case VIEW_TYPE_CONTENT_PREVIEW: return new ItemViewHolder( createContentPreviewView(parent, mPreviewCoordinator), - false, - viewType); + viewType, + null, + null); case VIEW_TYPE_PROFILE: - return new ItemViewHolder(createProfileView(parent), false, viewType); + return new ItemViewHolder( + createProfileView(parent), + viewType, + null, + null); case VIEW_TYPE_AZ_LABEL: - return new ItemViewHolder(createAzLabelView(parent), false, viewType); + return new ItemViewHolder( + createAzLabelView(parent), + viewType, + null, + null); case VIEW_TYPE_NORMAL: return new ItemViewHolder( - mChooserListAdapter.createView(parent), true, viewType); + mChooserListAdapter.createView(parent), + viewType, + selectedPosition -> startSelected( + selectedPosition, + /* always= */ false, + /* filtered= */ true), + selectedPosition -> { + final TargetInfo longPressedTargetInfo = + mChooserMultiProfilePagerAdapter + .getActiveListAdapter() + .targetInfoForPosition( + selectedPosition, /* filtered= */ true); + // ItemViewHolder contents should always be "display resolve info" + // targets, but check just to make sure. + if (longPressedTargetInfo.isDisplayResolveInfo()) { + showTargetDetails(longPressedTargetInfo); + } + }); case VIEW_TYPE_DIRECT_SHARE: case VIEW_TYPE_CALLER_AND_RANK: return createItemGroupViewHolder(viewType, parent); @@ -2656,7 +2630,7 @@ public class ChooserActivity extends ResolverActivity implements void bindItemViewHolder(int position, ItemViewHolder holder) { View v = holder.itemView; int listPosition = getListPosition(position); - holder.mListPosition = listPosition; + holder.setListPosition(listPosition); mChooserListAdapter.bindView(listPosition, v); } @@ -2773,254 +2747,6 @@ public class ChooserActivity extends ResolverActivity implements } } - /** - * Used to bind types for group of items including: - * {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE}, - * and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}. - */ - abstract static class ItemGroupViewHolder extends ViewHolderBase { - protected int mMeasuredRowHeight; - private int[] mItemIndices; - protected final View[] mCells; - private final int mColumnCount; - - ItemGroupViewHolder(int cellCount, View itemView, int viewType) { - super(itemView, viewType); - this.mCells = new View[cellCount]; - this.mItemIndices = new int[cellCount]; - this.mColumnCount = cellCount; - } - - abstract ViewGroup addView(int index, View v); - - abstract ViewGroup getViewGroup(); - - abstract ViewGroup getRowByIndex(int index); - - abstract ViewGroup getRow(int rowNumber); - - abstract void setViewVisibility(int i, int visibility); - - public int getColumnCount() { - return mColumnCount; - } - - public void measure() { - final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - getViewGroup().measure(spec, spec); - mMeasuredRowHeight = getViewGroup().getMeasuredHeight(); - } - - public int getMeasuredRowHeight() { - return mMeasuredRowHeight; - } - - public void setItemIndex(int itemIndex, int listIndex) { - mItemIndices[itemIndex] = listIndex; - } - - public int getItemIndex(int itemIndex) { - return mItemIndices[itemIndex]; - } - - public View getView(int index) { - return mCells[index]; - } - } - - static class SingleRowViewHolder extends ItemGroupViewHolder { - private final ViewGroup mRow; - - SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) { - super(cellCount, row, viewType); - - this.mRow = row; - } - - public ViewGroup getViewGroup() { - return mRow; - } - - public ViewGroup getRowByIndex(int index) { - return mRow; - } - - public ViewGroup getRow(int rowNumber) { - if (rowNumber == 0) return mRow; - return null; - } - - public ViewGroup addView(int index, View v) { - mRow.addView(v); - mCells[index] = v; - - return mRow; - } - - public void setViewVisibility(int i, int visibility) { - getView(i).setVisibility(visibility); - } - } - - static class DirectShareViewHolder extends ItemGroupViewHolder { - private final ViewGroup mParent; - private final List mRows; - private int mCellCountPerRow; - - private boolean mHideDirectShareExpansion = false; - private int mDirectShareMinHeight = 0; - private int mDirectShareCurrHeight = 0; - private int mDirectShareMaxHeight = 0; - - private final boolean[] mCellVisibility; - - private final Supplier mListAdapterSupplier; - - DirectShareViewHolder(ViewGroup parent, List rows, int cellCountPerRow, - int viewType, Supplier listAdapterSupplier) { - super(rows.size() * cellCountPerRow, parent, viewType); - - this.mParent = parent; - this.mRows = rows; - this.mCellCountPerRow = cellCountPerRow; - this.mCellVisibility = new boolean[rows.size() * cellCountPerRow]; - Arrays.fill(mCellVisibility, true); - this.mListAdapterSupplier = listAdapterSupplier; - } - - public ViewGroup addView(int index, View v) { - ViewGroup row = getRowByIndex(index); - row.addView(v); - mCells[index] = v; - - return row; - } - - public ViewGroup getViewGroup() { - return mParent; - } - - public ViewGroup getRowByIndex(int index) { - return mRows.get(index / mCellCountPerRow); - } - - public ViewGroup getRow(int rowNumber) { - return mRows.get(rowNumber); - } - - public void measure() { - final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - getRow(0).measure(spec, spec); - getRow(1).measure(spec, spec); - - mDirectShareMinHeight = getRow(0).getMeasuredHeight(); - mDirectShareCurrHeight = mDirectShareCurrHeight > 0 - ? mDirectShareCurrHeight : mDirectShareMinHeight; - mDirectShareMaxHeight = 2 * mDirectShareMinHeight; - } - - public int getMeasuredRowHeight() { - return mDirectShareCurrHeight; - } - - public int getMinRowHeight() { - return mDirectShareMinHeight; - } - - public void setViewVisibility(int i, int visibility) { - final View v = getView(i); - if (visibility == View.VISIBLE) { - mCellVisibility[i] = true; - v.setVisibility(visibility); - v.setAlpha(1.0f); - } else if (visibility == View.INVISIBLE && mCellVisibility[i]) { - mCellVisibility[i] = false; - - ValueAnimator fadeAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0f); - fadeAnim.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS); - fadeAnim.setInterpolator(new AccelerateInterpolator(1.0f)); - fadeAnim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - v.setVisibility(View.INVISIBLE); - } - }); - fadeAnim.start(); - } - } - - public void handleScroll(RecyclerView view, int y, int oldy, int maxTargetsPerRow) { - // only exit early if fully collapsed, otherwise onListRebuilt() with shifting - // targets can lock us into an expanded mode - boolean notExpanded = mDirectShareCurrHeight == mDirectShareMinHeight; - if (notExpanded) { - if (mHideDirectShareExpansion) { - return; - } - - // only expand if we have more than maxTargetsPerRow, and delay that decision - // until they start to scroll - ChooserListAdapter adapter = mListAdapterSupplier.get(); - int validTargets = adapter.getSelectableServiceTargetCount(); - if (validTargets <= maxTargetsPerRow) { - mHideDirectShareExpansion = true; - return; - } - } - - int yDiff = (int) ((oldy - y) * DIRECT_SHARE_EXPANSION_RATE); - - int prevHeight = mDirectShareCurrHeight; - int newHeight = Math.min(prevHeight + yDiff, mDirectShareMaxHeight); - newHeight = Math.max(newHeight, mDirectShareMinHeight); - yDiff = newHeight - prevHeight; - - updateDirectShareRowHeight(view, yDiff, newHeight); - } - - void expand(RecyclerView view) { - updateDirectShareRowHeight(view, mDirectShareMaxHeight - mDirectShareCurrHeight, - mDirectShareMaxHeight); - } - - void collapse(RecyclerView view) { - updateDirectShareRowHeight(view, mDirectShareMinHeight - mDirectShareCurrHeight, - mDirectShareMinHeight); - } - - private void updateDirectShareRowHeight(RecyclerView view, int yDiff, int newHeight) { - if (view == null || view.getChildCount() == 0 || yDiff == 0) { - return; - } - - // locate the item to expand, and offset the rows below that one - boolean foundExpansion = false; - for (int i = 0; i < view.getChildCount(); i++) { - View child = view.getChildAt(i); - - if (foundExpansion) { - child.offsetTopAndBottom(yDiff); - } else { - if (child.getTag() != null && child.getTag() instanceof DirectShareViewHolder) { - int widthSpec = MeasureSpec.makeMeasureSpec(child.getWidth(), - MeasureSpec.EXACTLY); - int heightSpec = MeasureSpec.makeMeasureSpec(newHeight, - MeasureSpec.EXACTLY); - child.measure(widthSpec, heightSpec); - child.getLayoutParams().height = child.getMeasuredHeight(); - child.layout(child.getLeft(), child.getTop(), child.getRight(), - child.getTop() + child.getMeasuredHeight()); - - foundExpansion = true; - } - } - } - - if (foundExpansion) { - mDirectShareCurrHeight = newHeight; - } - } - } - static class ChooserTargetRankingInfo { public final List scores; public final UserHandle userHandle; diff --git a/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java b/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java new file mode 100644 index 00000000..95c61e3a --- /dev/null +++ b/java/src/com/android/intentresolver/grid/DirectShareViewHolder.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2022 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.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; + +import androidx.recyclerview.widget.RecyclerView; + +import com.android.intentresolver.ChooserActivity; +import com.android.intentresolver.ChooserListAdapter; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; + +/** Holder for direct share targets in the {@link ChooserGridAdapter}. */ +public class DirectShareViewHolder extends ItemGroupViewHolder { + private final ViewGroup mParent; + private final List mRows; + private int mCellCountPerRow; + + private boolean mHideDirectShareExpansion = false; + private int mDirectShareMinHeight = 0; + private int mDirectShareCurrHeight = 0; + private int mDirectShareMaxHeight = 0; + + private final boolean[] mCellVisibility; + + private final Supplier mListAdapterSupplier; + + public DirectShareViewHolder( + ViewGroup parent, + List rows, + int cellCountPerRow, + int viewType, + Supplier listAdapterSupplier) { + super(rows.size() * cellCountPerRow, parent, viewType); + + this.mParent = parent; + this.mRows = rows; + this.mCellCountPerRow = cellCountPerRow; + this.mCellVisibility = new boolean[rows.size() * cellCountPerRow]; + Arrays.fill(mCellVisibility, true); + this.mListAdapterSupplier = listAdapterSupplier; + } + + public ViewGroup addView(int index, View v) { + ViewGroup row = getRowByIndex(index); + row.addView(v); + mCells[index] = v; + + return row; + } + + public ViewGroup getViewGroup() { + return mParent; + } + + public ViewGroup getRowByIndex(int index) { + return mRows.get(index / mCellCountPerRow); + } + + public ViewGroup getRow(int rowNumber) { + return mRows.get(rowNumber); + } + + public void measure() { + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + getRow(0).measure(spec, spec); + getRow(1).measure(spec, spec); + + mDirectShareMinHeight = getRow(0).getMeasuredHeight(); + mDirectShareCurrHeight = (mDirectShareCurrHeight > 0) + ? mDirectShareCurrHeight : mDirectShareMinHeight; + mDirectShareMaxHeight = 2 * mDirectShareMinHeight; + } + + public int getMeasuredRowHeight() { + return mDirectShareCurrHeight; + } + + public int getMinRowHeight() { + return mDirectShareMinHeight; + } + + public void setViewVisibility(int i, int visibility) { + final View v = getView(i); + if (visibility == View.VISIBLE) { + mCellVisibility[i] = true; + v.setVisibility(visibility); + v.setAlpha(1.0f); + } else if (visibility == View.INVISIBLE && mCellVisibility[i]) { + mCellVisibility[i] = false; + + ValueAnimator fadeAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0f); + fadeAnim.setDuration(ChooserActivity.NO_DIRECT_SHARE_ANIM_IN_MILLIS); + fadeAnim.setInterpolator(new AccelerateInterpolator(1.0f)); + fadeAnim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + v.setVisibility(View.INVISIBLE); + } + }); + fadeAnim.start(); + } + } + + public void handleScroll(RecyclerView view, int y, int oldy, int maxTargetsPerRow) { + // only exit early if fully collapsed, otherwise onListRebuilt() with shifting + // targets can lock us into an expanded mode + boolean notExpanded = mDirectShareCurrHeight == mDirectShareMinHeight; + if (notExpanded) { + if (mHideDirectShareExpansion) { + return; + } + + // only expand if we have more than maxTargetsPerRow, and delay that decision + // until they start to scroll + ChooserListAdapter adapter = mListAdapterSupplier.get(); + int validTargets = adapter.getSelectableServiceTargetCount(); + if (validTargets <= maxTargetsPerRow) { + mHideDirectShareExpansion = true; + return; + } + } + + int yDiff = (int) ((oldy - y) * ChooserActivity.DIRECT_SHARE_EXPANSION_RATE); + + int prevHeight = mDirectShareCurrHeight; + int newHeight = Math.min(prevHeight + yDiff, mDirectShareMaxHeight); + newHeight = Math.max(newHeight, mDirectShareMinHeight); + yDiff = newHeight - prevHeight; + + updateDirectShareRowHeight(view, yDiff, newHeight); + } + + public void expand(RecyclerView view) { + updateDirectShareRowHeight( + view, mDirectShareMaxHeight - mDirectShareCurrHeight, mDirectShareMaxHeight); + } + + public void collapse(RecyclerView view) { + updateDirectShareRowHeight( + view, mDirectShareMinHeight - mDirectShareCurrHeight, mDirectShareMinHeight); + } + + private void updateDirectShareRowHeight(RecyclerView view, int yDiff, int newHeight) { + if (view == null || view.getChildCount() == 0 || yDiff == 0) { + return; + } + + // locate the item to expand, and offset the rows below that one + boolean foundExpansion = false; + for (int i = 0; i < view.getChildCount(); i++) { + View child = view.getChildAt(i); + + if (foundExpansion) { + child.offsetTopAndBottom(yDiff); + } else { + if (child.getTag() != null && child.getTag() instanceof DirectShareViewHolder) { + int widthSpec = MeasureSpec.makeMeasureSpec(child.getWidth(), + MeasureSpec.EXACTLY); + int heightSpec = MeasureSpec.makeMeasureSpec(newHeight, + MeasureSpec.EXACTLY); + child.measure(widthSpec, heightSpec); + child.getLayoutParams().height = child.getMeasuredHeight(); + child.layout(child.getLeft(), child.getTop(), child.getRight(), + child.getTop() + child.getMeasuredHeight()); + + foundExpansion = true; + } + } + } + + if (foundExpansion) { + mDirectShareCurrHeight = newHeight; + } + } +} diff --git a/java/src/com/android/intentresolver/grid/FooterViewHolder.java b/java/src/com/android/intentresolver/grid/FooterViewHolder.java new file mode 100644 index 00000000..0c94e3ed --- /dev/null +++ b/java/src/com/android/intentresolver/grid/FooterViewHolder.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 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.view.View; + +/** + * A footer on the list, to support scrolling behavior below the navbar. + */ +public final class FooterViewHolder extends ViewHolderBase { + public FooterViewHolder(View itemView, int viewType) { + super(itemView, viewType); + } +} diff --git a/java/src/com/android/intentresolver/grid/ItemGroupViewHolder.java b/java/src/com/android/intentresolver/grid/ItemGroupViewHolder.java new file mode 100644 index 00000000..5470506b --- /dev/null +++ b/java/src/com/android/intentresolver/grid/ItemGroupViewHolder.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 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.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewGroup; + +/** + * Used to bind types for group of items including: + * {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE}, + * and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}. + */ +public abstract class ItemGroupViewHolder extends ViewHolderBase { + protected int mMeasuredRowHeight; + private int[] mItemIndices; + protected final View[] mCells; + private final int mColumnCount; + + public ItemGroupViewHolder(int cellCount, View itemView, int viewType) { + super(itemView, viewType); + this.mCells = new View[cellCount]; + this.mItemIndices = new int[cellCount]; + this.mColumnCount = cellCount; + } + + public abstract ViewGroup addView(int index, View v); + + public abstract ViewGroup getViewGroup(); + + public abstract ViewGroup getRowByIndex(int index); + + public abstract ViewGroup getRow(int rowNumber); + + public abstract void setViewVisibility(int i, int visibility); + + public int getColumnCount() { + return mColumnCount; + } + + public void measure() { + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + getViewGroup().measure(spec, spec); + mMeasuredRowHeight = getViewGroup().getMeasuredHeight(); + } + + public int getMeasuredRowHeight() { + return mMeasuredRowHeight; + } + + public void setItemIndex(int itemIndex, int listIndex) { + mItemIndices[itemIndex] = listIndex; + } + + public int getItemIndex(int itemIndex) { + return mItemIndices[itemIndex]; + } + + public View getView(int index) { + return mCells[index]; + } +} diff --git a/java/src/com/android/intentresolver/grid/ItemViewHolder.java b/java/src/com/android/intentresolver/grid/ItemViewHolder.java new file mode 100644 index 00000000..2ec56b1b --- /dev/null +++ b/java/src/com/android/intentresolver/grid/ItemViewHolder.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 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.view.View; + +import androidx.annotation.Nullable; + +import com.android.intentresolver.ChooserListAdapter; +import com.android.intentresolver.ResolverListAdapter; + +import java.util.function.Consumer; + +/** + * Used to bind types of individual item including + * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL}, + * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW}, + * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE}, + * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}. + */ +public final class ItemViewHolder extends ViewHolderBase { + private final ResolverListAdapter.ViewHolder mWrappedViewHolder; + + private int mListPosition = ChooserListAdapter.NO_POSITION; + + public ItemViewHolder( + View itemView, + int viewType, + @Nullable Consumer onClick, + @Nullable Consumer onLongClick) { + super(itemView, viewType); + mWrappedViewHolder = new ResolverListAdapter.ViewHolder(itemView); + + if (onClick != null) { + itemView.setOnClickListener(v -> onClick.accept(mListPosition)); + } + + if (onLongClick != null) { + itemView.setOnLongClickListener(v -> { + onLongClick.accept(mListPosition); + return true; + }); + } + } + + public void setListPosition(int listPosition) { + mListPosition = listPosition; + } +} diff --git a/java/src/com/android/intentresolver/grid/SingleRowViewHolder.java b/java/src/com/android/intentresolver/grid/SingleRowViewHolder.java new file mode 100644 index 00000000..a72da7aa --- /dev/null +++ b/java/src/com/android/intentresolver/grid/SingleRowViewHolder.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 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.view.View; +import android.view.ViewGroup; + +/** Holder for a group of items displayed in a single row of the {@link ChooserGridAdapter}. */ +public final class SingleRowViewHolder extends ItemGroupViewHolder { + private final ViewGroup mRow; + + public SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) { + super(cellCount, row, viewType); + + this.mRow = row; + } + + /** Get the group of all views in this holder. */ + public ViewGroup getViewGroup() { + return mRow; + } + + /** + * Get the group of views for the row containing the specified cell index. + * TODO: unclear if that's what this `index` meant. It doesn't matter for our "single row" + * holders, and it doesn't look like this is an override from some other interface; maybe we can + * just remove? + */ + public ViewGroup getRowByIndex(int index) { + return mRow; + } + + /** Get the group of views for the specified {@code rowNumber}, if any. */ + public ViewGroup getRow(int rowNumber) { + if (rowNumber == 0) { + return mRow; + } + return null; + } + + /** + * @param index the index of the cell to add the view into. + * @param v the view to add into the cell. + */ + public ViewGroup addView(int index, View v) { + mRow.addView(v); + mCells[index] = v; + + return mRow; + } + + /** + * @param i the index of the cell containing the view to modify. + * @param visibility the new visibility to set on the view with the specified index. + */ + public void setViewVisibility(int i, int visibility) { + getView(i).setVisibility(visibility); + } +} diff --git a/java/src/com/android/intentresolver/grid/ViewHolderBase.java b/java/src/com/android/intentresolver/grid/ViewHolderBase.java new file mode 100644 index 00000000..78e9104a --- /dev/null +++ b/java/src/com/android/intentresolver/grid/ViewHolderBase.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 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.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +/** Base class for all {@link RecyclerView.ViewHolder} types in the {@link ChooserGridAdapter}. */ +public abstract class ViewHolderBase extends RecyclerView.ViewHolder { + private int mViewType; + + ViewHolderBase(View itemView, int viewType) { + super(itemView); + this.mViewType = viewType; + } + + public int getViewType() { + return mViewType; + } +} -- cgit v1.2.3-59-g8ed1b