mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 11:18:21 +00:00
Merge "Unifying scroll calculation logic for both widgets and apps recycler view Also using itemType instead of item object for widget size cache" into tm-qpr-dev am: 5069964ea2
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/18763224 Change-Id: Ief18fa0a43fd0c01685109ef26f37d32af1c200b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
-->
|
||||
<resources>
|
||||
<item type="id" name="apps_list_view_work" />
|
||||
<item type="id" name="tag_widget_entry" />
|
||||
<item type="id" name="view_type_widgets_space" />
|
||||
<item type="id" name="view_type_widgets_list" />
|
||||
<item type="id" name="view_type_widgets_header" />
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
@@ -86,15 +87,20 @@ public abstract class FastScrollRecyclerView extends RecyclerView {
|
||||
* Returns the available scroll height:
|
||||
* AvailableScrollHeight = Total height of the all items - last page height
|
||||
*/
|
||||
protected abstract int getAvailableScrollHeight();
|
||||
protected int getAvailableScrollHeight() {
|
||||
// AvailableScrollHeight = Total height of the all items - first page height
|
||||
int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
|
||||
int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
|
||||
int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
|
||||
return Math.max(0, availableScrollHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available scroll bar height:
|
||||
* AvailableScrollBarHeight = Total height of the visible view - thumb height
|
||||
*/
|
||||
protected int getAvailableScrollBarHeight() {
|
||||
int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
|
||||
return availableScrollBarHeight;
|
||||
return getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,12 +158,51 @@ public abstract class FastScrollRecyclerView extends RecyclerView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the touch (from 0..1) to the adapter position that should be visible.
|
||||
* <p>Override in each subclass of this base class.
|
||||
*
|
||||
* @return the scroll top of this recycler view.
|
||||
*/
|
||||
public abstract int getCurrentScrollY();
|
||||
public int getCurrentScrollY() {
|
||||
Adapter adapter = getAdapter();
|
||||
if (adapter == null) {
|
||||
return -1;
|
||||
}
|
||||
if (adapter.getItemCount() == 0 || getChildCount() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int itemPosition = NO_POSITION;
|
||||
View child = null;
|
||||
|
||||
LayoutManager layoutManager = getLayoutManager();
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
// Use the LayoutManager as the source of truth for visible positions. During
|
||||
// animations, the view group child may not correspond to the visible views that appear
|
||||
// at the top.
|
||||
itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
|
||||
child = layoutManager.findViewByPosition(itemPosition);
|
||||
}
|
||||
|
||||
if (child == null) {
|
||||
// If the layout manager returns null for any reason, which can happen before layout
|
||||
// has occurred for the position, then look at the child of this view as a ViewGroup.
|
||||
child = getChildAt(0);
|
||||
itemPosition = getChildAdapterPosition(child);
|
||||
}
|
||||
if (itemPosition == NO_POSITION) {
|
||||
return -1;
|
||||
}
|
||||
return getPaddingTop() + getItemsHeight(itemPosition)
|
||||
- layoutManager.getDecoratedTop(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the height, in pixels, of this list adapter's items from index
|
||||
* 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
|
||||
* it returns the full height of all the items.
|
||||
*
|
||||
* <p>If the untilIndex is larger than the total number of items in this adapter, returns the
|
||||
* sum of all items' height.
|
||||
*/
|
||||
protected abstract int getItemsHeight(int untilIndex);
|
||||
|
||||
/**
|
||||
* Maps the touch (from 0..1) to the adapter position that should be visible.
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -342,24 +341,7 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentScrollY() {
|
||||
// Return early if there are no items or we haven't been measured
|
||||
List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
|
||||
if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calculate the y and offset for the item
|
||||
View child = getChildAt(0);
|
||||
int position = getChildAdapterPosition(child);
|
||||
if (position == NO_POSITION) {
|
||||
return -1;
|
||||
}
|
||||
return getPaddingTop() +
|
||||
getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
|
||||
}
|
||||
|
||||
public int getCurrentScrollY(int position, int offset) {
|
||||
protected int getItemsHeight(int position) {
|
||||
List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
|
||||
AllAppsGridAdapter.AdapterItem posItem = position < items.size()
|
||||
? items.get(position) : null;
|
||||
@@ -400,17 +382,7 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView {
|
||||
}
|
||||
mCachedScrollPositions.put(position, y);
|
||||
}
|
||||
return y - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available scroll height:
|
||||
* AvailableScrollHeight = Total height of the all items - last page height
|
||||
*/
|
||||
@Override
|
||||
protected int getAvailableScrollHeight() {
|
||||
return getPaddingTop() + getCurrentScrollY(getAdapter().getItemCount(), 0)
|
||||
- getHeight() + getPaddingBottom();
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getScrollBarTop() {
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_FIRST
|
||||
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
@@ -36,7 +35,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
import androidx.recyclerview.widget.RecyclerView.LayoutParams;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
@@ -80,10 +78,10 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/** Uniquely identifies widgets list view type within the app. */
|
||||
private static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
|
||||
private static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
|
||||
private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
|
||||
private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
|
||||
public static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
|
||||
public static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
|
||||
public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
|
||||
public static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
|
||||
|
||||
private final Context mContext;
|
||||
private final WidgetsDiffReporter mDiffReporter;
|
||||
@@ -103,7 +101,6 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
|
||||
@Nullable private RecyclerView mRecyclerView;
|
||||
@Nullable private PackageUserKey mPendingClickHeader;
|
||||
private final int mSpacingBetweenEntries;
|
||||
private int mMaxSpanSize = 4;
|
||||
|
||||
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
||||
@@ -133,28 +130,11 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
mViewHolderBinders.put(
|
||||
VIEW_TYPE_WIDGETS_SPACE,
|
||||
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
|
||||
mSpacingBetweenEntries =
|
||||
context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||
mRecyclerView = recyclerView;
|
||||
|
||||
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
|
||||
@Override
|
||||
public void getItemOffsets(
|
||||
@NonNull Rect outRect,
|
||||
@NonNull View view,
|
||||
@NonNull RecyclerView parent,
|
||||
@NonNull RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
int position = ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
|
||||
boolean isHeader =
|
||||
view.getTag(R.id.tag_widget_entry) instanceof WidgetsListBaseEntry.Header;
|
||||
outRect.top += position > 0 && isHeader ? mSpacingBetweenEntries : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -286,7 +266,6 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
listPos |= POSITION_LAST;
|
||||
}
|
||||
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
|
||||
holder.itemView.setTag(R.id.tag_widget_entry, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,27 +16,24 @@
|
||||
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_HEADER;
|
||||
import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_SEARCH_HEADER;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TableLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.FastScrollRecyclerView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
|
||||
import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
|
||||
|
||||
/**
|
||||
* The widgets recycler view.
|
||||
@@ -51,12 +48,14 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
|
||||
private boolean mTouchDownOnScroller;
|
||||
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
|
||||
|
||||
// Cached sizes
|
||||
private int mLastVisibleWidgetContentTableHeight = 0;
|
||||
private int mWidgetHeaderHeight = 0;
|
||||
private int mWidgetEmptySpaceHeight = 0;
|
||||
|
||||
private final int mSpacingBetweenEntries;
|
||||
/**
|
||||
* There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
|
||||
* the size can be used for all other items of same type. Caching the last know size for
|
||||
* VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
|
||||
* VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
|
||||
*/
|
||||
private final SparseIntArray mCachedSizes = new SparseIntArray();
|
||||
private final SpacingDecoration mSpacingDecoration;
|
||||
|
||||
public WidgetsRecyclerView(Context context) {
|
||||
this(context, null);
|
||||
@@ -72,12 +71,8 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
|
||||
mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
|
||||
addOnItemTouchListener(this);
|
||||
|
||||
ActivityContext activity = ActivityContext.lookupContext(getContext());
|
||||
DeviceProfile grid = activity.getDeviceProfile();
|
||||
|
||||
// The spacing used between entries.
|
||||
mSpacingBetweenEntries =
|
||||
getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
|
||||
mSpacingDecoration = new SpacingDecoration(context);
|
||||
addItemDecoration(mSpacingDecoration);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,67 +133,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
|
||||
synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentScrollY() {
|
||||
// Skip early if widgets are not bound.
|
||||
if (isModelNotReady() || getChildCount() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rowIndex = -1;
|
||||
View child = null;
|
||||
|
||||
LayoutManager layoutManager = getLayoutManager();
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
// Use the LayoutManager as the source of truth for visible positions. During
|
||||
// animations, the view group child may not correspond to the visible views that appear
|
||||
// at the top.
|
||||
rowIndex = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
|
||||
child = layoutManager.findViewByPosition(rowIndex);
|
||||
}
|
||||
|
||||
if (child == null) {
|
||||
// If the layout manager returns null for any reason, which can happen before layout
|
||||
// has occurred for the position, then look at the child of this view as a ViewGroup.
|
||||
child = getChildAt(0);
|
||||
rowIndex = getChildPosition(child);
|
||||
}
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View view = getChildAt(i);
|
||||
if (view instanceof TableLayout) {
|
||||
// This assumes there is ever only one content shown in this recycler view.
|
||||
mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight();
|
||||
} else if (view instanceof WidgetsListHeader
|
||||
&& mWidgetHeaderHeight == 0
|
||||
&& view.getMeasuredHeight() > 0) {
|
||||
// This assumes all header views are of the same height.
|
||||
mWidgetHeaderHeight = view.getMeasuredHeight();
|
||||
} else if (view instanceof EmptySpaceView && view.getMeasuredHeight() > 0) {
|
||||
mWidgetEmptySpaceHeight = view.getMeasuredHeight();
|
||||
}
|
||||
}
|
||||
|
||||
int scrollPosition = getItemsHeight(rowIndex);
|
||||
int offset = getLayoutManager().getDecoratedTop(child);
|
||||
|
||||
return getPaddingTop() + scrollPosition - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available scroll height, in pixel.
|
||||
*
|
||||
* <p>If the recycler view can't be scrolled, returns 0.
|
||||
*/
|
||||
@Override
|
||||
protected int getAvailableScrollHeight() {
|
||||
// AvailableScrollHeight = Total height of the all items - first page height
|
||||
int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
|
||||
int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ mAdapter.getItemCount());
|
||||
int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
|
||||
return Math.max(0, availableScrollHeight);
|
||||
}
|
||||
|
||||
private boolean isModelNotReady() {
|
||||
return mAdapter.getItemCount() == 0;
|
||||
}
|
||||
@@ -246,28 +180,27 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
|
||||
* <p>If the untilIndex is larger than the total number of items in this adapter, returns the
|
||||
* sum of all items' height.
|
||||
*/
|
||||
private int getItemsHeight(int untilIndex) {
|
||||
@Override
|
||||
protected int getItemsHeight(int untilIndex) {
|
||||
// Initialize cache
|
||||
int childCount = getChildCount();
|
||||
int startPosition;
|
||||
if (childCount > 0
|
||||
&& ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
mCachedSizes.put(
|
||||
mAdapter.getItemViewType(startPosition + i),
|
||||
getChildAt(i).getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
if (untilIndex > mAdapter.getItems().size()) {
|
||||
untilIndex = mAdapter.getItems().size();
|
||||
}
|
||||
int totalItemsHeight = 0;
|
||||
for (int i = 0; i < untilIndex; i++) {
|
||||
WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
|
||||
if (entry instanceof WidgetsListHeaderEntry
|
||||
|| entry instanceof WidgetsListSearchHeaderEntry) {
|
||||
totalItemsHeight += mWidgetHeaderHeight;
|
||||
if (i > 0) {
|
||||
// Each header contains the spacing between entries as top decoration, except
|
||||
// the first one.
|
||||
totalItemsHeight += mSpacingBetweenEntries;
|
||||
}
|
||||
} else if (entry instanceof WidgetsListContentEntry) {
|
||||
totalItemsHeight += mLastVisibleWidgetContentTableHeight;
|
||||
} else if (entry instanceof WidgetListSpaceEntry) {
|
||||
totalItemsHeight += mWidgetEmptySpaceHeight;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Can't estimate height for " + entry);
|
||||
}
|
||||
int type = mAdapter.getItemViewType(i);
|
||||
totalItemsHeight += mCachedSizes.get(type) + mSpacingDecoration.getSpacing(i, type);
|
||||
}
|
||||
return totalItemsHeight;
|
||||
}
|
||||
@@ -283,4 +216,31 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte
|
||||
*/
|
||||
int getHeaderViewHeight();
|
||||
}
|
||||
|
||||
private static class SpacingDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private final int mSpacingBetweenEntries;
|
||||
|
||||
SpacingDecoration(@NonNull Context context) {
|
||||
mSpacingBetweenEntries =
|
||||
context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(
|
||||
@NonNull Rect outRect,
|
||||
@NonNull View view,
|
||||
@NonNull RecyclerView parent,
|
||||
@NonNull RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
outRect.top += getSpacing(position, parent.getAdapter().getItemViewType(position));
|
||||
}
|
||||
|
||||
public int getSpacing(int position, int type) {
|
||||
boolean isHeader = type == VIEW_TYPE_WIDGETS_SEARCH_HEADER
|
||||
|| type == VIEW_TYPE_WIDGETS_HEADER;
|
||||
return position > 0 && isHeader ? mSpacingBetweenEntries : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user