summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/allapps
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/allapps')
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java32
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java2
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java248
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java76
4 files changed, 228 insertions, 130 deletions
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d56e9fc1e..32b7be807 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -17,7 +17,6 @@ package com.android.launcher3.allapps;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
-import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -155,6 +154,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
private int mSectionNamesMargin;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
+ private int mRecyclerViewTopBottomPadding;
// This coordinate is relative to this container view
private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
// This coordinate is relative to its parent
@@ -189,7 +189,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) +
2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) +
- 2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding));
+ res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_padding) +
+ res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding));
mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
mApps = new AlphabeticalAppsList(context);
mApps.setAdapterChangedCallback(this);
@@ -199,6 +200,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
+ mRecyclerViewTopBottomPadding =
+ res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding);
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
@@ -414,7 +417,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
- mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
+ mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
}
@@ -431,14 +434,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
boolean isRtl = Utilities.isRtl(getResources());
- // TODO: Use quantum_panel instead of quantum_panel_shape.
+ // TODO: Use quantum_panel instead of quantum_panel_shape
InsetDrawable background = new InsetDrawable(
getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0,
padding.right, 0);
+ Rect bgPadding = new Rect();
+ background.getPadding(bgPadding);
mContainerView.setBackground(background);
mRevealView.setBackground(background.getConstantState().newDrawable());
- mAppsRecyclerView.updateBackgroundPadding(padding);
- mAdapter.updateBackgroundPadding(padding);
+ mAppsRecyclerView.updateBackgroundPadding(bgPadding);
+ mAdapter.updateBackgroundPadding(bgPadding);
// Hack: We are going to let the recycler view take the full width, so reset the padding on
// the container to zero after setting the background and apply the top-bottom padding to
@@ -448,13 +453,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// Pad the recycler view by the background padding plus the start margin (for the section
// names)
- int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getScrollbarWidth());
+ int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getMaxScrollbarWidth());
+ int topBottomPadding = mRecyclerViewTopBottomPadding;
if (isRtl) {
- mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getScrollbarWidth(), 0,
- padding.right + startInset, 0);
+ mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getMaxScrollbarWidth(),
+ topBottomPadding, padding.right + startInset, topBottomPadding);
} else {
- mAppsRecyclerView.setPadding(padding.left + startInset, 0,
- padding.right + mAppsRecyclerView.getScrollbarWidth(), 0);
+ mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
+ padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), topBottomPadding);
}
// Inset the search bar to fit its bounds above the container
@@ -474,8 +480,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// Update the prediction bar insets as well
mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
- lp.leftMargin = padding.left + mAppsRecyclerView.getScrollbarWidth();
- lp.rightMargin = padding.right + mAppsRecyclerView.getScrollbarWidth();
+ lp.leftMargin = padding.left + mAppsRecyclerView.getMaxScrollbarWidth();
+ lp.rightMargin = padding.right + mAppsRecyclerView.getMaxScrollbarWidth();
mPredictionBarView.requestLayout();
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 68407bdd5..19e2757f9 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -337,7 +337,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
mPredictedAppsDividerPaint.setColor(0x1E000000);
mPredictedAppsDividerPaint.setAntiAlias(true);
mPredictionBarBottomPadding =
- res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding);
+ res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding);
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index ff327dae3..a17f0e35d 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -22,10 +22,10 @@ import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.Stats;
-import com.android.launcher3.Utilities;
import java.util.List;
@@ -35,13 +35,25 @@ import java.util.List;
public class AllAppsRecyclerView extends BaseRecyclerView
implements Stats.LaunchSourceProvider {
+ private static final int FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON = 0;
+ private static final int FAST_SCROLL_MODE_FREE_SCROLL = 1;
+
+ private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW = 0;
+ private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS = 1;
+
private AlphabeticalAppsList mApps;
private int mNumAppsPerRow;
- private int mNumPredictedAppsPerRow;
private int mPredictionBarHeight;
- private int mLastFastscrollPosition = -1;
+
+ private BaseRecyclerViewFastScrollBar.FastScrollFocusableView mLastFastScrollFocusedView;
+ private int mPrevFastScrollFocusedPosition;
+ private int mFastScrollFrameIndex;
+ private int[] mFastScrollFrames = new int[10];
+ private final int mFastScrollMode = FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON;
+ private final int mScrollBarMode = FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW;
private Launcher mLauncher;
+ private ScrollPositionState mScrollPosState = new ScrollPositionState();
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -59,6 +71,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
int defStyleRes) {
super(context, attrs, defStyleAttr);
mLauncher = (Launcher) context;
+ setOverScrollMode(View.OVER_SCROLL_NEVER);
}
/**
@@ -71,9 +84,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
/**
* Sets the number of apps per row in this recycler view.
*/
- public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
+ public void setNumAppsPerRow(int numAppsPerRow) {
mNumAppsPerRow = numAppsPerRow;
- mNumPredictedAppsPerRow = numPredictedAppsPerRow;
DeviceProfile grid = mLauncher.getDeviceProfile();
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
@@ -103,11 +115,12 @@ public class AllAppsRecyclerView extends BaseRecyclerView
*/
public int getScrollPosition() {
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- getCurScrollState(scrollPosState, items);
- if (scrollPosState.rowIndex != -1) {
+ getCurScrollState(mScrollPosState, items);
+ if (mScrollPosState.rowIndex != -1) {
int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
- return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) +
- predictionBarHeight - scrollPosState.rowTopOffset;
+ return getPaddingTop() + predictionBarHeight +
+ (mScrollPosState.rowIndex * mScrollPosState.rowHeight) -
+ mScrollPosState.rowTopOffset;
}
return 0;
}
@@ -132,143 +145,159 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
}
- @Override
- protected void onFastScrollingEnd() {
- mLastFastscrollPosition = -1;
- }
-
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
@Override
public String scrollToPositionAtProgress(float touchFraction) {
- // Ensure that we have any sections
- List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
- mApps.getFastScrollerSections();
- if (fastScrollSections.isEmpty()) {
+ int rowCount = mApps.getNumAppRows();
+ if (rowCount == 0) {
return "";
}
// Stop the scroller if it is scrolling
- LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
stopScroll();
- // If there is a prediction bar, then capture the appropriate area for the prediction bar
- float predictionBarFraction = 0f;
- if (!mApps.getPredictedApps().isEmpty()) {
- predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize();
- if (touchFraction <= predictionBarFraction) {
- // Scroll to the top of the view, where the prediction bar is
- layoutManager.scrollToPositionWithOffset(0, 0);
- return "";
+ // Find the fastscroll section that maps to this touch fraction
+ List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
+ mApps.getFastScrollerSections();
+ AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0);
+ if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW) {
+ for (int i = 1; i < fastScrollSections.size(); i++) {
+ AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i);
+ if (info.touchFraction > touchFraction) {
+ break;
+ }
+ lastInfo = info;
}
+ } else if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS){
+ lastInfo = fastScrollSections.get((int) (touchFraction * (fastScrollSections.size() - 1)));
+ } else {
+ throw new RuntimeException("Unexpected scroll bar mode");
}
- // Since the app ranges are from 0..1, we need to map the touch fraction back to 0..1 from
- // predictionBarFraction..1
- touchFraction = (touchFraction - predictionBarFraction) *
- (1f / (1f - predictionBarFraction));
- AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0);
- for (int i = 1; i < fastScrollSections.size(); i++) {
- AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i);
- if (lastScrollSection.appRangeFraction <= touchFraction &&
- touchFraction < scrollSection.appRangeFraction) {
- break;
- }
- lastScrollSection = scrollSection;
+ // Map the touch position back to the scroll of the recycler view
+ getCurScrollState(mScrollPosState, mApps.getAdapterItems());
+ int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
+ int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight,
+ predictionBarHeight);
+ LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+ if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
+ layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
}
- // Scroll to the view at the position, anchored at the top of the screen. We call the scroll
- // method on the LayoutManager directly since it is not exposed by RecyclerView.
- if (mLastFastscrollPosition != lastScrollSection.appItem.position) {
- mLastFastscrollPosition = lastScrollSection.appItem.position;
- layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
- }
+ if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) {
+ mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position;
- return lastScrollSection.sectionName;
- }
+ // Reset the last focused view
+ if (mLastFastScrollFocusedView != null) {
+ mLastFastScrollFocusedView.setFastScrollFocused(false, true);
+ mLastFastScrollFocusedView = null;
+ }
- /**
- * Returns the row index for a app index in the list.
- */
- private int findRowForAppIndex(int index) {
- List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
- int appIndex = 0;
- int rowCount = 0;
- for (AlphabeticalAppsList.SectionInfo info : sections) {
- int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
- if (appIndex + info.numApps > index) {
- return rowCount + ((index - appIndex) / mNumAppsPerRow);
+ if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) {
+ smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState);
+ } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
+ final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
+ if (vh != null &&
+ vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
+ mLastFastScrollFocusedView =
+ (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
+ mLastFastScrollFocusedView.setFastScrollFocused(true, true);
+ }
+ } else {
+ throw new RuntimeException("Unexpected fast scroll mode");
}
- appIndex += info.numApps;
- rowCount += numRowsInSection;
}
- return appIndex;
+ return lastInfo.sectionName;
}
- /**
- * Returns the total number of rows in the list.
- */
- private int getNumRows() {
- List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
- int rowCount = 0;
- for (AlphabeticalAppsList.SectionInfo info : sections) {
- int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
- rowCount += numRowsInSection;
+ @Override
+ public void onFastScrollCompleted() {
+ super.onFastScrollCompleted();
+ // Reset and clean up the last focused view
+ if (mLastFastScrollFocusedView != null) {
+ mLastFastScrollFocusedView.setFastScrollFocused(false, true);
+ mLastFastScrollFocusedView = null;
}
- return rowCount;
+ mPrevFastScrollFocusedPosition = -1;
}
-
/**
* Updates the bounds for the scrollbar.
*/
@Override
- public void updateVerticalScrollbarBounds() {
+ public void onUpdateScrollbar() {
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
if (items.isEmpty() || mNumAppsPerRow == 0) {
- verticalScrollbarBounds.setEmpty();
+ mScrollbar.setScrollbarThumbOffset(-1, -1);
return;
}
// Find the index and height of the first visible row (all rows have the same height)
- int x, y;
+ int rowCount = mApps.getNumAppRows();
+ getCurScrollState(mScrollPosState, items);
+ if (mScrollPosState.rowIndex < 0) {
+ mScrollbar.setScrollbarThumbOffset(-1, -1);
+ return;
+ }
+
int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
- int rowCount = getNumRows();
- getCurScrollState(scrollPosState, items);
- if (scrollPosState.rowIndex != -1) {
- int height = getHeight() - getPaddingTop() - getPaddingBottom();
- int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight;
- if (totalScrollHeight > height) {
- int scrollbarHeight = (int) (height / ((float) totalScrollHeight / height));
-
- // Calculate the position and size of the scroll bar
- if (Utilities.isRtl(getResources())) {
- x = mBackgroundPadding.left;
- } else {
- x = getWidth() - mBackgroundPadding.right - getScrollbarWidth();
- }
+ synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, predictionBarHeight);
+ }
- // 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 = (scrollPosState.rowIndex * scrollPosState.rowHeight) + predictionBarHeight
- - scrollPosState.rowTopOffset;
- y = getPaddingTop() +
- (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY);
-
- verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight);
- return;
+ /**
+ * This runnable runs a single frame of the smooth scroll animation and posts the next frame
+ * if necessary.
+ */
+ private Runnable mSmoothSnapNextFrameRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mFastScrollFrameIndex < mFastScrollFrames.length) {
+ scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]);
+ mFastScrollFrameIndex++;
+ postOnAnimation(mSmoothSnapNextFrameRunnable);
+ } else {
+ // Animation completed, set the fast scroll state on the target view
+ final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
+ if (vh != null &&
+ vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView &&
+ mLastFastScrollFocusedView != vh.itemView) {
+ mLastFastScrollFocusedView =
+ (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
+ mLastFastScrollFocusedView.setFastScrollFocused(true, true);
+ }
}
}
- verticalScrollbarBounds.setEmpty();
+ };
+
+ /**
+ * Smoothly snaps to a given position. We do this manually by calculating the keyframes
+ * ourselves and animating the scroll on the recycler view.
+ */
+ private void smoothSnapToPosition(final int position, ScrollPositionState scrollPosState) {
+ removeCallbacks(mSmoothSnapNextFrameRunnable);
+
+ // Calculate the full animation from the current scroll position to the final scroll
+ // position, and then run the animation for the duration.
+ int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
+ int curScrollY = getPaddingTop() + predictionBarHeight +
+ (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
+ int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight);
+ int numFrames = mFastScrollFrames.length;
+ for (int i = 0; i < numFrames; i++) {
+ // TODO(winsonc): We can interpolate this as well.
+ mFastScrollFrames[i] = (newScrollY - curScrollY) / numFrames;
+ }
+ mFastScrollFrameIndex = 0;
+ postOnAnimation(mSmoothSnapNextFrameRunnable);
}
/**
- * Returns the current scroll state.
+ * Returns the current scroll state of the apps rows, not including the prediction
+ * bar.
*/
private void getCurScrollState(ScrollPositionState stateOut,
List<AlphabeticalAppsList.AdapterItem> items) {
@@ -288,7 +317,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
if (position != NO_POSITION) {
AlphabeticalAppsList.AdapterItem item = items.get(position);
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
- stateOut.rowIndex = findRowForAppIndex(item.appIndex);
+ stateOut.rowIndex = item.rowIndex;
stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
stateOut.rowHeight = child.getHeight();
break;
@@ -296,4 +325,17 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
}
}
+
+ /**
+ * Returns the scrollY for the given position in the adapter.
+ */
+ private int getScrollAtPosition(int position, int rowHeight) {
+ AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
+ if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
+ int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
+ return getPaddingTop() + predictionBarHeight + item.rowIndex * rowHeight;
+ } else {
+ return 0;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index aa73c74cf..ea99872ed 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -62,15 +62,13 @@ public class AlphabeticalAppsList {
public static class FastScrollSectionInfo {
// The section name
public String sectionName;
- // To map the touch (from 0..1) to the index in the app list to jump to in the fast
- // scroller, we use the fraction in range (0..1) of the app index / total app count.
- public float appRangeFraction;
// The AdapterItem to scroll to for this section
- public AdapterItem appItem;
+ public AdapterItem fastScrollToItem;
+ // The touch fraction that should map to this fast scroll section info
+ public float touchFraction;
- public FastScrollSectionInfo(String sectionName, float appRangeFraction) {
+ public FastScrollSectionInfo(String sectionName) {
this.sectionName = sectionName;
- this.appRangeFraction = appRangeFraction;
}
}
@@ -83,6 +81,8 @@ public class AlphabeticalAppsList {
public int position;
// The type of this item
public int viewType;
+ // The row that this item shows up on
+ public int rowIndex;
/** Section & App properties */
// The section for this item
@@ -94,6 +94,8 @@ public class AlphabeticalAppsList {
public String sectionName = null;
// The index of this app in the section
public int sectionAppIndex = -1;
+ // The index of this app in the row
+ public int rowAppIndex;
// The associated AppInfo for the app
public AppInfo appInfo = null;
// The index of this app not including sections
@@ -172,6 +174,7 @@ public class AlphabeticalAppsList {
private AdapterChangedCallback mAdapterChangedCallback;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
+ private int mNumAppRowsInAdapter;
public AlphabeticalAppsList(Context context) {
mLauncher = (Launcher) context;
@@ -241,6 +244,13 @@ public class AlphabeticalAppsList {
}
/**
+ * Returns the number of rows of applications (not including predictions)
+ */
+ public int getNumAppRows() {
+ return mNumAppRowsInAdapter;
+ }
+
+ /**
* Returns whether there are is a filter set.
*/
public boolean hasFilter() {
@@ -419,23 +429,23 @@ public class AlphabeticalAppsList {
// Create a new spacer for the prediction bar
AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++);
mAdapterItems.add(sectionItem);
+ // Add a fastscroller section for the prediction bar
+ lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
+ lastFastScrollerSectionInfo.fastScrollToItem = sectionItem;
+ mFastScrollerSections.add(lastFastScrollerSectionInfo);
}
}
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
- List<AppInfo> apps = getFiltersAppInfos();
- int numApps = apps.size();
- for (int i = 0; i < numApps; i++) {
- AppInfo info = apps.get(i);
+ for (AppInfo info : getFiltersAppInfos()) {
String sectionName = getAndUpdateCachedSectionName(info.title);
// Create a new section if the section names do not match
if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
lastSectionName = sectionName;
lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName,
- (float) appIndex / numApps);
+ lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
mSections.add(lastSectionInfo);
mFastScrollerSections.add(lastFastScrollerSectionInfo);
@@ -451,7 +461,7 @@ public class AlphabeticalAppsList {
lastSectionInfo.numApps++, info, appIndex++);
if (lastSectionInfo.firstAppItem == null) {
lastSectionInfo.firstAppItem = appItem;
- lastFastScrollerSectionInfo.appItem = appItem;
+ lastFastScrollerSectionInfo.fastScrollToItem = appItem;
}
mAdapterItems.add(appItem);
mFilteredApps.add(info);
@@ -460,6 +470,45 @@ public class AlphabeticalAppsList {
// Merge multiple sections together as requested by the merge strategy for this device
mergeSections();
+ if (mNumAppsPerRow != 0) {
+ // Update the number of rows in the adapter after we do all the merging (otherwise, we
+ // would have to shift the values again)
+ int numAppsInSection = 0;
+ int numAppsInRow = 0;
+ int rowIndex = -1;
+ for (AdapterItem item : mAdapterItems) {
+ item.rowIndex = 0;
+ if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) {
+ numAppsInSection = 0;
+ } else if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
+ if (numAppsInSection % mNumAppsPerRow == 0) {
+ numAppsInRow = 0;
+ rowIndex++;
+ }
+ item.rowIndex = rowIndex;
+ item.rowAppIndex = numAppsInRow;
+ numAppsInSection++;
+ numAppsInRow++;
+ }
+ }
+ mNumAppRowsInAdapter = rowIndex + 1;
+
+ // Pre-calculate all the fast scroller fractions based on the number of rows, if we have
+ // predicted apps, then we should account for that as a row in the touchFraction
+ float rowFraction = 1f / (mNumAppRowsInAdapter + (mPredictedApps.isEmpty() ? 0 : 1));
+ float initialOffset = mPredictedApps.isEmpty() ? 0 : rowFraction;
+ for (FastScrollSectionInfo info : mFastScrollerSections) {
+ AdapterItem item = info.fastScrollToItem;
+ if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+ info.touchFraction = 0f;
+ continue;
+ }
+
+ float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow);
+ info.touchFraction = initialOffset + item.rowIndex * rowFraction + subRowFraction;
+ }
+ }
+
// Refresh the recycler view
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
@@ -511,6 +560,7 @@ public class AlphabeticalAppsList {
// Remove the next section break
mAdapterItems.remove(nextSection.sectionBreakItem);
int pos = mAdapterItems.indexOf(section.firstAppItem);
+
// Point the section for these new apps to the merged section
int nextPos = pos + section.numApps;
for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {