Consistent scrolling experience for All apps and widget tray

b/21375339

Change-Id: I8362b3ca94b7c4e75932d42cd09a989e0e3919c0
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 8d418f9..a207d9a 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -86,10 +86,8 @@
 
     private int mDownX;
     private int mDownY;
-    private int mLastX;
     private int mLastY;
     private int mScrollbarWidth;
-    private int mScrollbarMinHeight;
     private int mScrollbarInset;
     private Rect mBackgroundPadding = new Rect();
 
@@ -121,8 +119,6 @@
         mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize(
                 R.dimen.all_apps_fast_scroll_text_size));
         mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width);
-        mScrollbarMinHeight =
-                res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height);
         mScrollbarInset =
                 res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset);
         setFastScrollerAlpha(mFastScrollAlpha);
@@ -173,7 +169,7 @@
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 // Keep track of the down positions
-                mDownX = mLastX = x;
+                mDownX = x;
                 mDownY = mLastY = y;
                 if (shouldStopScroll(ev)) {
                     stopScroll();
@@ -188,7 +184,6 @@
                     animateFastScrollerVisibility(true);
                 }
                 if (mDraggingFastScroller) {
-                    mLastX = x;
                     mLastY = y;
 
                     // Scroll to the right position, and update the section name
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index 0f0134a..30f228c 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.model;
 
-import android.content.ComponentName;
 import android.graphics.Bitmap;
 
 import com.android.launcher3.ItemInfo;
@@ -27,7 +26,6 @@
  * Represents a {@link Package} in the widget tray section.
  */
 public class PackageItemInfo extends ItemInfo {
-    private static final String TAG = "PackageInfo";
 
     /**
      * A bitmap version of the application icon.
@@ -35,12 +33,21 @@
     public Bitmap iconBitmap;
 
     /**
-     * Indicates whether we're using a low res icon
+     * Indicates whether we're using a low res icon.
      */
     public boolean usingLowResIcon;
 
+    /**
+     * Package name of the {@link ItemInfo}.
+     */
     public String packageName;
 
+    /**
+     * Character that is used as a section name for the {@link ItemInfo#title}.
+     * (e.g., "G" will be stored if title is "Google")
+     */
+    public String titleSectionName;
+
     int flags = 0;
 
     PackageItemInfo(String packageName) {
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index fdb9795..76e6a9d 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -10,6 +10,7 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 
 import java.util.ArrayList;
@@ -39,11 +40,13 @@
     private final Comparator mWidgetAndShortcutNameComparator;
     private final Comparator mAppNameComparator;
     private final IconCache mIconCache;
+    private AlphabeticIndexCompat mIndexer;
 
     public WidgetsModel(Context context) {
         mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
         mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
         mIconCache = LauncherAppState.getInstance().getIconCache();
+        mIndexer = new AlphabeticIndexCompat(context);
     }
 
     private WidgetsModel(WidgetsModel model) {
@@ -62,6 +65,9 @@
 
     // Access methods that may be deleted if the private fields are made package-private.
     public PackageItemInfo getPackageItemInfo(int pos) {
+        if (pos >= mPackageItemInfos.size() || pos < 0) {
+            return null;
+        }
         return mPackageItemInfos.get(pos);
     }
 
@@ -112,6 +118,7 @@
                 pInfo = new PackageItemInfo(packageName);
                 mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
                         true /* userLowResIcon */, pInfo);
+                pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title);
                 mWidgetsList.put(pInfo, widgetsShortcutsList);
                 tmpPackageItemInfos.put(packageName,  pInfo);
                 mPackageItemInfos.add(pInfo);
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 11c2107..18b93c3 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -20,8 +20,8 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.State;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -92,7 +92,6 @@
         mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
         mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
-
         if (DEBUG) {
             Log.d(TAG, "WidgetsContainerView constructor");
         }
@@ -345,6 +344,23 @@
             setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
                     mFixedBounds.bottom);
         }
