diff options
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/launcher3/AppsContainerRecyclerView.java | 102 | ||||
-rw-r--r-- | src/com/android/launcher3/AppsContainerView.java | 68 | ||||
-rw-r--r-- | src/com/android/launcher3/AppsGridAdapter.java | 11 | ||||
-rw-r--r-- | src/com/android/launcher3/BaseContainerRecyclerView.java | 30 |
4 files changed, 150 insertions, 61 deletions
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java index edb6f0c6e..e918bc2ee 100644 --- a/src/com/android/launcher3/AppsContainerRecyclerView.java +++ b/src/com/android/launcher3/AppsContainerRecyclerView.java @@ -38,6 +38,23 @@ import java.util.List; */ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { + /** + * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() + * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so + * that we can calculate what the scroll bar looks like, and where to jump to from the fast + * scroller. + */ + private static class ScrollPositionState { + // The index of the first app in the row (Note that is this not the position) + int rowFirstAppIndex; + // The index of the first visible row + int rowIndex; + // The offset of the first visible row + int rowTopOffset; + // The height of a given row (they are currently all the same height) + int rowHeight; + } + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; private AlphabeticalAppsList mApps; @@ -58,6 +75,8 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { private int mScrollbarWidth; private int mScrollbarMinHeight; private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); + private ScrollPositionState mScrollPosState = new ScrollPositionState(); public AppsContainerRecyclerView(Context context) { this(context, null); @@ -91,6 +110,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mScrollbarInset = res.getDimensionPixelSize(R.dimen.apps_view_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(getFastScrollerAlpha()); + setOverScrollMode(View.OVER_SCROLL_NEVER); } /** @@ -107,6 +127,12 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { mNumAppsPerRow = rowSize; } + @Override + public void setBackground(Drawable background) { + super.setBackground(background); + background.getPadding(mBackgroundPadding); + } + /** * Sets the fast scroller alpha. */ @@ -129,6 +155,14 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { return mScrollbarWidth; } + /** + * Scrolls this recycler view to the top. + */ + public void scrollToTop() { + scrollToPosition(0); + updateScrollY(0); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -238,7 +272,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { // Calculate the position for the fast scroller popup Rect bgBounds = mFastScrollerBg.getBounds(); if (isRtl) { - x = getPaddingLeft() + getScrollBarSize(); + x = mBackgroundPadding.left + getScrollBarSize(); } else { x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); } @@ -281,7 +315,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { * Invalidates the fast scroller popup. */ private void invalidateFastScroller() { - invalidate(getWidth() - getPaddingRight() - getScrollBarSize() - + invalidate(getWidth() - mBackgroundPadding.right - getScrollBarSize() - mFastScrollerBg.getIntrinsicWidth(), 0, getWidth(), getHeight()); } @@ -312,6 +346,15 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { stopScroll(); layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0); + // We need to workaround the RecyclerView to get the right scroll position after scrolling + List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); + getCurScrollState(mScrollPosState, items); + if (mScrollPosState.rowIndex != -1) { + int rowIndex = findRowForAppIndex(mScrollPosState.rowFirstAppIndex); + int y = (rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset; + updateScrollY(y); + } + return lastScrollSection.sectionName; } @@ -332,44 +375,29 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { int y; boolean isRtl = (getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL); - int rowIndex = -1; - int rowTopOffset = -1; - int rowHeight = -1; int rowCount = getNumRows(); - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - int position = getChildPosition(child); - if (position != NO_POSITION) { - AlphabeticalAppsList.AdapterItem item = items.get(position); - if (!item.isSectionHeader) { - rowIndex = findRowForAppIndex(item.appIndex); - rowTopOffset = getLayoutManager().getDecoratedTop(child); - rowHeight = child.getHeight(); - break; - } - } - } + getCurScrollState(mScrollPosState, items); - if (rowIndex != -1) { + if (mScrollPosState.rowIndex != -1) { int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * rowHeight; + int totalScrollHeight = rowCount * mScrollPosState.rowHeight; if (totalScrollHeight > height) { int scrollbarHeight = Math.max(mScrollbarMinHeight, (int) (height / ((float) totalScrollHeight / height))); // Calculate the position and size of the scroll bar if (isRtl) { - x = getPaddingLeft(); + x = mBackgroundPadding.left; } else { - x = getWidth() - getPaddingRight() - mScrollbarWidth; + x = getWidth() - mBackgroundPadding.right - mScrollbarWidth; } // To calculate the offset, we compute the percentage of the total scrollable height // that the user has already scrolled and then map that to the scroll bar bounds int availableY = totalScrollHeight - height; int availableScrollY = height - scrollbarHeight; - y = (rowIndex * rowHeight) - rowTopOffset; + y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - + mScrollPosState.rowTopOffset; y = getPaddingTop() + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); @@ -410,4 +438,30 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView { } return rowCount; } + + /** + * Returns the current scroll state. + */ + private void getCurScrollState(ScrollPositionState stateOut, + List<AlphabeticalAppsList.AdapterItem> items) { + stateOut.rowFirstAppIndex = -1; + stateOut.rowIndex = -1; + stateOut.rowTopOffset = -1; + stateOut.rowHeight = -1; + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + int position = getChildPosition(child); + if (position != NO_POSITION) { + AlphabeticalAppsList.AdapterItem item = items.get(position); + if (!item.isSectionHeader) { + stateOut.rowFirstAppIndex = item.appIndex; + stateOut.rowIndex = findRowForAppIndex(item.appIndex); + stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); + stateOut.rowHeight = child.getHeight(); + break; + } + } + } + } } diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java index 8a5c6605e..5dac9f1e8 100644 --- a/src/com/android/launcher3/AppsContainerView.java +++ b/src/com/android/launcher3/AppsContainerView.java @@ -51,9 +51,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource, public static final boolean GRID_HIDE_SECTION_HEADERS = false; private static final boolean ALLOW_SINGLE_APP_LAUNCH = true; - private static final boolean DYNAMIC_HEADER_ELEVATION = false; + private static final boolean DYNAMIC_HEADER_ELEVATION = true; private static final boolean DISMISS_SEARCH_ON_BACK = true; private static final float HEADER_ELEVATION_DP = 4; + // How far the user has to scroll in order to reach the full elevation + private static final float HEADER_SCROLL_TO_ELEVATION_DP = 16; private static final int FADE_IN_DURATION = 175; private static final int FADE_OUT_DURATION = 100; private static final int SEARCH_TRANSLATION_X_DP = 18; @@ -159,8 +161,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource, * Scrolls this list view to the top. */ public void scrollToTop() { - mAppsRecyclerView.scrollToPosition(0); - mRecyclerViewScrollY = 0; + mAppsRecyclerView.scrollToTop(); } /** @@ -230,18 +231,14 @@ public class AppsContainerView extends BaseContainerView implements DragSource, mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); mAppsRecyclerView.setHasFixedSize(true); - mAppsRecyclerView.setOnScrollListenerProxy(new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - // Do nothing - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mRecyclerViewScrollY += dy; - onRecyclerViewScrolled(); - } - }); + mAppsRecyclerView.setOnScrollListenerProxy( + new BaseContainerRecyclerView.OnScrollToListener() { + @Override + public void onScrolledTo(int x, int y) { + mRecyclerViewScrollY = y; + onRecyclerViewScrolled(); + } + }); if (mItemDecoration != null) { mAppsRecyclerView.addItemDecoration(mItemDecoration); } @@ -291,9 +288,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource, int startMargin = grid.isPhone() ? mContentMarginStart : 0; int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset; if (isRtl) { - mAppsRecyclerView.setPadding(inset, inset, inset + startMargin, inset); + mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset, + inset + startMargin, inset); } else { - mAppsRecyclerView.setPadding(inset + startMargin, inset, inset, inset); + mAppsRecyclerView.setPadding(inset + startMargin, inset, + inset + mAppsRecyclerView.getScrollbarWidth(), inset); } // Update the header bar @@ -456,7 +455,10 @@ public class AppsContainerView extends BaseContainerView implements DragSource, String formatStr = getResources().getString(R.string.apps_view_no_search_results); mAdapter.setEmptySearchText(String.format(formatStr, queryText)); + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. final String queryTextLower = queryText.toLowerCase(); + final String[] queryWords = SPLIT_PATTERN.split(queryTextLower); mApps.setFilter(new AlphabeticalAppsList.Filter() { @Override public boolean retainApp(AppInfo info, String sectionName) { @@ -465,12 +467,21 @@ public class AppsContainerView extends BaseContainerView implements DragSource, } String title = info.title.toString(); String[] words = SPLIT_PATTERN.split(title.toLowerCase()); - for (int i = 0; i < words.length; i++) { - if (words[i].startsWith(queryTextLower)) { - return true; + for (int qi = 0; qi < queryWords.length; qi++) { + boolean foundMatch = false; + for (int i = 0; i < words.length; i++) { + if (words[i].startsWith(queryWords[qi])) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + // If there is a word in the query that does not match any words in this + // title, so skip it. + return false; } } - return false; + return true; } }); } @@ -531,11 +542,16 @@ public class AppsContainerView extends BaseContainerView implements DragSource, * Updates the container when the recycler view is scrolled. */ private void onRecyclerViewScrolled() { - if (DYNAMIC_HEADER_ELEVATION) { - int elevation = Math.min(mRecyclerViewScrollY, DynamicGrid.pxFromDp(HEADER_ELEVATION_DP, - getContext().getResources().getDisplayMetrics())); - if (Float.compare(mHeaderView.getElevation(), elevation) != 0) { - mHeaderView.setElevation(elevation); + if (DYNAMIC_HEADER_ELEVATION && Utilities.isLmpOrAbove()) { + int elevation = DynamicGrid.pxFromDp(HEADER_ELEVATION_DP, + getContext().getResources().getDisplayMetrics()); + int scrollToElevation = DynamicGrid.pxFromDp(HEADER_SCROLL_TO_ELEVATION_DP, + getContext().getResources().getDisplayMetrics()); + float elevationPct = (float) Math.min(mRecyclerViewScrollY, scrollToElevation) / + scrollToElevation; + float newElevation = elevation * elevationPct; + if (Float.compare(mHeaderView.getElevation(), newElevation) != 0) { + mHeaderView.setElevation(newElevation); } } } diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java index a6902d5d3..4014e3804 100644 --- a/src/com/android/launcher3/AppsGridAdapter.java +++ b/src/com/android/launcher3/AppsGridAdapter.java @@ -101,11 +101,10 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) { // Draw the divider under the predicted app + parent.getBackground().getPadding(mTmpBounds); int top = child.getTop() + child.getHeight(); - int left = parent.getPaddingLeft(); - int right = parent.getWidth() - parent.getPaddingRight(); - int iconInset = (((right - left) / mAppsPerRow) - grid.allAppsIconSizePx) / 2; - c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint); + c.drawLine(mTmpBounds.left, top, parent.getWidth() - mTmpBounds.right, top, + mPredictedAppsDividerPaint); hasDrawnPredictedAppDivider = true; } else if (grid.isPhone() && shouldDrawItemSection(holder, i, items)) { @@ -297,8 +296,8 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> { mSectionTextPaint.setAntiAlias(true); mPredictedAppsDividerPaint = new Paint(); - mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1.5f, res.getDisplayMetrics())); - mPredictedAppsDividerPaint.setColor(0x10000000); + mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1f, res.getDisplayMetrics())); + mPredictedAppsDividerPaint.setColor(0x1E000000); mPredictedAppsDividerPaint.setAntiAlias(true); } diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java index 5b30e3df6..59e20ca2f 100644 --- a/src/com/android/launcher3/BaseContainerRecyclerView.java +++ b/src/com/android/launcher3/BaseContainerRecyclerView.java @@ -29,12 +29,20 @@ import com.android.launcher3.util.Thunk; public class BaseContainerRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { + /** + * Listener to get notified when the absolute scroll changes. + */ + public interface OnScrollToListener { + void onScrolledTo(int x, int y); + } + private static final int SCROLL_DELTA_THRESHOLD_DP = 4; /** Keeps the last known scrolling delta/velocity along y-axis. */ @Thunk int mDy = 0; + @Thunk int mScrollY; private float mDeltaThreshold; - private RecyclerView.OnScrollListener mScrollListenerProxy; + private OnScrollToListener mScrollToListener; public BaseContainerRecyclerView(Context context) { this(context, null); @@ -60,8 +68,9 @@ public class BaseContainerRecyclerView extends RecyclerView @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { mDy = dy; - if (mScrollListenerProxy != null) { - mScrollListenerProxy.onScrolled(recyclerView, dx, dy); + mScrollY += dy; + if (mScrollToListener != null) { + mScrollToListener.onScrolledTo(0, mScrollY); } } } @@ -69,8 +78,8 @@ public class BaseContainerRecyclerView extends RecyclerView /** * Sets an additional scroll listener, only needed for LMR1 version of the support lib. */ - public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) { - mScrollListenerProxy = listener; + public void setOnScrollListenerProxy(OnScrollToListener listener) { + mScrollToListener = listener; } @Override @@ -97,6 +106,17 @@ public class BaseContainerRecyclerView extends RecyclerView } /** + * Updates the scroll position, used to workaround a RecyclerView issue with scrolling to + * position. + */ + protected void updateScrollY(int scrollY) { + mScrollY = scrollY; + if (mScrollToListener != null) { + mScrollToListener.onScrolledTo(0, mScrollY); + } + } + + /** * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped. */ protected boolean shouldStopScroll(MotionEvent ev) { |