From eb367e471b03a9edbb4499a230e4d8433b596d9f Mon Sep 17 00:00:00 2001 From: Stevie Kideckel Date: Wed, 2 Jun 2021 16:21:54 +0000 Subject: [PATCH] Scroll to the selected widget picker row after headers are clicked This keeps the row in view. Currently, this will scroll the row to the centre-bottom of the screen due to the top padding being double counted, but that will remedied in a future CL. This also resolves the issue where the last row's widgets aren't visible by handling that case specially Fix: 188665456 Bug: 183378651 Test: verified locally Change-Id: I9acb9087a8cdaf130ac5955c810c96462b368f36 --- .../widget/picker/WidgetsListAdapter.java | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java index 7963431932..3936ec8e80 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -25,6 +25,7 @@ import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.TableRow; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; @@ -48,8 +49,10 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Recycler view adapter for the widget tray. @@ -87,6 +90,7 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC || new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) .equals(mWidgetsContentVisiblePackageUserKey); @Nullable private Predicate mFilter = null; + @Nullable private RecyclerView mRecyclerView; public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache, @@ -106,6 +110,16 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this)); } + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = null; + } + public void setFilter(Predicate filter) { mFilter = filter; } @@ -168,12 +182,10 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC mAllEntries.forEach(entry -> { if (entry instanceof WidgetsListHeaderEntry) { ((WidgetsListHeaderEntry) entry).setIsWidgetListShown( - new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) - .equals(mWidgetsContentVisiblePackageUserKey)); + isHeaderForVisibleContent(entry)); } else if (entry instanceof WidgetsListSearchHeaderEntry) { ((WidgetsListSearchHeaderEntry) entry).setIsWidgetListShown( - new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) - .equals(mWidgetsContentVisiblePackageUserKey)); + isHeaderForVisibleContent(entry)); } }); List newVisibleEntries = mAllEntries.stream() @@ -183,6 +195,13 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator); } + private boolean isHeaderForVisibleContent(WidgetsListBaseEntry entry) { + return (entry instanceof WidgetsListHeaderEntry + || entry instanceof WidgetsListSearchHeaderEntry) + && new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) + .equals(mWidgetsContentVisiblePackageUserKey); + } + /** * Resets any expanded widget header. */ @@ -247,12 +266,40 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC if (showWidgets) { mWidgetsContentVisiblePackageUserKey = packageUserKey; updateVisibleEntries(); + // Scroll the layout manager to the header position to keep it anchored to the same + // position. + scrollToSelectedHeaderPosition(); } else if (packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) { mWidgetsContentVisiblePackageUserKey = null; updateVisibleEntries(); } } + private void scrollToSelectedHeaderPosition() { + OptionalInt selectedHeaderPosition = + IntStream.range(0, mVisibleEntries.size()) + .filter(index -> isHeaderForVisibleContent(mVisibleEntries.get(index))) + .findFirst(); + RecyclerView.LayoutManager layoutManager = + mRecyclerView == null ? null : mRecyclerView.getLayoutManager(); + if (!selectedHeaderPosition.isPresent() || layoutManager == null) { + return; + } + + // Scroll to the selected header position. LinearLayoutManager scrolls the minimum distance + // necessary, so this will keep the selected header in place during clicks, without + // interrupting the animation. + int position = selectedHeaderPosition.getAsInt(); + if (position == mVisibleEntries.size() - 2) { + // If the selected header is in the last position (-1 for the content), then scroll to + // the final position so the last list of widgets will show. + layoutManager.scrollToPosition(mVisibleEntries.size() - 1); + } else { + // Otherwise, scroll to the position of the selected header. + layoutManager.scrollToPosition(position); + } + } + /** * Sets the max horizontal spans that are allowed for grouping more than one widgets in a table * row.