+
+        int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset;
+        mView.setPadding(inset + mView.getScrollbarWidth(), inset,
+                inset, inset);
+    }
+
+    @Override
+    protected void onUpdateBackgrounds() {
+        InsetDrawable background;
+        // Update the background of the reveal view and list to be inset with the fixed bound
+        // insets instead of the default insets
+        // TODO: Use quantum_panel instead of quantum_panel_shape.
+        int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset;
+        background = new InsetDrawable(
+                getContext().getResources().getDrawable(R.drawable.quantum_panel_shape),
+                inset, 0, inset, 0);
+        mView.updateBackgroundPadding(background);
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index bef2559..4aa3323 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -19,20 +19,25 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.LinearLayoutManager;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
+import android.view.View;
 
 import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.PackageItemInfo;
 
 /**
  * The widgets recycler view.
  */
 public class WidgetsRecyclerView extends BaseRecyclerView {
 
+    private static final String TAG = "WidgetsRecyclerView";
     private WidgetsModel mWidgets;
     private Rect mBackgroundPadding = new Rect();
+    private PackageItemInfo mLastPackageItemInfo;
 
     public WidgetsRecyclerView(Context context) {
         this(context, null);
@@ -68,8 +73,17 @@
      */
     @Override
     public String scrollToPositionAtProgress(float touchFraction) {
-        // Ensure that we have any sections
-        return "";
+        float pos = mWidgets.getPackageSize() * touchFraction;
+
+        int posInt = (int) pos;
+        LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
+        getCurScrollState(scrollPosState);
+        layoutManager.scrollToPositionWithOffset((int) pos,
+                (int) (scrollPosState.rowHeight * ((float) posInt - pos)));
+
+        posInt = (int) ((touchFraction == 1)? pos -1 : pos);
+        PackageItemInfo p = mWidgets.getPackageItemInfo(posInt);
+        return p.titleSectionName;
     }
 
     /**
@@ -78,19 +92,41 @@
     @Override
     public void updateVerticalScrollbarBounds() {
         int rowCount = mWidgets.getPackageSize();
+        verticalScrollbarBounds.setEmpty();
 
-        // Skip early if there are no items.
+        // Skip early if, there are no items.
         if (rowCount == 0) {
+            return;
+        }
+
+        // Skip early if, there no child laid out in the container.
+        getCurScrollState(scrollPosState);
+        if (scrollPosState.rowIndex < 0) {
+            return;
+        }
+
+        int actualHeight = getHeight() - getPaddingTop() - getPaddingBottom();
+        int totalScrollHeight = rowCount * scrollPosState.rowHeight;
+        // Skip early if the height of all the rows are actually less than the container height.
+        if (totalScrollHeight < actualHeight) {
             verticalScrollbarBounds.setEmpty();
             return;
         }
 
-        int x, y;
-        getCurScrollState(scrollPosState);
-        if (scrollPosState.rowIndex < 0) {
-            verticalScrollbarBounds.setEmpty();
+        int scrollbarHeight = (int) (actualHeight / ((float) totalScrollHeight / actualHeight));
+        int availableY = totalScrollHeight - actualHeight;
+        int availableScrollY = actualHeight - scrollbarHeight;
+        int y = (scrollPosState.rowIndex * scrollPosState.rowHeight)
+                - scrollPosState.rowTopOffset;
+        y = getPaddingTop() +
+                (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY);
+
+        // Calculate the position and size of the scroll bar.
+        int x = getWidth() - getScrollbarWidth() - mBackgroundPadding.right;
+        if (Utilities.isRtl(getResources())) {
+            x = mBackgroundPadding.left;
         }
-        // TODO
+        verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight);
     }
 
     /**
@@ -107,6 +143,11 @@
         if (rowCount == 0) {
             return;
         }
-        // TODO
+        View child = getChildAt(0);
+        int position = getChildPosition(child);
+
+        stateOut.rowIndex = position;
+        stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
+        stateOut.rowHeight = child.getHeight();
     }
 }
\ No newline at end of file