Refactoring fast scroller.

- Fixing issue with fast scroller not fitting name width.
- Refactoring fast scrolling/scroll bar code out of base recycler view
- Adding animations to fast scroller to match design
- Smooth scrolling when jumping between app rows
- Fixing issue with fast scroller jumping when you first pick it up
- Fixing issue with wrong background paddings being used

Bug: 21874346
Bug: 22031923
Change-Id: I9f011b1f375751f437604b900e95a2942d3f4601
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 6c13b4a..a0be8ea 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -24,6 +25,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -34,6 +36,8 @@
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 import android.widget.TextView;
 import com.android.launcher3.IconCache.IconLoadRequest;
 import com.android.launcher3.model.PackageItemInfo;
@@ -43,7 +47,8 @@
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView {
+public class BubbleTextView extends TextView
+        implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView {
 
     private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
 
@@ -56,6 +61,13 @@
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
 
+    private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
+    private static final int FAST_SCROLL_FOCUS_MODE_NONE = 0;
+    private static final int FAST_SCROLL_FOCUS_MODE_SCALE_ICON = 1;
+    private static final int FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG = 2;
+    private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
+    private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;
+
     private final Launcher mLauncher;
     private Drawable mIcon;
     private final Drawable mBackground;
@@ -79,6 +91,12 @@
     private boolean mIgnorePressedStateChange;
     private boolean mDisableRelayout = false;
 
+    private ObjectAnimator mFastScrollFocusAnimator;
+    private Paint mFastScrollFocusBgPaint;
+    private float mFastScrollFocusFraction;
+    private boolean mFastScrollFocused;
+    private final int mFastScrollMode = FAST_SCROLL_FOCUS_MODE_SCALE_ICON;
+
     private IconLoadRequest mIconLoadRequest;
 
     public BubbleTextView(Context context) {
@@ -131,6 +149,13 @@
             setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
         }
 
+        if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG) {
+            mFastScrollFocusBgPaint = new Paint();
+            mFastScrollFocusBgPaint.setAntiAlias(true);
+            mFastScrollFocusBgPaint.setColor(
+                    getResources().getColor(R.color.container_fastscroll_thumb_active_color));
+        }
+
         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
@@ -335,7 +360,18 @@
     @Override
     public void draw(Canvas canvas) {
         if (!mCustomShadowsEnabled) {
+            // Draw the fast scroll focus bg if we have one
+            if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG &&
+                    mFastScrollFocusFraction > 0f) {
+                DeviceProfile grid = mLauncher.getDeviceProfile();
+                int iconCenterX = getScrollX() + (getWidth() / 2);
+                int iconCenterY = getScrollY() + getPaddingTop() + (grid.iconSizePx / 2);
+                canvas.drawCircle(iconCenterX, iconCenterY,
+                        mFastScrollFocusFraction * (getWidth() / 2), mFastScrollFocusBgPaint);
+            }
+
             super.draw(canvas);
+
             return;
         }
 
@@ -538,6 +574,51 @@
         }
     }
 
+    // Setters & getters for the animation
+    public void setFastScrollFocus(float fraction) {
+        mFastScrollFocusFraction = fraction;
+        if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_SCALE_ICON) {
+            setScaleX(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
+            setScaleY(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
+        } else {
+            invalidate();
+        }
+    }
+
+    public float getFastScrollFocus() {
+        return mFastScrollFocusFraction;
+    }
+
+    @Override
+    public void setFastScrollFocused(final boolean focused, boolean animated) {
+        if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) {
+            return;
+        }
+
+        if (mFastScrollFocused != focused) {
+            mFastScrollFocused = focused;
+
+            if (animated) {
+                // Clean up the previous focus animator
+                if (mFastScrollFocusAnimator != null) {
+                    mFastScrollFocusAnimator.cancel();
+                }
+                mFastScrollFocusAnimator = ObjectAnimator.ofFloat(this, "fastScrollFocus",
+                        focused ? 1f : 0f);
+                if (focused) {
+                    mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
+                } else {
+                    mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
+                }
+                mFastScrollFocusAnimator.setDuration(focused ?
+                        FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
+                mFastScrollFocusAnimator.start();
+            } else {
+                mFastScrollFocusFraction = focused ? 1f : 0f;
+            }
+        }
+    }
+
     /**
      * Interface to be implemented by the grand parent to allow click shadow effect.
      */