Merge "Bitmap resource should not be scaled on lower density devices Bug: 63008339" into ub-launcher3-dorval-polish
diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..957c331
--- /dev/null
+++ b/res/layout-land/all_apps_fast_scroller.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- Fast scroller popup -->
+    <TextView
+        android:id="@+id/fast_scroller_popup"
+        style="@style/FastScrollerPopup"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="-5dp" />
+
+    <com.android.launcher3.allapps.LandscapeFastScroller
+        android:id="@+id/fast_scroller"
+        android:layout_width="48dp"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="-48dp"
+        android:layout_marginTop="-8dp"
+        launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout-sw720dp/all_apps_fast_scroller.xml b/res/layout-sw720dp/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..12c15cc
--- /dev/null
+++ b/res/layout-sw720dp/all_apps_fast_scroller.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- Fast scroller popup -->
+    <TextView
+        android:id="@+id/fast_scroller_popup"
+        style="@style/FastScrollerPopup"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+    <com.android.launcher3.views.RecyclerViewFastScroller
+        android:id="@+id/fast_scroller"
+        android:layout_width="@dimen/fastscroll_width"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_end_margin"
+        launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index a3c2535..93662fc 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -16,7 +16,8 @@
 <!-- The top and bottom paddings are defined in this container, but since we want
      the list view to span the full width (for touch interception purposes), we
      will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.allapps.AllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.AllAppsContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
@@ -39,6 +40,8 @@
         android:layout_height="match_parent"
         android:layout_gravity="center"
         android:focusable="true"
+        android:clipToPadding="false"
+        android:clipChildren="true"
         android:focusableInTouchMode="true"
         android:saveEnabled="false"
         android:visibility="gone">
@@ -55,20 +58,14 @@
             android:descendantFocusability="afterDescendants"
             android:focusable="true" />
 
-        <!-- Fast scroller popup -->
-        <TextView
-            style="@style/FastScrollerPopup"
-            android:layout_alignTop="@+id/apps_list_view"
-            android:id="@+id/fast_scroller_popup"
-            android:layout_alignParentEnd="true"
-            android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
-
         <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
          platform bug, which prevents using custom attributes in <include> tag -->
         <include
             layout="?android:attr/keyboardLayout"
             android:id="@+id/search_container" />
 
+        <include layout="@layout/all_apps_fast_scroller" />
+
     </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
     <View
         style="@style/AllAppsNavBarProtection"
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..12c15cc
--- /dev/null
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- Fast scroller popup -->
+    <TextView
+        android:id="@+id/fast_scroller_popup"
+        style="@style/FastScrollerPopup"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+    <com.android.launcher3.views.RecyclerViewFastScroller
+        android:id="@+id/fast_scroller"
+        android:layout_width="@dimen/fastscroll_width"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_end_margin"
+        launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout/all_apps_search_container.xml b/res/layout/all_apps_search_container.xml
index c79360f..2528034 100644
--- a/res/layout/all_apps_search_container.xml
+++ b/res/layout/all_apps_search_container.xml
@@ -20,13 +20,24 @@
     android:layout_height="@dimen/all_apps_search_bar_height"
     android:layout_gravity="center|top"
     android:gravity="center|bottom"
-    android:saveEnabled="false">
+    android:saveEnabled="false"
+    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+    android:paddingRight="@dimen/dynamic_grid_edge_margin"
+    android:layout_marginBottom="-8dp" >
 
+    <!--
+      Note: The following relation should always be true so that the shadows are aligned properly
+      search_box_input.layout_marginBottom
+            == search_divider.layout_marginBottom
+            == - (search_container.layout_marginBottom)
+            >= 5dp
+    -->
     <com.android.launcher3.ExtendedEditText
         android:id="@+id/search_box_input"
         android:layout_width="match_parent"
         android:layout_height="@dimen/all_apps_search_bar_field_height"
         android:layout_gravity="bottom"
+        android:layout_marginBottom="8dp"
         android:background="@android:color/transparent"
         android:focusableInTouchMode="true"
         android:gravity="center"
@@ -39,4 +50,19 @@
         android:textColor="?android:attr/textColorSecondary"
         android:textColorHint="@drawable/all_apps_search_hint"
         android:textSize="16sp" />
+
+    <!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
+         We translate the header down, and its content up by the same amount, so that it gets
+         clipped by the parent, making it look like the divider was scrolled out of the view. -->
+    <FrameLayout
+        android:id="@+id/search_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="8dp" >
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="@drawable/all_apps_search_divider"/>
+    </FrameLayout>
 </com.android.launcher3.allapps.search.AppsSearchContainerLayout>
\ No newline at end of file
diff --git a/res/layout/all_apps_search_divider.xml b/res/layout/all_apps_search_divider.xml
deleted file mode 100644
index c052c66..0000000
--- a/res/layout/all_apps_search_divider.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:importantForAccessibility="no"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
-    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
-    android:paddingRight="@dimen/dynamic_grid_edge_margin"
-    android:src="@drawable/all_apps_search_divider"
-    android:scaleType="fitXY"
-    android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index f1370f0..e8c6961 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -22,7 +22,8 @@
     android:paddingTop="28dp"
     android:background="?android:attr/colorPrimary"
     android:elevation="@dimen/deep_shortcuts_elevation"
-    android:layout_gravity="bottom">
+    android:layout_gravity="bottom"
+    android:theme="?attr/widgetsTheme">
 
     <TextView
         style="@style/TextTitle"
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 47b0683..4f3c7c8 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -23,25 +23,25 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:descendantFocusability="afterDescendants"
-    launcher:revealBackground="@drawable/round_rect_primary"
-    android:theme="@style/WidgetContainerTheme">
+    android:theme="?attr/widgetsTheme"
+    launcher:revealBackground="@drawable/round_rect_primary">
 
     <View
         android:id="@+id/reveal_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center"
-        android:focusable="false"
         android:elevation="2dp"
+        android:focusable="false"
         android:visibility="invisible" />
 
     <FrameLayout
         android:id="@+id/main_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:layout_gravity="center"
         android:elevation="15dp"
-        android:visibility="gone"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:visibility="gone">
 
         <com.android.launcher3.widget.WidgetsRecyclerView
             android:id="@+id/widgets_list_view"
@@ -50,17 +50,24 @@
 
         <!-- Fast scroller popup -->
         <TextView
-            style="@style/FastScrollerPopup"
             android:id="@+id/fast_scroller_popup"
+            style="@style/FastScrollerPopup"
             android:layout_gravity="top|end"
             android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
 
         <ProgressBar
+            android:id="@+id/loader"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:id="@+id/loader"
             android:layout_gravity="center" />
-    </FrameLayout>
 
+        <com.android.launcher3.views.RecyclerViewFastScroller
+            android:id="@+id/fast_scroller"
+            android:layout_width="@dimen/fastscroll_width"
+            android:layout_height="match_parent"
+            android:layout_gravity="end"
+            android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+    </FrameLayout>
 
 </com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
index cb18409..fd6fc4d 100644
--- a/res/values-v26/styles.xml
+++ b/res/values-v26/styles.xml
@@ -21,6 +21,10 @@
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
     </style>
+    <style name="WidgetContainerTheme.Dark" parent="LauncherThemeDark">
+        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+        <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
+    </style>
 
     <!-- From O and above, we show a dark nav bar in all-apps -->
     <style name="AllAppsNavBarProtection">
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 7929e65..7b52dae 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -30,6 +30,7 @@
     <attr name="workspaceAmbientShadowColor" format="color"/>
     <attr name="workspaceKeyShadowColor" format="color" />
     <attr name="workspaceStatusBarScrim" format="reference" />
+    <attr name="widgetsTheme" format="reference" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
@@ -137,4 +138,8 @@
         <attr name="android:elevation" />
         <attr name="darkTintColor" format="color"/>
     </declare-styleable>
+
+    <declare-styleable name="RecyclerViewFastScroller">
+        <attr name="canThumbDetach" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 71f9edc..a4dff71 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -65,7 +65,15 @@
     <dimen name="fastscroll_popup_text_size">32dp</dimen>
     <dimen name="fastscroll_popup_margin">19dp</dimen>
 
-<!-- All Apps -->
+    <!--
+      Fast scroller draws the content horizontally centered. The end of the track should be
+      aligned at the end of the container.
+        fastscroll_end_margin = - (fastscroll_width - fastscroll_track_min_width) / 2
+    -->
+    <dimen name="fastscroll_width">58dp</dimen>
+    <dimen name="fastscroll_end_margin">-26dp</dimen>
+
+    <!-- All Apps -->
     <dimen name="all_apps_button_scale_down">0dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
     <dimen name="all_apps_search_bar_height">60dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8943a45..d11b002 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -40,6 +40,7 @@
         <item name="workspaceAmbientShadowColor">#33000000</item>
         <item name="workspaceKeyShadowColor">#44000000</item>
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
+        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
     </style>
 
     <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs"></style>
@@ -64,6 +65,7 @@
         <item name="popupColorPrimary">?android:attr/colorPrimary</item>
         <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
         <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+        <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="isMainColorDark">true</item>
     </style>
 
@@ -85,6 +87,8 @@
         <item name="android:textColorSecondary">?android:attr/textColorSecondaryInverse</item>
     </style>
 
+    <style name="WidgetContainerTheme.Dark" />
+
     <style name="FastScrollerPopup" >
         <item name="android:layout_width">wrap_content</item>
         <item name="android:minWidth">@dimen/fastscroll_popup_width</item>
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 939fece..c76f118 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -16,23 +16,6 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <SwitchPreference
-        android:key="pref_add_icon_to_home"
-        android:title="@string/auto_add_shortcuts_label"
-        android:summary="@string/auto_add_shortcuts_description"
-        android:defaultValue="true"
-        android:persistent="true"
-        />
-
-    <ListPreference
-        android:key="pref_override_icon_shape"
-        android:title="@string/icon_shape_override_label"
-        android:summary="%s"
-        android:entries="@array/icon_shape_override_paths_names"
-        android:entryValues="@array/icon_shape_override_paths_values"
-        android:defaultValue=""
-        android:persistent="false" />
-
     <Preference
         android:key="pref_icon_badging"
         android:title="@string/icon_badging_title"
@@ -46,10 +29,27 @@
     </Preference>
 
     <SwitchPreference
+        android:key="pref_add_icon_to_home"
+        android:title="@string/auto_add_shortcuts_label"
+        android:summary="@string/auto_add_shortcuts_description"
+        android:defaultValue="true"
+        android:persistent="true"
+        />
+
+    <SwitchPreference
         android:key="pref_allowRotation"
         android:title="@string/allow_rotation_title"
         android:defaultValue="@bool/allow_rotation"
         android:persistent="true"
         />
 
+    <ListPreference
+        android:key="pref_override_icon_shape"
+        android:title="@string/icon_shape_override_label"
+        android:summary="%s"
+        android:entries="@array/icon_shape_override_paths_names"
+        android:entryValues="@array/icon_shape_override_paths_values"
+        android:defaultValue=""
+        android:persistent="false" />
+
 </PreferenceScreen>
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 84358ea..3ee6e51 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,8 +22,9 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.RecyclerViewFastScroller;
 
 
 /**
@@ -36,19 +37,7 @@
 public abstract class BaseRecyclerView extends RecyclerView
         implements RecyclerView.OnItemTouchListener {
 
-    private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
-
-    /** Keeps the last known scrolling delta/velocity along y-axis. */
-    @Thunk int mDy = 0;
-    private float mDeltaThreshold;
-
-    protected final BaseRecyclerViewFastScrollBar mScrollbar;
-
-    private int mDownX;
-    private int mDownY;
-    private int mLastY;
-
-    private boolean mScrollBarVisible = true;
+    protected RecyclerViewFastScroller mScrollbar;
 
     public BaseRecyclerView(Context context) {
         this(context, null);
@@ -60,28 +49,6 @@
 
     public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
-        mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
-
-        ScrollListener listener = new ScrollListener();
-        setOnScrollListener(listener);
-    }
-
-    private class ScrollListener extends OnScrollListener {
-        public ScrollListener() {
-            // Do nothing
-        }
-
-        @Override
-        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-            mDy = dy;
-
-            // TODO(winsonc): If we want to animate the section heads while scrolling, we can
-            //                initiate that here if the recycler view scroll state is not
-            //                RecyclerView.SCROLL_STATE_IDLE.
-
-            onUpdateScrollbar(dy);
-        }
     }
 
     @Override
@@ -93,7 +60,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup));
+        ViewGroup parent = (ViewGroup) getParent();
+        mScrollbar = parent.findViewById(R.id.fast_scroller);
+        mScrollbar.setRecyclerView(this, (TextView) parent.findViewById(R.id.fast_scroller_popup));
     }
 
     /**
@@ -115,32 +84,15 @@
      * it is already showing).
      */
     private boolean handleTouchEvent(MotionEvent ev) {
-        ev.offsetLocation(0, -getPaddingTop());
-        int action = ev.getAction();
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                // Keep track of the down positions
-                mDownX = x;
-                mDownY = mLastY = y;
-                if (shouldStopScroll(ev)) {
-                    stopScroll();
-                }
-                mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
-                break;
-            case MotionEvent.ACTION_MOVE:
-                mLastY = y;
-                mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                onFastScrollCompleted();
-                mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
-                break;
+        // Move to mScrollbar's coordinate system.
+        int left = getLeft() - mScrollbar.getLeft();
+        int top = getTop() - mScrollbar.getTop();
+        ev.offsetLocation(left, top);
+        try {
+            return mScrollbar.handleTouchEvent(ev);
+        } finally {
+            ev.offsetLocation(-left, -top);
         }
-        ev.offsetLocation(0, getPaddingTop());
-        return mScrollbar.isDraggingThumb();
     }
 
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -148,24 +100,9 @@
     }
 
     /**
-     * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
-     */
-    protected boolean shouldStopScroll(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if ((Math.abs(mDy) < mDeltaThreshold &&
-                    getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
-                // now the touch events are being passed to the {@link WidgetCell} until the
-                // touch sequence goes over the touch slop.
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Returns the height of the fast scroll bar
      */
-    protected int getScrollbarTrackHeight() {
+    public int getScrollbarTrackHeight() {
         return getHeight() - getPaddingTop() - getPaddingBottom();
     }
 
@@ -187,25 +124,14 @@
     /**
      * Returns the scrollbar for this recycler view.
      */
-    public BaseRecyclerViewFastScrollBar getScrollBar() {
+    public RecyclerViewFastScroller getScrollBar() {
         return mScrollbar;
     }
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        onUpdateScrollbar(0);
         super.dispatchDraw(canvas);
-        if (mScrollBarVisible) {
-            onUpdateScrollbar(0);
-            mScrollbar.draw(canvas);
-        }
-    }
-
-    /**
-     * Sets the scrollbar visibility. The call does not refresh the UI, its the responsibility
-     * of the caller to call {@link #invalidate()}.
-     */
-    public void setScrollBarVisible(boolean visible) {
-        mScrollBarVisible = visible;
     }
 
     /**
@@ -236,7 +162,7 @@
     /**
      * @return whether fast scrolling is supported in the current state.
      */
-    protected boolean supportsFastScrolling() {
+    public boolean supportsFastScrolling() {
         return true;
     }
 
@@ -252,16 +178,16 @@
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      * <p>Override in each subclass of this base class.
      */
-    protected abstract String scrollToPositionAtProgress(float touchFraction);
+    public abstract String scrollToPositionAtProgress(float touchFraction);
 
     /**
      * Updates the bounds for the scrollbar.
      * <p>Override in each subclass of this base class.
      */
-    protected abstract void onUpdateScrollbar(int dy);
+    public abstract void onUpdateScrollbar(int dy);
 
     /**
      * <p>Override in each subclass of this base class.
      */
-    protected void onFastScrollCompleted() {}
+    public void onFastScrollCompleted() {}
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
deleted file mode 100644
index 9e8d300..0000000
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.util.Property;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.TextView;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.FastScrollThumbDrawable;
-import com.android.launcher3.util.Themes;
-
-/**
- * The track and scrollbar that shows when you scroll the list.
- */
-public class BaseRecyclerViewFastScrollBar {
-
-    private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
-            new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
-
-                @Override
-                public Integer get(BaseRecyclerViewFastScrollBar scrollBar) {
-                    return scrollBar.mWidth;
-                }
-
-                @Override
-                public void set(BaseRecyclerViewFastScrollBar scrollBar, Integer value) {
-                    scrollBar.setTrackWidth(value);
-                }
-            };
-
-    private final static int MAX_TRACK_ALPHA = 30;
-    private final static int SCROLL_BAR_VIS_DURATION = 150;
-    private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
-
-    private final Rect mTmpRect = new Rect();
-    private final BaseRecyclerView mRv;
-
-    private final boolean mIsRtl;
-
-    // The inset is the buffer around which a point will still register as a click on the scrollbar
-    private final int mTouchInset;
-
-    private final int mMinWidth;
-    private final int mMaxWidth;
-    private final int mThumbPadding;
-
-    // Current width of the track
-    private int mWidth;
-    private ObjectAnimator mWidthAnimator;
-
-    private final Paint mThumbPaint;
-    private final int mThumbHeight;
-
-    private final Paint mTrackPaint;
-
-    private float mLastTouchY;
-    private boolean mIsDragging;
-    private boolean mIsThumbDetached;
-    private boolean mCanThumbDetach;
-    private boolean mIgnoreDragGesture;
-
-    // This is the offset from the top of the scrollbar when the user first starts touching.  To
-    // prevent jumping, this offset is applied as the user scrolls.
-    private int mTouchOffsetY;
-    private int mThumbOffsetY;
-
-    // Fast scroller popup
-    private TextView mPopupView;
-    private boolean mPopupVisible;
-    private String mPopupSectionName;
-
-    public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) {
-        mRv = rv;
-        mTrackPaint = new Paint();
-        mTrackPaint.setColor(Themes.getAttrColor(rv.getContext(), android.R.attr.textColorPrimary));
-        mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
-
-        mThumbPaint = new Paint();
-        mThumbPaint.setAntiAlias(true);
-        mThumbPaint.setColor(Themes.getColorAccent(rv.getContext()));
-        mThumbPaint.setStyle(Paint.Style.FILL);
-
-        mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width);
-        mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width);
-
-        mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding);
-        mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
-
-        mTouchInset = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_touch_inset);
-        mIsRtl = Utilities.isRtl(res);
-    }
-
-    public void setPopupView(View popup) {
-        mPopupView = (TextView) popup;
-        mPopupView.setBackground(new FastScrollThumbDrawable(mThumbPaint, mIsRtl));
-    }
-
-    public void setDetachThumbOnFastScroll() {
-        mCanThumbDetach = true;
-    }
-
-    public void reattachThumbToScroll() {
-        mIsThumbDetached = false;
-    }
-
-    private int getDrawLeft() {
-        return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth);
-    }
-
-    public void setThumbOffsetY(int y) {
-        if (mThumbOffsetY == y) {
-            return;
-        }
-
-        // Invalidate the previous and new thumb area
-        int drawLeft = getDrawLeft();
-        mTmpRect.set(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
-        mThumbOffsetY = y;
-        mTmpRect.union(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
-        mTmpRect.offset(0, mRv.getPaddingTop());
-        mRv.invalidate(mTmpRect);
-    }
-
-    public int getThumbOffsetY() {
-        return mThumbOffsetY;
-    }
-
-    private void setTrackWidth(int width) {
-        if (mWidth == width) {
-            return;
-        }
-        int left = getDrawLeft();
-        int top = mRv.getPaddingTop();
-        // Invalidate the whole scroll bar area.
-        mRv.invalidate(left, top, left + mMaxWidth, top + mRv.getScrollbarTrackHeight());
-
-        mWidth = width;
-    }
-
-    public int getThumbHeight() {
-        return mThumbHeight;
-    }
-
-    public boolean isDraggingThumb() {
-        return mIsDragging;
-    }
-
-    public boolean isThumbDetached() {
-        return mIsThumbDetached;
-    }
-
-    /**
-     * Handles the touch event and determines whether to show the fast scroller (or updates it if
-     * it is already showing).
-     */
-    public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY) {
-        ViewConfiguration config = ViewConfiguration.get(mRv.getContext());
-
-        int action = ev.getAction();
-        int y = (int) ev.getY();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                if (isNearThumb(downX, downY)) {
-                    mTouchOffsetY = downY - mThumbOffsetY;
-                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
-                        && mRv.supportsFastScrolling()
-                        && isNearScrollBar(downX)) {
-                    calcTouchOffsetAndPrepToFastScroll(downY, lastY);
-                    updateFastScrollSectionNameAndThumbOffset(lastY, y);
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                // Check if we should start scrolling, but ignore this fastscroll gesture if we have
-                // exceeded some fixed movement
-                mIgnoreDragGesture |= Math.abs(y - downY) > config.getScaledPagingTouchSlop();
-                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
-                        isNearThumb(downX, lastY) &&
-                        Math.abs(y - downY) > config.getScaledTouchSlop()) {
-                    calcTouchOffsetAndPrepToFastScroll(downY, lastY);
-                }
-                if (mIsDragging) {
-                    updateFastScrollSectionNameAndThumbOffset(lastY, y);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mTouchOffsetY = 0;
-                mLastTouchY = 0;
-                mIgnoreDragGesture = false;
-                if (mIsDragging) {
-                    mIsDragging = false;
-                    animatePopupVisibility(false);
-                    showActiveScrollbar(false);
-                }
-                break;
-        }
-    }
-
-    private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
-        mRv.getParent().requestDisallowInterceptTouchEvent(true);
-        mIsDragging = true;
-        if (mCanThumbDetach) {
-            mIsThumbDetached = true;
-        }
-        mTouchOffsetY += (lastY - downY);
-        animatePopupVisibility(true);
-        showActiveScrollbar(true);
-    }
-
-    private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
-        // Update the fastscroller section name at this touch position
-        int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
-        float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
-        String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
-        if (!sectionName.equals(mPopupSectionName)) {
-            mPopupSectionName = sectionName;
-            mPopupView.setText(sectionName);
-        }
-        animatePopupVisibility(!sectionName.isEmpty());
-        updatePopupY(lastY);
-        mLastTouchY = boundedY;
-        setThumbOffsetY((int) mLastTouchY);
-    }
-
-    public void draw(Canvas canvas) {
-        if (mThumbOffsetY < 0) {
-            return;
-        }
-        int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        if (!mIsRtl) {
-            canvas.translate(mRv.getWidth() - mWidth, 0);
-        }
-        canvas.translate(0, mRv.getPaddingTop());
-        // Draw the track
-        canvas.drawRoundRect(0, 0, mWidth, mRv.getScrollbarTrackHeight(),
-                mWidth, mWidth, mTrackPaint);
-
-        canvas.translate(-mThumbPadding, mThumbOffsetY);
-        float r = mWidth + mThumbPadding + mThumbPadding;
-        canvas.drawRoundRect(0, 0, r, mThumbHeight, r, r, mThumbPaint);
-        canvas.restoreToCount(saveCount);
-    }
-
-    /**
-     * Animates the width of the scrollbar.
-     */
-    private void showActiveScrollbar(boolean isScrolling) {
-        if (mWidthAnimator != null) {
-            mWidthAnimator.cancel();
-        }
-
-        mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
-                isScrolling ? mMaxWidth : mMinWidth);
-        mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
-        mWidthAnimator.start();
-    }
-
-    /**
-     * Returns whether the specified point is inside the thumb bounds.
-     */
-    public boolean isNearThumb(int x, int y) {
-        int left = getDrawLeft();
-        mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight);
-        mTmpRect.inset(mTouchInset, mTouchInset);
-        return mTmpRect.contains(x, y);
-    }
-
-    /**
-     * Returns whether the specified x position is near the scroll bar.
-     */
-    public boolean isNearScrollBar(int x) {
-        int left = getDrawLeft();
-        return x >= left && x <= left + mMaxWidth;
-    }
-
-    private void animatePopupVisibility(boolean visible) {
-        if (mPopupVisible != visible) {
-            mPopupVisible = visible;
-            mPopupView.animate().cancel();
-            mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
-        }
-    }
-
-    private void updatePopupY(int lastTouchY) {
-        int height = mPopupView.getHeight();
-        float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height);
-        top = Utilities.boundToRange(top,
-                mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
-        mPopupView.setTranslationY(top);
-    }
-}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 66aab43..26c5c9d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -79,7 +79,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.OvershootInterpolator;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
@@ -216,9 +215,9 @@
     private static final int ACTIVITY_START_DELAY = 1000;
 
     // How long to wait before the new-shortcut animation automatically pans the workspace
-    private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
-    private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
-    @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
+    private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+    private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
+    @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
 
     private final ExtractedColors mExtractedColors = new ExtractedColors();
 
@@ -232,7 +231,7 @@
     private AppWidgetManagerCompat mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    private int[] mTmpAddItemCellCoordinates = new int[2];
+    private final int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
     private ViewGroup mOverviewPanel;
@@ -262,15 +261,15 @@
     private boolean mPaused = true;
     private boolean mOnResumeNeedsLoad;
 
-    private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
-    private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
+    private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
+    private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
     private ViewOnDrawExecutor mPendingExecutor;
 
     private LauncherModel mModel;
     private ModelWriter mModelWriter;
     private IconCache mIconCache;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
-    private Handler mHandler = new Handler();
+    private final Handler mHandler = new Handler();
     private boolean mIsResumeFromActionScreenOff;
     private boolean mHasFocus = false;
 
@@ -284,7 +283,7 @@
     // match the sensor state.
     private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
 
-    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
+    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
 
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
@@ -297,8 +296,8 @@
     // the press state and keep this reference to reset the press state when we return to launcher.
     private BubbleTextView mWaitingForResume;
 
-    protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
-            new HashMap<String, CustomAppWidget>();
+    protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets =
+        new HashMap<>();
 
     static {
         if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
@@ -311,7 +310,7 @@
     // simply unregister this runnable.
     private Runnable mExitSpringLoadedModeRunnable;
 
-    @Thunk Runnable mBuildLayersRunnable = new Runnable() {
+    @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
                 mWorkspace.buildPageHardwareLayers();
@@ -1063,13 +1062,13 @@
     public interface CustomContentCallbacks {
         // Custom content is completely shown. {@code fromResume} indicates whether this was caused
         // by a onResume or by scrolling otherwise.
-        public void onShow(boolean fromResume);
+        void onShow(boolean fromResume);
 
         // Custom content is completely hidden
-        public void onHide();
+        void onHide();
 
         // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
-        public void onScrollProgressChanged(float progress);
+        void onScrollProgressChanged(float progress);
 
         // Indicates whether the user is allowed to scroll away from the custom content.
         boolean isScrollingAllowed();
@@ -1080,28 +1079,28 @@
         /**
          * Touch interaction leading to overscroll has begun
          */
-        public void onScrollInteractionBegin();
+        void onScrollInteractionBegin();
 
         /**
          * Touch interaction related to overscroll has ended
          */
-        public void onScrollInteractionEnd();
+        void onScrollInteractionEnd();
 
         /**
          * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
          * screen (or in the case of RTL, the rightmost screen).
          */
-        public void onScrollChange(float progress, boolean rtl);
+        void onScrollChange(float progress, boolean rtl);
 
         /**
          * Called when the launcher is ready to use the overlay
          * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
          */
-        public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
+        void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
     }
 
     public interface LauncherOverlayCallbacks {
-        public void onScrollChanged(float progress);
+        void onScrollChanged(float progress);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1285,7 +1284,7 @@
         mDragController.addDragListener(mWorkspace);
 
         // Get the search/delete/uninstall bar
-        mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
+        mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
 
         // Setup Apps and Widgets
         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
@@ -1607,7 +1606,6 @@
                                 }
                             }
                         });
-                        return;
                     }
                 });
             }
@@ -1744,8 +1742,9 @@
         // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
         // animation.
         if (isActionMain) {
-            boolean callbackAllowsMoveToDefaultScreen = mLauncherCallbacks != null ?
-                    mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
+            boolean callbackAllowsMoveToDefaultScreen =
+                mLauncherCallbacks == null || mLauncherCallbacks
+                    .shouldMoveToDefaultScreenOnHomeIntent();
             if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
                     && callbackAllowsMoveToDefaultScreen) {
 
@@ -2626,9 +2625,9 @@
         if (Utilities.ATLEAST_MARSHMALLOW) {
             int left = 0, top = 0;
             int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-            if (v instanceof TextView) {
+            if (v instanceof BubbleTextView) {
                 // Launch from center of icon, not entire view
-                Drawable icon = Workspace.getTextViewIcon((TextView) v);
+                Drawable icon = ((BubbleTextView) v).getIcon();
                 if (icon != null) {
                     Rect bounds = icon.getBounds();
                     left = (width - bounds.width()) / 2;
@@ -3196,7 +3195,7 @@
                 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
             orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
             orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
-            mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+            LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
         } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
             // If there are no screens, we need to have an empty screen
             mWorkspace.addExtraEmptyScreen();
@@ -3283,7 +3282,7 @@
 
         // Get the list of added items and intersect them with the set of items here
         final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
-        final Collection<Animator> bounceAnims = new ArrayList<Animator>();
+        final Collection<Animator> bounceAnims = new ArrayList<>();
         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
         Workspace workspace = mWorkspace;
         long newItemsScreenId = -1;
@@ -3667,7 +3666,7 @@
      * multiple calls to bind the same list.)
      */
     @Thunk ArrayList<AppInfo> mTmpAppsList;
-    private Runnable mBindAllApplicationsRunnable = new Runnable() {
+    private final Runnable mBindAllApplicationsRunnable = new Runnable() {
         public void run() {
             bindAllApplications(mTmpAppsList);
             mTmpAppsList = null;
@@ -3871,7 +3870,7 @@
         }
     }
 
-    private Runnable mBindAllWidgetsRunnable = new Runnable() {
+    private final Runnable mBindAllWidgetsRunnable = new Runnable() {
             public void run() {
                 bindAllWidgets(mAllWidgets);
             }
@@ -3942,7 +3941,7 @@
     }
 
     private boolean shouldShowDiscoveryBounce() {
-        if (mState != mState.WORKSPACE) {
+        if (mState != State.WORKSPACE) {
             return false;
         }
         if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
@@ -3951,10 +3950,7 @@
         if (!mIsResumeFromActionScreenOff) {
             return false;
         }
-        if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
-            return false;
-        }
-        return true;
+        return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
     }
 
     protected void moveWorkspaceToDefaultScreen() {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0fabeeb..767e332 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -51,7 +51,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
-import android.widget.TextView;
 import android.widget.Toast;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
@@ -2083,19 +2082,6 @@
         }
     }
 
-    /**
-     * Returns the drawable for the given text view.
-     */
-    public static Drawable getTextViewIcon(TextView tv) {
-        final Drawable[] drawables = tv.getCompoundDrawables();
-        for (int i = 0; i < drawables.length; i++) {
-            if (drawables[i] != null) {
-                return drawables[i];
-            }
-        }
-        return null;
-    }
-
     public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
         View child = cellInfo.cell;
 
@@ -3865,7 +3851,7 @@
                         updates.contains(info)) {
                     ShortcutInfo si = (ShortcutInfo) info;
                     BubbleTextView shortcut = (BubbleTextView) v;
-                    Drawable oldIcon = getTextViewIcon(shortcut);
+                    Drawable oldIcon = shortcut.getIcon();
                     boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
                             && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
                     shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 4954e0c..47b68a2 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -171,19 +171,19 @@
      * Returns whether the view itself will handle the touch event or not.
      */
     public boolean shouldContainerScroll(MotionEvent ev) {
-        int[] point = new int[2];
-        point[0] = (int) ev.getX();
-        point[1] = (int) ev.getY();
-        Utilities.mapCoordInSelfToDescendant(mAppsRecyclerView, this, point);
-
         // IF the MotionEvent is inside the search box, and the container keeps on receiving
         // touch input, container should move down.
         if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
             return true;
         }
 
+        int[] point = new int[2];
+        point[0] = (int) ev.getX();
+        point[1] = (int) ev.getY();
+        Utilities.mapCoordInSelfToDescendant(
+                mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point);
         // IF the MotionEvent is inside the thumb, container should not be pulled down.
-        if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
+        if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) {
             return false;
         }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index d6514a8..1054a56 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -69,16 +69,13 @@
 
     // A divider that separates the apps list and the search market button
     public static final int VIEW_TYPE_SEARCH_MARKET_DIVIDER = 1 << 5;
-    // The divider under the search field
-    public static final int VIEW_TYPE_SEARCH_DIVIDER = 1 << 6;
     // The divider that separates prediction icons from the app list
-    public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 7;
-    public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 8;
-    public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 9;
+    public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
+    public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 7;
+    public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 8;
 
     // Common view type masks
-    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_DIVIDER
-            | VIEW_TYPE_SEARCH_MARKET_DIVIDER
+    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_MARKET_DIVIDER
             | VIEW_TYPE_PREDICTION_DIVIDER;
     public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
             | VIEW_TYPE_PREDICTION_ICON;
@@ -319,9 +316,6 @@
                     }
                 });
                 return new ViewHolder(searchMarketView);
-            case VIEW_TYPE_SEARCH_DIVIDER:
-                return new ViewHolder(mLayoutInflater.inflate(
-                        R.layout.all_apps_search_divider, parent, false));
             case VIEW_TYPE_APPS_LOADING_DIVIDER:
                 View loadingDividerView = mLayoutInflater.inflate(
                         R.layout.all_apps_discovery_loading_divider, parent, false);
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 0607a1e..2b2fddc 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -72,7 +72,6 @@
         super(context, attrs, defStyleAttr);
         Resources res = getResources();
         addOnItemTouchListener(this);
-        mScrollbar.setDetachThumbOnFastScroll();
         mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
                 R.dimen.all_apps_empty_search_bg_top_offset);
     }
@@ -110,7 +109,6 @@
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
@@ -137,8 +135,6 @@
                 AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER,
                 AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER);
         putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
-                AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER);
-        putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
                 AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET);
         putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
                 AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH);
@@ -164,7 +160,9 @@
      */
     public void scrollToTop() {
         // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
-        mScrollbar.reattachThumbToScroll();
+        if (mScrollbar != null) {
+            mScrollbar.reattachThumbToScroll();
+        }
         scrollToPosition(0);
     }
 
@@ -356,7 +354,7 @@
     }
 
     @Override
-    protected boolean supportsFastScrolling() {
+    public boolean supportsFastScrolling() {
         // Only allow fast scrolling when the user is not searching, since the results are not
         // grouped in a meaningful order
         return !mApps.hasFilter();
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 7bf6651..608e898 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -138,13 +138,6 @@
             return item;
         }
 
-        public static AdapterItem asSearchDivider(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER;
-            item.position = pos;
-            return item;
-        }
-
         public static AdapterItem asMarketDivider(int pos) {
             AdapterItem item = new AdapterItem();
             item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
@@ -195,8 +188,6 @@
     private int mNumPredictedAppsPerRow;
     private int mNumAppRowsInAdapter;
 
-    private boolean mHasSearchDivider = true;
-
     public AlphabeticalAppsList(Context context) {
         mLauncher = Launcher.getLauncher(context);
         mIndexer = new AlphabeticIndexCompat(context);
@@ -352,10 +343,6 @@
         onAppsUpdated();
     }
 
-    public void disableSearchDivider() {
-        mHasSearchDivider = false;
-    }
-
     /**
      * Updates internals when the set of apps are updated.
      */
@@ -442,11 +429,6 @@
             }
         }
 
-        if (mHasSearchDivider) {
-            // Add the search divider
-            mAdapterItems.add(AdapterItem.asSearchDivider(position++));
-        }
-
         // Process the predicted app components
         mPredictedApps.clear();
         if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
diff --git a/src/com/android/launcher3/allapps/LandscapeFastScroller.java b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
new file mode 100644
index 0000000..cdde657
--- /dev/null
+++ b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.launcher3.views.RecyclerViewFastScroller;
+
+/**
+ * Extension of {@link RecyclerViewFastScroller} to be used in landscape layout.
+ */
+public class LandscapeFastScroller extends RecyclerViewFastScroller {
+
+    public LandscapeFastScroller(Context context) {
+        super(context);
+    }
+
+    public LandscapeFastScroller(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public LandscapeFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public boolean handleTouchEvent(MotionEvent ev) {
+        // We handle our own touch event, no need to handle recycler view touch delegates.
+        return false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        event.offsetLocation(0, -mRv.getPaddingTop());
+        if (super.handleTouchEvent(event)) {
+            getParent().requestDisallowInterceptTouchEvent(true);
+        }
+        event.offsetLocation(0, mRv.getPaddingTop());
+        return true;
+    }
+
+    @Override
+    public boolean shouldBlockIntercept(int x, int y) {
+        // If the user touched the scroll bar area, block swipe
+        return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
+    }
+}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 8e5452e..5cb12d5 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -23,13 +23,11 @@
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.FrameLayout;
-
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Launcher;
@@ -43,7 +41,6 @@
 import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.util.ComponentKey;
-
 import java.util.ArrayList;
 
 /**
@@ -57,12 +54,13 @@
     private final int mSearchBoxHeight;
     private final AllAppsSearchBarController mSearchBarController;
     private final SpannableStringBuilder mSearchQueryBuilder;
-    private final HeaderElevationController mElevationController;
 
     private ExtendedEditText mSearchInput;
     private AlphabeticalAppsList mApps;
     private AllAppsRecyclerView mAppsRecyclerView;
     private AllAppsGridAdapter mAdapter;
+    private View mDivider;
+    private HeaderElevationController mElevationController;
 
     public AppsSearchContainerLayout(Context context) {
         this(context, null);
@@ -80,7 +78,6 @@
         mSearchBoxHeight = getResources()
                 .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
         mSearchBarController = new AllAppsSearchBarController();
-        mElevationController = new HeaderElevationController(this);
 
         mSearchQueryBuilder = new SpannableStringBuilder();
         Selection.setSelection(mSearchQueryBuilder, 0);
@@ -90,6 +87,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mSearchInput = findViewById(R.id.search_box_input);
+        mDivider = findViewById(R.id.search_divider);
+        mElevationController = new HeaderElevationController(mDivider);
 
         // Update the hint to contain the icon.
         // Prefix the original hint with two spaces. The first space gets replaced by the icon
@@ -99,6 +98,12 @@
         spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
                 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
         mSearchInput.setHint(spanned);
+
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        if (!dp.isVerticalBarLayout()) {
+            LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
+            lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
index ab4e88f..7cd32b2 100644
--- a/src/com/android/launcher3/allapps/search/HeaderElevationController.java
+++ b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
@@ -4,11 +4,11 @@
 import android.graphics.Outline;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 
 /**
  * Helper class for controlling the header elevation in response to RecyclerView scroll.
@@ -16,6 +16,7 @@
 public class HeaderElevationController extends RecyclerView.OnScrollListener {
 
     private final View mHeader;
+    private final View mHeaderChild;
     private final float mMaxElevation;
     private final float mScrollToElevation;
 
@@ -28,23 +29,27 @@
         mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
 
         // We need to provide a custom outline so the shadow only appears on the bottom edge.
-        // The top, left and right edges are all extended out, and the shadow is clipped
-        // by the parent.
+        // The top, left and right edges are all extended out to match parent's edge, so that
+        // the shadow is clipped by the parent.
         final ViewOutlineProvider vop = new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
-                final View parent = (View) mHeader.getParent();
+                // Set the left and top to be at the parents edge. Since the coordinates are
+                // relative to this view,
+                //    (x = -view.getLeft()) for this view => (x = 0) for parent
+                final int left = -view.getLeft();
+                final int top = -view.getTop();
 
-                final int left = parent.getLeft(); // Use the parent to account for offsets
-                final int top = view.getTop();
-                final int right = left + view.getWidth();
-                final int bottom = view.getBottom();
-
-                final int offset = Utilities.pxFromDp(mMaxElevation, res.getDisplayMetrics());
+                // Since the view is centered align, the spacing on left and right are same.
+                // Add same spacing on the right to reach parent's edge.
+                final int right = view.getWidth() - left;
+                final int bottom = view.getHeight();
+                final int offset = (int) mMaxElevation;
                 outline.setRect(left - offset, top - offset, right + offset, bottom);
             }
         };
         mHeader.setOutlineProvider(vop);
+        mHeaderChild = ((ViewGroup) mHeader).getChildAt(0);
     }
 
     public void reset() {
@@ -63,6 +68,13 @@
         float newElevation = mMaxElevation * elevationPct;
         if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
             mHeader.setElevation(newElevation);
+
+            // To simulate a scrolling effect for the header, we translate the header down, and
+            // its content up by the same amount, so that it gets clipped by the parent, making it
+            // look like the content was scrolled out of the view.
+            int shift = Math.min(mHeader.getHeight(), scrollY);
+            mHeader.setTranslationY(-shift);
+            mHeaderChild.setTranslationY(shift);
         }
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 50ad0ff..b852714 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -210,13 +210,13 @@
     }
 
     private void callOnDragStart() {
-        for (DragListener listener : new ArrayList<>(mListeners)) {
-            listener.onDragStart(mDragObject, mOptions);
-        }
         if (mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
         mIsInPreDrag = false;
+        for (DragListener listener : new ArrayList<>(mListeners)) {
+            listener.onDragStart(mDragObject, mOptions);
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
index d2dd0d3..de614f0 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.WallpaperColorsCompat;
 
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -97,7 +98,7 @@
         hsl[0] /= 360f;
 
         // Find the palette that contains the closest color
-        TonalPalette palette = findTonalPalette(hsl[0]);
+        TonalPalette palette = findTonalPalette(hsl[0], hsl[1]);
         if (palette == null) {
             Log.w(TAG, "Could not find a tonal palette!");
             return null;
@@ -116,10 +117,22 @@
         float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
         float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
 
-        // Normal colors:
-        // best fit + a 2 colors offset
-        int primaryIndex = fitIndex;
-        int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
+        final int textInversionIndex = h.length - 3;
+
+        int primaryIndex;
+        int secondaryIndex;
+
+        // Dark colors:
+        // Stops at 4th color, only lighter if dark text is supported
+        if (fitIndex < 2) {
+            primaryIndex = 0;
+        } else if (fitIndex < textInversionIndex) {
+            primaryIndex = Math.min(fitIndex, 3);
+        } else {
+            primaryIndex = h.length - 1;
+        }
+        secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
+
         int mainColor = getColorInt(primaryIndex, h, s, l);
         int secondaryColor = getColorInt(secondaryIndex, h, s, l);
 
@@ -196,7 +209,13 @@
     }
 
     @Nullable
-    private static TonalPalette findTonalPalette(float h) {
+    private static TonalPalette findTonalPalette(float h, float s) {
+        // Fallback to a grey palette if the color is too desaturated.
+        // This avoids hue shifts.
+        if (s < 0.05f) {
+            return GREY_PALETTE;
+        }
+
         TonalPalette best = null;
         float error = Float.POSITIVE_INFINITY;
 
@@ -250,6 +269,12 @@
         final float maxHue;
 
         TonalPalette(float[] h, float[] s, float[] l) {
+            if (h.length != s.length || s.length != l.length) {
+                throw new IllegalArgumentException("All arrays should have the same size. h: "
+                        + Arrays.toString(h) + " s: " + Arrays.toString(s) + " l: "
+                        + Arrays.toString(l));
+            }
+
             this.h = h;
             this.s = s;
             this.l = l;
@@ -272,271 +297,280 @@
     // a best fit. Each palette is defined as 22 HSL colors
     private static final TonalPalette[] TONAL_PALETTES = {
             new TonalPalette(
-                    new float[]{0.991f, 0.9833333333333333f, 0f, 0f, 0f, 0.01134380453752181f,
-                            0.015625000000000003f, 0.024193548387096798f, 0.027397260273972573f,
-                            0.017543859649122865f},
-                    new float[]{1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f, 1f},
-                    new float[]{0.2f, 0.27450980392156865f, 0.34901960784313724f,
-                            0.4235294117647059f, 0.5490196078431373f, 0.6254901960784314f,
-                            0.6862745098039216f, 0.7568627450980392f, 0.8568627450980393f,
-                            0.9254901960784314f}
+                    new float[] {1f, 1f, 0.991f, 0.991f, 0.9833333333333333f, 0f, 0f, 0f,
+                            0.01134380453752181f, 0.015625000000000003f, 0.024193548387096798f,
+                            0.027397260273972573f, 0.017543859649122865f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f,
+                            1f},
+                    new float[] {0.04f, 0.09f, 0.14f, 0.2f, 0.27450980392156865f,
+                            0.34901960784313724f, 0.4235294117647059f, 0.5490196078431373f,
+                            0.6254901960784314f, 0.6862745098039216f, 0.7568627450980392f,
+                            0.8568627450980393f, 0.9254901960784314f}
             ),
             new TonalPalette(
-                    new float[]{0.6385767790262171f, 0.6301169590643275f, 0.6223958333333334f,
-                            0.6151079136690647f, 0.6065400843881856f, 0.5986964618249534f,
-                            0.5910746812386157f, 0.5833333333333334f, 0.5748031496062993f,
-                            0.5582010582010583f},
-                    new float[]{1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
-                            0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f,
-                            1f, 1f, 1f},
-                    new float[]{0.17450980392156862f, 0.2235294117647059f, 0.2784313725490196f,
-                            0.3352941176470588f, 0.388235294117647f, 0.44901960784313727f,
-                            0.5392156862745098f, 0.6509803921568628f, 0.7509803921568627f,
-                            0.8764705882352941f}
+                    new float[] {0.638f, 0.638f, 0.6385767790262171f, 0.6301169590643275f,
+                            0.6223958333333334f, 0.6151079136690647f, 0.6065400843881856f,
+                            0.5986964618249534f, 0.5910746812386157f, 0.5833333333333334f,
+                            0.5748031496062993f, 0.5582010582010583f},
+                    new float[] {1f, 1f, 1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
+                            0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f, 1f, 1f,
+                            1f},
+                    new float[] {0.05f, 0.12f, 0.17450980392156862f, 0.2235294117647059f,
+                            0.2784313725490196f, 0.3352941176470588f, 0.388235294117647f,
+                            0.44901960784313727f, 0.5392156862745098f, 0.6509803921568628f,
+                            0.7509803921568627f, 0.8764705882352941f}
             ),
             new TonalPalette(
-                    new float[]{0.5669934640522876f, 0.5748031496062993f,
+                    new float[] {0.563f, 0.569f, 0.5666f, 0.5669934640522876f, 0.5748031496062993f,
                             0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
                             0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
                             0.508080808080808f, 0.5f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f, 1f},
-                    new float[]{0.2f, 0.24901960784313726f, 0.27450980392156865f,
-                            0.30392156862745096f, 0.34901960784313724f, 0.4137254901960784f,
-                            0.47647058823529415f, 0.5352941176470588f, 0.6764705882352942f, 0.8f}
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f,
+                            1f},
+                    new float[] {0.07f, 0.12f, 0.16f, 0.2f, 0.24901960784313726f,
+                            0.27450980392156865f, 0.30392156862745096f, 0.34901960784313724f,
+                            0.4137254901960784f, 0.47647058823529415f, 0.5352941176470588f,
+                            0.6764705882352942f, 0.8f}
             ),
             new TonalPalette(
-                    new float[]{0.5082304526748972f, 0.5069444444444444f, 0.5f, 0.5f,
-                            0.5f, 0.48724954462659376f, 0.4800347222222222f,
-                            0.4755134281200632f, 0.4724409448818897f, 0.4671052631578947f},
-                    new float[]{1f, 0.8888888888888887f, 0.9242424242424242f, 1f, 1f,
-                            0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
-                    new float[]{0.1588235294117647f, 0.21176470588235297f,
-                            0.25882352941176473f, 0.3f, 0.34901960784313724f,
+                    new float[] {0.508f, 0.511f, 0.508f, 0.508f, 0.5082304526748972f,
+                            0.5069444444444444f, 0.5f, 0.5f, 0.5f, 0.48724954462659376f,
+                            0.4800347222222222f, 0.4755134281200632f, 0.4724409448818897f,
+                            0.4671052631578947f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8888888888888887f, 0.9242424242424242f, 1f,
+                            1f, 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
+                    new float[] {0.04f, 0.06f, 0.08f, 0.12f, 0.1588235294117647f,
+                            0.21176470588235297f, 0.25882352941176473f, 0.3f, 0.34901960784313724f,
                             0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
                             0.7509803921568627f, 0.8509803921568627f}
             ),
             new TonalPalette(
-                    new float[]{0.3333333333333333f, 0.3333333333333333f,
+                    new float[] {0.333f, 0.333f, 0.333f, 0.3333333333333333f, 0.3333333333333333f,
                             0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
                             0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
                             0.3467741935483871f, 0.3703703703703704f},
-                    new float[]{0.6703296703296703f, 0.728813559322034f,
+                    new float[] {0.70f, 0.72f, 0.69f, 0.6703296703296703f, 0.728813559322034f,
                             0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
                             0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
-                    new float[]{0.1784313725490196f, 0.23137254901960785f,
+                    new float[] {0.05f, 0.08f, 0.14f, 0.1784313725490196f, 0.23137254901960785f,
                             0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
                             0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
                             0.8784313725490196f, 0.9294117647058824f}
             ),
             new TonalPalette(
-                    new float[]{0.162280701754386f, 0.15032679738562088f,
+                    new float[] {0.161f, 0.163f, 0.163f, 0.162280701754386f, 0.15032679738562088f,
                             0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
-                            0.17824074074074076f, 0.18674698795180725f,
-                            0.18692449355432778f, 0.1946778711484594f, 0.18604651162790695f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[]{0.14901960784313725f, 0.2f, 0.24901960784313726f,
-                            0.30392156862745096f, 0.3784313725490196f, 0.4235294117647059f,
-                            0.48823529411764705f, 0.6450980392156863f, 0.7666666666666666f,
-                            0.8313725490196078f}
+                            0.17824074074074076f, 0.18674698795180725f, 0.18692449355432778f,
+                            0.1946778711484594f, 0.18604651162790695f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.05f, 0.08f, 0.11f, 0.14901960784313725f, 0.2f,
+                            0.24901960784313726f, 0.30392156862745096f, 0.3784313725490196f,
+                            0.4235294117647059f, 0.48823529411764705f, 0.6450980392156863f,
+                            0.7666666666666666f, 0.8313725490196078f}
             ),
             new TonalPalette(
-                    new float[]{0.10619469026548674f, 0.11924686192468618f,
-                            0.13046448087431692f, 0.14248366013071895f, 0.1506024096385542f,
-                            0.16220238095238093f, 0.16666666666666666f,
+                    new float[] {0.108f, 0.105f, 0.105f, 0.105f, 0.10619469026548674f,
+                            0.11924686192468618f, 0.13046448087431692f, 0.14248366013071895f,
+                            0.1506024096385542f, 0.16220238095238093f, 0.16666666666666666f,
                             0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[]{0.44313725490196076f, 0.46862745098039216f,
-                            0.47843137254901963f, 0.5f, 0.5117647058823529f,
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.17f, 0.22f, 0.28f, 0.35f, 0.44313725490196076f,
+                            0.46862745098039216f, 0.47843137254901963f, 0.5f, 0.5117647058823529f,
                             0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
                             0.8509803921568627f, 0.9f}
             ),
             new TonalPalette(
-                    new float[]{0.03561253561253561f, 0.05098039215686275f,
-                            0.07516339869281045f, 0.09477124183006536f, 0.1150326797385621f,
-                            0.134640522875817f, 0.14640522875816991f, 0.1582397003745319f,
-                            0.15773809523809523f, 0.15359477124183002f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[]{0.4588235294117647f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
-                            0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
+                    new float[] {0.036f, 0.036f, 0.036f, 0.036f, 0.03561253561253561f,
+                            0.05098039215686275f, 0.07516339869281045f, 0.09477124183006536f,
+                            0.1150326797385621f, 0.134640522875817f, 0.14640522875816991f,
+                            0.1582397003745319f, 0.15773809523809523f, 0.15359477124183002f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.19f, 0.26f, 0.34f, 0.39f, 0.4588235294117647f, 0.5f, 0.5f, 0.5f,
+                            0.5f, 0.5f, 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
             ),
             new TonalPalette(
-                    new float[]{0.9596491228070175f, 0.9593837535014005f,
+                    new float[] {0.955f, 0.961f, 0.958f, 0.9596491228070175f, 0.9593837535014005f,
                             0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
                             0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
                             0.9754098360655739f, 0.9824561403508771f},
-                    new float[]{0.84070796460177f, 0.8206896551724138f,
+                    new float[] {0.87f, 0.85f, 0.85f, 0.84070796460177f, 0.8206896551724138f,
                             0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
                             1f, 1f, 1f, 1f, 1f},
-                    new float[]{0.22156862745098038f, 0.2843137254901961f,
+                    new float[] {0.06f, 0.11f, 0.16f, 0.22156862745098038f, 0.2843137254901961f,
                             0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
                             0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
                             0.9254901960784314f}
             ),
             new TonalPalette(
-                    new float[]{0.841025641025641f, 0.8333333333333334f,
+                    new float[] {0.866f, 0.855f, 0.841025641025641f, 0.8333333333333334f,
                             0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
                             0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
                             0.7771084337349398f, 0.7747747747747749f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f,
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
                             0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
-                    new float[]{0.12745098039215685f, 0.15490196078431373f,
+                    new float[] {0.05f, 0.08f, 0.12745098039215685f, 0.15490196078431373f,
                             0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
-                            0.36470588235294116f, 0.44901960784313727f,
-                            0.6568627450980392f, 0.7470588235294118f, 0.8450980392156863f}
+                            0.36470588235294116f, 0.44901960784313727f, 0.6568627450980392f,
+                            0.7470588235294118f, 0.8450980392156863f}
             ),
             new TonalPalette(
-                    new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
-                    new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
-                    new float[]{0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
-                            0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
-                            0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
-            ),
-            new TonalPalette(
-                    new float[]{0.955952380952381f, 0.9681069958847737f,
-                            0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
+                    new float[] {0.925f, 0.93f, 0.938f, 0.947f, 0.955952380952381f,
+                            0.9681069958847737f, 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
                             0.009057971014492771f, 0.026748971193415648f,
                             0.041666666666666616f, 0.05303030303030304f},
-                    new float[]{1f, 0.8350515463917526f, 0.6929460580912863f,
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8350515463917526f, 0.6929460580912863f,
                             0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
                             0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
-                    new float[]{0.27450980392156865f, 0.3803921568627451f,
-                            0.4725490196078432f, 0.5549019607843138f, 0.6313725490196078f,
-                            0.707843137254902f, 0.7764705882352941f, 0.8294117647058823f,
-                            0.9058823529411765f, 0.9568627450980391f}
+                    new float[] {0.10f, 0.13f, 0.17f, 0.2f, 0.27450980392156865f,
+                            0.3803921568627451f, 0.4725490196078432f, 0.5549019607843138f,
+                            0.6313725490196078f, 0.707843137254902f, 0.7764705882352941f,
+                            0.8294117647058823f, 0.9058823529411765f, 0.9568627450980391f}
             ),
             new TonalPalette(
-                    new float[]{0.7514619883040936f, 0.7679738562091503f,
+                    new float[] {0.733f, 0.736f, 0.744f, 0.7514619883040936f, 0.7679738562091503f,
                             0.7802083333333333f, 0.7844311377245509f, 0.796875f,
                             0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
                             0.8562091503267975f, 0.8666666666666667f},
-                    new float[]{1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
                             0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
                             0.9560439560439562f, 1f, 1f},
-                    new float[]{0.2235294117647059f, 0.3f, 0.38431372549019605f,
-                            0.492156862745098f, 0.5843137254901961f, 0.6647058823529411f,
-                            0.7333333333333334f, 0.8215686274509804f, 0.9f,
+                    new float[] {0.07f, 0.12f, 0.17f, 0.2235294117647059f, 0.3f,
+                            0.38431372549019605f, 0.492156862745098f, 0.5843137254901961f,
+                            0.6647058823529411f, 0.7333333333333334f, 0.8215686274509804f, 0.9f,
                             0.9411764705882353f}
             ),
             new TonalPalette(
-                    new float[]{0.6666666666666666f, 0.6666666666666666f,
+                    new float[] {0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
                             0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
                             0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
                             0.6666666666666666f, 0.6666666666666666f},
-                    new float[]{0.24590163934426232f, 0.17880794701986752f,
+                    new float[] {0.25f, 0.24590163934426232f, 0.17880794701986752f,
                             0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
-                            0.16756756756756758f, 0.20312500000000017f,
-                            0.26086956521739135f, 0.29999999999999966f, 0.5000000000000004f},
-                    new float[]{0.2392156862745098f, 0.296078431372549f,
+                            0.16756756756756758f, 0.20312500000000017f, 0.26086956521739135f,
+                            0.29999999999999966f, 0.5000000000000004f},
+                    new float[] {0.18f, 0.2392156862745098f, 0.296078431372549f,
                             0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
                             0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
                             0.8823529411764706f, 0.9372549019607843f}
             ),
             new TonalPalette(
-                    new float[]{0.9678571428571429f, 0.9944812362030905f, 0f, 0f,
+                    new float[] {0.938f, 0.944f, 0.952f, 0.961f, 0.9678571428571429f,
+                            0.9944812362030905f, 0f, 0f,
                             0.0047348484848484815f, 0.00316455696202532f, 0f,
                             0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
-                    new float[]{1f, 0.7023255813953488f, 0.6638655462184874f,
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.7023255813953488f, 0.6638655462184874f,
                             0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
                             0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
                             0.8181818181818189f},
-                    new float[]{0.27450980392156865f, 0.4215686274509804f,
+                    new float[] {0.08f, 0.13f, 0.18f, 0.23f, 0.27450980392156865f,
+                            0.4215686274509804f,
                             0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
                             0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
                             0.892156862745098f, 0.9568627450980391f}
             ),
             new TonalPalette(
-                    new float[]{0.9052287581699346f, 0.9112021857923498f, 0.9270152505446624f,
-                            0.9343137254901961f, 0.9391534391534391f, 0.9437984496124031f,
-                            0.943661971830986f, 0.9438943894389439f, 0.9426229508196722f,
-                            0.9444444444444444f},
-                    new float[]{1f, 0.8133333333333332f, 0.7927461139896375f, 0.7798165137614679f,
-                            0.7777777777777779f, 0.8190476190476191f, 0.8255813953488372f,
-                            0.8211382113821142f, 0.8133333333333336f, 0.8000000000000006f},
-                    new float[]{0.2f, 0.29411764705882354f, 0.3784313725490196f,
-                            0.42745098039215684f, 0.4764705882352941f, 0.5882352941176471f,
-                            0.6627450980392157f, 0.7588235294117647f, 0.8529411764705882f,
-                            0.9411764705882353f}
+                    new float[] {0.88f, 0.888f, 0.897f, 0.9052287581699346f, 0.9112021857923498f,
+                            0.9270152505446624f, 0.9343137254901961f, 0.9391534391534391f,
+                            0.9437984496124031f, 0.943661971830986f, 0.9438943894389439f,
+                            0.9426229508196722f, 0.9444444444444444f},
+                    new float[] {1f, 1f, 1f, 1f, 0.8133333333333332f, 0.7927461139896375f,
+                            0.7798165137614679f, 0.7777777777777779f, 0.8190476190476191f,
+                            0.8255813953488372f, 0.8211382113821142f, 0.8133333333333336f,
+                            0.8000000000000006f},
+                    new float[] {0.08f, 0.12f, 0.16f, 0.2f, 0.29411764705882354f,
+                            0.3784313725490196f, 0.42745098039215684f, 0.4764705882352941f,
+                            0.5882352941176471f, 0.6627450980392157f, 0.7588235294117647f,
+                            0.8529411764705882f, 0.9411764705882353f}
             ),
             new TonalPalette(
-                    new float[]{0.6884057971014492f, 0.6974789915966387f, 0.7079889807162534f,
-                            0.7154471544715447f, 0.7217741935483872f, 0.7274143302180687f,
-                            0.7272727272727273f, 0.7258064516129031f, 0.7252252252252251f,
-                            0.7333333333333333f},
-                    new float[]{0.8214285714285715f, 0.6878612716763006f, 0.6080402010050251f,
-                            0.5774647887323943f, 0.5391304347826086f, 0.46724890829694316f,
-                            0.4680851063829788f, 0.462686567164179f, 0.45679012345678977f,
-                            0.4545454545454551f},
-                    new float[]{0.2196078431372549f, 0.33921568627450976f, 0.39019607843137255f,
-                            0.4176470588235294f, 0.45098039215686275f,
+                    new float[] {0.669f, 0.680f, 0.6884057971014492f, 0.6974789915966387f,
+                            0.7079889807162534f, 0.7154471544715447f, 0.7217741935483872f,
+                            0.7274143302180687f, 0.7272727272727273f, 0.7258064516129031f,
+                            0.7252252252252251f, 0.7333333333333333f},
+                    new float[] {0.81f, 0.81f, 0.8214285714285715f, 0.6878612716763006f,
+                            0.6080402010050251f, 0.5774647887323943f, 0.5391304347826086f,
+                            0.46724890829694316f, 0.4680851063829788f, 0.462686567164179f,
+                            0.45679012345678977f, 0.4545454545454551f},
+                    new float[] {0.12f, 0.16f, 0.2196078431372549f, 0.33921568627450976f,
+                            0.39019607843137255f, 0.4176470588235294f, 0.45098039215686275f,
                             0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
                             0.8411764705882353f, 0.9352941176470588f}
             ),
             new TonalPalette(
-                    new float[]{0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
+                    new float[] {0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
                             0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
                             0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
                             0.6428571428571429f},
-                    new float[]{0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
+                    new float[] {0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
                             0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
-                            0.44360902255639095f,
-                            0.4499999999999997f, 0.4375000000000006f},
-                    new float[]{0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
+                            0.44360902255639095f, 0.4499999999999997f, 0.4375000000000006f},
+                    new float[] {0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
                             0.40588235294117647f, 0.44705882352941173f,
                             0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
                             0.8431372549019608f, 0.9372549019607843f}
             ),
             new TonalPalette(
-                    new float[]{0.46732026143790845f, 0.4718614718614719f, 0.4793650793650794f,
-                            0.48071625344352614f, 0.4829683698296837f, 0.484375f,
-                            0.4841269841269842f, 0.48444444444444457f, 0.48518518518518516f,
-                            0.4907407407407408f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
-                            0.41899441340782106f, 0.4128440366972478f,
-                            0.4090909090909088f},
-                    new float[]{0.1f, 0.15098039215686274f, 0.20588235294117646f,
+                    new float[] {0.469f, 0.46732026143790845f, 0.4718614718614719f,
+                            0.4793650793650794f, 0.48071625344352614f, 0.4829683698296837f,
+                            0.484375f, 0.4841269841269842f, 0.48444444444444457f,
+                            0.48518518518518516f, 0.4907407407407408f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
+                            0.41899441340782106f, 0.4128440366972478f, 0.4090909090909088f},
+                    new float[] {0.07f, 0.1f, 0.15098039215686274f, 0.20588235294117646f,
                             0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
                             0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
             ),
             new TonalPalette(
-                    new float[]{0.5444444444444444f, 0.5555555555555556f, 0.5555555555555556f,
-                            0.553763440860215f, 0.5526315789473684f, 0.5555555555555556f,
-                            0.5555555555555555f, 0.5555555555555556f, 0.5512820512820514f,
-                            0.5666666666666667f},
-                    new float[]{0.24590163934426232f, 0.19148936170212766f, 0.1791044776119403f,
-                            0.18343195266272191f, 0.18446601941747576f,
+                    new float[] {0.542f, 0.5444444444444444f, 0.5555555555555556f,
+                            0.5555555555555556f, 0.553763440860215f, 0.5526315789473684f,
+                            0.5555555555555556f, 0.5555555555555555f, 0.5555555555555556f,
+                            0.5512820512820514f, 0.5666666666666667f},
+                    new float[] {0.25f, 0.24590163934426232f, 0.19148936170212766f,
+                            0.1791044776119403f, 0.18343195266272191f, 0.18446601941747576f,
                             0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
                             0.15662650602409653f, 0.151515151515151f},
-                    new float[]{0.1196078431372549f, 0.1843137254901961f, 0.2627450980392157f,
+                    new float[] {0.05f, 0.1196078431372549f, 0.1843137254901961f,
+                            0.2627450980392157f,
                             0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
                             0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
                             0.9352941176470588f}
             ),
             new TonalPalette(
-                    new float[]{0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
+                    new float[] {0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
                             0.03947368421052631f, 0.04166666666666668f,
                             0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
                             0.04444444444444459f, 0.05555555555555529f},
-                    new float[]{0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
+                    new float[] {0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
                             0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
                             0.15315315315315312f, 0.15189873417721522f,
                             0.15789473684210534f, 0.15789473684210542f},
-                    new float[]{0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
+                    new float[] {0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
                             0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
                             0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
                             0.9254901960784314f}
             ),
             new TonalPalette(
-                    new float[]{0.050884955752212385f, 0.07254901960784313f, 0.0934640522875817f,
+                    new float[] {0.027f, 0.03f, 0.038f, 0.044f, 0.050884955752212385f,
+                            0.07254901960784313f, 0.0934640522875817f,
                             0.10457516339869281f, 0.11699346405228758f,
                             0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
                             0.12500000000000003f, 0.12777777777777777f},
-                    new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[]{0.44313725490196076f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5784313725490196f,
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.25f, 0.3f, 0.35f, 0.4f, 0.44313725490196076f, 0.5f, 0.5f, 0.5f,
+                            0.5f, 0.5784313725490196f,
                             0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
                             0.9411764705882353f}
             )
     };
 
+    private static final TonalPalette GREY_PALETTE = new TonalPalette(
+            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+            new float[]{0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
+                    0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
+                    0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
+    );
+
     @SuppressWarnings("WeakerAccess")
     static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
 
@@ -738,5 +772,4 @@
         }
         return colors;
     }
-
 }
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 33bf275..ff357c0 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -29,8 +29,8 @@
     }
 
     @Override
-    public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
-            int curNumItems, FolderIcon.PreviewItemDrawingParams params) {
+    public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params) {
 
         float totalScale = scaleForItem(index, curNumItems);
         float transX;
@@ -47,7 +47,7 @@
         }
 
         if (params == null) {
-            params = new FolderIcon.PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
         } else {
             params.update(transX, transY, totalScale);
             params.overlayAlpha = overlayAlpha;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index aad8123..3c7c698 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -791,6 +791,7 @@
         if (mFolderIcon != null) {
             mFolderIcon.setVisibility(View.VISIBLE);
             if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                mFolderIcon.mFolderName.setTextVisibility(true);
                 mFolderIcon.setBackgroundVisible(true);
                 mFolderIcon.mBackground.fadeInBackgroundShadow();
             }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 7e6205a..3648c60 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -71,8 +71,7 @@
     private final TimeInterpolator mFolderInterpolator;
     private final TimeInterpolator mLargeFolderPreviewItemInterpolator;
 
-    private final FolderIcon.PreviewItemDrawingParams mTmpParams =
-            new FolderIcon.PreviewItemDrawingParams(0, 0, 0, 0);
+    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
 
     private static final Property<View, Float> SCALE_PROPERTY =
             new Property<View, Float>(Float.class, "scale") {
@@ -202,20 +201,20 @@
         play(a, new RoundedRectRevealOutlineProvider(initialRadius, finalRadius, startRect,
                 endRect).createRevealAnimator(mFolder, !mIsOpening));
 
+        // Animate the elevation midway so that the shadow is not noticeable in the background.
+        int midDuration = mDuration / 2;
+        Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
+        play(a, z, midDuration, midDuration);
+
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
                 mFolder.setTranslationX(0.0f);
                 mFolder.setTranslationY(0.0f);
+                mFolder.setTranslationZ(0.0f);
                 mFolder.setScaleX(1f);
                 mFolder.setScaleY(1f);
-
-                if (mIsOpening) {
-                    getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0)
-                            .setDuration(150)
-                            .start();
-                }
             }
         });
 
@@ -324,7 +323,12 @@
     }
 
     private void play(AnimatorSet as, Animator a) {
-        a.setDuration(mDuration);
+        play(as, a, a.getStartDelay(), mDuration);
+    }
+
+    private void play(AnimatorSet as, Animator a, long startDelay, int duration) {
+        a.setStartDelay(startDelay);
+        a.setDuration(duration);
         as.play(a);
     }
 
@@ -344,12 +348,6 @@
                 : ObjectAnimator.ofFloat(view, property, v2, v1);
     }
 
-    private Animator getAnimator(List<BubbleTextView> items, Property property, int v1, int v2) {
-        return mIsOpening
-                ? ObjectAnimator.ofArgb(items, property, v1, v2)
-                : ObjectAnimator.ofArgb(items, property, v2, v1);
-    }
-
     private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
         return mIsOpening
                 ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 215a31c..1cc285e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -19,8 +19,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Point;
@@ -227,8 +225,7 @@
     }
 
     public boolean acceptDrop(ItemInfo dragInfo) {
-        final ItemInfo item = dragInfo;
-        return !mFolder.isDestroyed() && willAcceptItem(item);
+        return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
     }
 
     public void addItem(ShortcutInfo item) {
@@ -423,39 +420,6 @@
         return mBadgeInfo != null && mBadgeInfo.hasBadge();
     }
 
-    static class PreviewItemDrawingParams {
-        PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-            this.overlayAlpha = overlayAlpha;
-        }
-
-        public void update(float transX, float transY, float scale) {
-            // We ensure the update will not interfere with an animation on the layout params
-            // If the final values differ, we cancel the animation.
-            if (anim != null) {
-                if (anim.finalTransX == transX || anim.finalTransY == transY
-                        || anim.finalScale == scale) {
-                    return;
-                }
-                anim.cancel();
-            }
-
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-        }
-
-        float transX;
-        float transY;
-        float scale;
-        public float overlayAlpha;
-        boolean hidden;
-        FolderPreviewItemAnim anim;
-        Drawable drawable;
-    }
-
     private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
         mTmpParams = computePreviewItemDrawingParams(
                 Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams);
@@ -465,12 +429,12 @@
         float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
         float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
 
-        center[0] = (int) Math.round(offsetX);
-        center[1] = (int) Math.round(offsetY);
+        center[0] = Math.round(offsetX);
+        center[1] = Math.round(offsetY);
         return mTmpParams.scale;
     }
 
-    private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+    PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
             PreviewItemDrawingParams params) {
         // We use an index of -1 to represent an icon on the workspace for the destroy and
         // create animations
@@ -582,89 +546,14 @@
         }
     }
 
-    class FolderPreviewItemAnim {
-        ValueAnimator mValueAnimator;
-        float finalScale;
-        float finalTransX;
-        float finalTransY;
-
-        /**
-         *
-         * @param params layout params to animate
-         * @param index0 original index of the item to be animated
-         * @param nItems0 original number of items in the preview
-         * @param index1 new index of the item to be animated
-         * @param nItems1 new number of items in the preview
-         * @param duration duration in ms of the animation
-         * @param onCompleteRunnable runnable to execute upon animation completion
-         */
-        public FolderPreviewItemAnim(final PreviewItemDrawingParams params, int index0, int nItems0,
-                int index1, int nItems1, int duration, final Runnable onCompleteRunnable) {
-
-            computePreviewItemDrawingParams(index1, nItems1, mTmpParams);
-
-            finalScale = mTmpParams.scale;
-            finalTransX = mTmpParams.transX;
-            finalTransY = mTmpParams.transY;
-
-            computePreviewItemDrawingParams(index0, nItems0, mTmpParams);
-
-            final float scale0 = mTmpParams.scale;
-            final float transX0 = mTmpParams.transX;
-            final float transY0 = mTmpParams.transY;
-
-            mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-            mValueAnimator.addUpdateListener(new AnimatorUpdateListener(){
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float progress = animation.getAnimatedFraction();
-
-                    params.transX = transX0 + progress * (finalTransX - transX0);
-                    params.transY = transY0 + progress * (finalTransY - transY0);
-                    params.scale = scale0 + progress * (finalScale - scale0);
-                    invalidate();
-                }
-            });
-
-            mValueAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (onCompleteRunnable != null) {
-                        onCompleteRunnable.run();
-                    }
-                    params.anim = null;
-                }
-            });
-            mValueAnimator.setDuration(duration);
-        }
-
-        public void start() {
-            mValueAnimator.start();
-        }
-
-        public void cancel() {
-            mValueAnimator.cancel();
-        }
-
-        public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
-            return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
-                    finalScale == anim.finalScale;
-
-        }
-    }
-
     private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
             final Runnable onCompleteRunnable) {
-
         FolderPreviewItemAnim anim;
         if (!reverse) {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), -1, -1, 0, 2, duration,
+            anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), -1, -1, 0, 2, duration,
                     onCompleteRunnable);
         } else {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
+            anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), 0, 2, -1, -1, duration,
                     onCompleteRunnable);
         }
         anim.start();
@@ -740,7 +629,7 @@
                     mReferenceDrawable = p.drawable;
                 }
             } else {
-                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
+                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
                         nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
 
                 if (p.anim != null) {
@@ -901,7 +790,7 @@
         }
     }
 
-    public interface PreviewLayoutRule {
+    interface PreviewLayoutRule {
         PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
             PreviewItemDrawingParams params);
         void init(int availableSpace, float intrinsicIconSize, boolean rtl);
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
new file mode 100644
index 0000000..0da7c5c
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.LauncherAnimUtils;
+
+/**
+ * Animates a Folder preview item.
+ */
+class FolderPreviewItemAnim {
+    private ValueAnimator mValueAnimator;
+
+    float finalScale;
+    float finalTransX;
+    float finalTransY;
+
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+    /**
+     * @param folderIcon The FolderIcon this preview will be drawn in.
+     * @param params layout params to animate
+     * @param index0 original index of the item to be animated
+     * @param items0 original number of items in the preview
+     * @param index1 new index of the item to be animated
+     * @param items1 new number of items in the preview
+     * @param duration duration in ms of the animation
+     * @param onCompleteRunnable runnable to execute upon animation completion
+     */
+    FolderPreviewItemAnim(final FolderIcon folderIcon, final PreviewItemDrawingParams params,
+            int index0, int items0, int index1, int items1, int duration,
+            final Runnable onCompleteRunnable) {
+        folderIcon.computePreviewItemDrawingParams(index1, items1, mTmpParams);
+
+        finalScale = mTmpParams.scale;
+        finalTransX = mTmpParams.transX;
+        finalTransY = mTmpParams.transY;
+
+        folderIcon.computePreviewItemDrawingParams(index0, items0, mTmpParams);
+
+        final float scale0 = mTmpParams.scale;
+        final float transX0 = mTmpParams.transX;
+        final float transY0 = mTmpParams.transY;
+
+        mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float progress = animation.getAnimatedFraction();
+
+                params.transX = transX0 + progress * (finalTransX - transX0);
+                params.transY = transY0 + progress * (finalTransY - transY0);
+                params.scale = scale0 + progress * (finalScale - scale0);
+                folderIcon.invalidate();
+            }
+        });
+        mValueAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onCompleteRunnable != null) {
+                    onCompleteRunnable.run();
+                }
+                params.anim = null;
+            }
+        });
+        mValueAnimator.setDuration(duration);
+    }
+
+    public void start() {
+        mValueAnimator.start();
+    }
+
+    public void cancel() {
+        mValueAnimator.cancel();
+    }
+
+    public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
+        return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
+                finalScale == anim.finalScale;
+
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
new file mode 100644
index 0000000..607b7ca
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Manages the parameters used to draw a Folder preview item.
+ */
+class PreviewItemDrawingParams {
+    float transX;
+    float transY;
+    float scale;
+    float overlayAlpha;
+    FolderPreviewItemAnim anim;
+    public boolean hidden;
+    Drawable drawable;
+
+    PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+        this.transX = transX;
+        this.transY = transY;
+        this.scale = scale;
+        this.overlayAlpha = overlayAlpha;
+    }
+
+    public void update(float transX, float transY, float scale) {
+        // We ensure the update will not interfere with an animation on the layout params
+        // If the final values differ, we cancel the animation.
+        if (anim != null) {
+            if (anim.finalTransX == transX || anim.finalTransY == transY
+                    || anim.finalScale == scale) {
+                return;
+            }
+            anim.cancel();
+        }
+
+        this.transX = transX;
+        this.transY = transY;
+        this.scale = scale;
+    }
+}
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 1ece278..138dc1c 100644
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.folder;
 
-import com.android.launcher3.folder.FolderIcon.PreviewItemDrawingParams;
-
 public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
 
     static final int MAX_NUM_ITEMS_IN_PREVIEW = 3;
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 492d853..10e91c0 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -23,12 +23,11 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.view.View;
-import android.widget.TextView;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetHostView;
 import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 
@@ -57,8 +56,8 @@
         blurSizeOutline =
                 context.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
 
-        if (mView instanceof TextView) {
-            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+        if (mView instanceof BubbleTextView) {
+            Drawable d = ((BubbleTextView) mView).getIcon();
             Rect bounds = getDrawableBounds(d);
             previewPadding = blurSizeOutline - bounds.left - bounds.top;
         } else {
@@ -71,8 +70,8 @@
      */
     private void drawDragView(Canvas destCanvas) {
         destCanvas.save();
-        if (mView instanceof TextView) {
-            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+        if (mView instanceof BubbleTextView) {
+            Drawable d = ((BubbleTextView) mView).getIcon();
             Rect bounds = getDrawableBounds(d);
             destCanvas.translate(blurSizeOutline / 2 - bounds.left,
                     blurSizeOutline / 2 - bounds.top);
@@ -112,8 +111,8 @@
         int width = mView.getWidth();
         int height = mView.getHeight();
 
-        if (mView instanceof TextView) {
-            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+        if (mView instanceof BubbleTextView) {
+            Drawable d = ((BubbleTextView) mView).getIcon();
             Rect bounds = getDrawableBounds(d);
             width = bounds.width();
             height = bounds.height();
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index b27ccfd..68012c4 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -20,9 +20,9 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.LongSparseArray;
 import android.util.Pair;
-
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
@@ -39,9 +39,7 @@
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
 import com.android.launcher3.util.Provider;
-
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -68,7 +66,7 @@
 
         final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
         final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<>();
-        HashMap<UserHandle, UserFolderInfo> userFolderMap = new HashMap<>();
+        ArrayMap<UserHandle, UserFolderInfo> userFolderMap = new ArrayMap<>();
 
         // Get the list of workspace screens.  We need to append to this list and
         // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
@@ -146,8 +144,8 @@
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
-                    final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
+                    final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
+                    final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
                     if (!addedItemsFinal.isEmpty()) {
                         ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
                         long lastScreenId = info.screenId;
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
index 9c8457a..c0b5fe1 100644
--- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -19,7 +19,9 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Region;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 
 import com.android.launcher3.BubbleTextView;
@@ -45,18 +47,20 @@
         mShadowInfo = new ShadowInfo(context, attrs, defStyle);
         setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
     }
+
     @Override
     public void onDraw(Canvas canvas) {
         // If text is transparent, don't draw any shadow
-        if ((getCurrentTextColor() >> 24) == 0) {
+        int alpha = Color.alpha(getCurrentTextColor());
+        if (alpha == 0) {
             getPaint().clearShadowLayer();
             super.onDraw(canvas);
             return;
         }
 
         // We enhance the shadow by drawing the shadow twice
-        getPaint().setShadowLayer(
-                mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
+        getPaint().setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0,
+                ColorUtils.setAlphaComponent(mShadowInfo.ambientShadowColor, alpha));
 
         drawWithoutBadge(canvas);
         canvas.save(Canvas.CLIP_SAVE_FLAG);
@@ -64,8 +68,8 @@
                 getScrollX() + getWidth(),
                 getScrollY() + getHeight(), Region.Op.INTERSECT);
 
-        getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f,
-                mShadowInfo.keyShadowOffset, mShadowInfo.keyShadowColor);
+        getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffset,
+                ColorUtils.setAlphaComponent(mShadowInfo.keyShadowColor, alpha));
         drawWithoutBadge(canvas);
         canvas.restore();
 
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
new file mode 100644
index 0000000..7b5bcdb
--- /dev/null
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.views;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.FastScrollThumbDrawable;
+import com.android.launcher3.util.Themes;
+
+/**
+ * The track and scrollbar that shows when you scroll the list.
+ */
+public class RecyclerViewFastScroller extends View {
+
+    private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+
+    private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
+            new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
+
+                @Override
+                public Integer get(RecyclerViewFastScroller scrollBar) {
+                    return scrollBar.mWidth;
+                }
+
+                @Override
+                public void set(RecyclerViewFastScroller scrollBar, Integer value) {
+                    scrollBar.setTrackWidth(value);
+                }
+            };
+
+    private final static int MAX_TRACK_ALPHA = 30;
+    private final static int SCROLL_BAR_VIS_DURATION = 150;
+    private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
+
+    private final int mMinWidth;
+    private final int mMaxWidth;
+    private final int mThumbPadding;
+
+    /** Keeps the last known scrolling delta/velocity along y-axis. */
+    private int mDy = 0;
+    private final float mDeltaThreshold;
+
+    private final ViewConfiguration mConfig;
+
+    // Current width of the track
+    private int mWidth;
+    private ObjectAnimator mWidthAnimator;
+
+    private final Paint mThumbPaint;
+    protected final int mThumbHeight;
+
+    private final Paint mTrackPaint;
+
+    private float mLastTouchY;
+    private boolean mIsDragging;
+    private boolean mIsThumbDetached;
+    private final boolean mCanThumbDetach;
+    private boolean mIgnoreDragGesture;
+
+    // This is the offset from the top of the scrollbar when the user first starts touching.  To
+    // prevent jumping, this offset is applied as the user scrolls.
+    protected int mTouchOffsetY;
+    protected int mThumbOffsetY;
+
+    // Fast scroller popup
+    private TextView mPopupView;
+    private boolean mPopupVisible;
+    private String mPopupSectionName;
+
+    protected BaseRecyclerView mRv;
+
+    private int mDownX;
+    private int mDownY;
+    private int mLastY;
+
+    public RecyclerViewFastScroller(Context context) {
+        this(context, null);
+    }
+
+    public RecyclerViewFastScroller(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mTrackPaint = new Paint();
+        mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+        mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
+
+        mThumbPaint = new Paint();
+        mThumbPaint.setAntiAlias(true);
+        mThumbPaint.setColor(Themes.getColorAccent(context));
+        mThumbPaint.setStyle(Paint.Style.FILL);
+
+        Resources res = getResources();
+        mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width);
+        mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width);
+
+        mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding);
+        mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
+
+        mConfig = ViewConfiguration.get(context);
+        mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
+
+        TypedArray ta =
+                context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0);
+        mCanThumbDetach = ta.getBoolean(R.styleable.RecyclerViewFastScroller_canThumbDetach, false);
+        ta.recycle();
+    }
+
+    public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
+        mRv = rv;
+        mRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+                mDy = dy;
+
+                // TODO(winsonc): If we want to animate the section heads while scrolling, we can
+                //                initiate that here if the recycler view scroll state is not
+                //                RecyclerView.SCROLL_STATE_IDLE.
+
+                mRv.onUpdateScrollbar(dy);
+            }
+        });
+
+        mPopupView = popupView;
+        mPopupView.setBackground(
+                new FastScrollThumbDrawable(mThumbPaint, Utilities.isRtl(getResources())));
+    }
+
+    public void reattachThumbToScroll() {
+        mIsThumbDetached = false;
+    }
+
+    public void setThumbOffsetY(int y) {
+        if (mThumbOffsetY == y) {
+            return;
+        }
+        mThumbOffsetY = y;
+        invalidate();
+    }
+
+    public int getThumbOffsetY() {
+        return mThumbOffsetY;
+    }
+
+    private void setTrackWidth(int width) {
+        if (mWidth == width) {
+            return;
+        }
+        mWidth = width;
+        invalidate();
+    }
+
+    public int getThumbHeight() {
+        return mThumbHeight;
+    }
+
+    public boolean isDraggingThumb() {
+        return mIsDragging;
+    }
+
+    public boolean isThumbDetached() {
+        return mIsThumbDetached;
+    }
+
+    /**
+     * Handles the touch event and determines whether to show the fast scroller (or updates it if
+     * it is already showing).
+     */
+    public boolean handleTouchEvent(MotionEvent ev) {
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                // Keep track of the down positions
+                mDownX = x;
+                mDownY = mLastY = y;
+
+                if ((Math.abs(mDy) < mDeltaThreshold &&
+                        mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
+                    // now the touch events are being passed to the {@link WidgetCell} until the
+                    // touch sequence goes over the touch slop.
+                    mRv.stopScroll();
+                }
+                if (isNearThumb(x, y)) {
+                    mTouchOffsetY = mDownY - mThumbOffsetY;
+                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
+                        && mRv.supportsFastScrolling()
+                        && isNearScrollBar(mDownX)) {
+                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+                    updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mLastY = y;
+
+                // Check if we should start scrolling, but ignore this fastscroll gesture if we have
+                // exceeded some fixed movement
+                mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
+                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
+                        isNearThumb(mDownX, mLastY) &&
+                        Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
+                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+                }
+                if (mIsDragging) {
+                    updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mRv.onFastScrollCompleted();
+                mTouchOffsetY = 0;
+                mLastTouchY = 0;
+                mIgnoreDragGesture = false;
+                if (mIsDragging) {
+                    mIsDragging = false;
+                    animatePopupVisibility(false);
+                    showActiveScrollbar(false);
+                }
+                break;
+        }
+        return mIsDragging;
+    }
+
+    private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
+        mRv.getParent().requestDisallowInterceptTouchEvent(true);
+        mIsDragging = true;
+        if (mCanThumbDetach) {
+            mIsThumbDetached = true;
+        }
+        mTouchOffsetY += (lastY - downY);
+        animatePopupVisibility(true);
+        showActiveScrollbar(true);
+    }
+
+    private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
+        // Update the fastscroller section name at this touch position
+        int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
+        float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
+        String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
+        if (!sectionName.equals(mPopupSectionName)) {
+            mPopupSectionName = sectionName;
+            mPopupView.setText(sectionName);
+        }
+        animatePopupVisibility(!sectionName.isEmpty());
+        updatePopupY(lastY);
+        mLastTouchY = boundedY;
+        setThumbOffsetY((int) mLastTouchY);
+    }
+
+    public void onDraw(Canvas canvas) {
+        if (mThumbOffsetY < 0) {
+            return;
+        }
+        int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.translate(getWidth() / 2, mRv.getPaddingTop());
+        // Draw the track
+        float halfW = mWidth / 2;
+        canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
+                mWidth, mWidth, mTrackPaint);
+
+        canvas.translate(0, mThumbOffsetY);
+        halfW += mThumbPadding;
+        float r = mWidth + mThumbPadding + mThumbPadding;
+        canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
+        canvas.restoreToCount(saveCount);
+    }
+
+
+    /**
+     * Animates the width of the scrollbar.
+     */
+    private void showActiveScrollbar(boolean isScrolling) {
+        if (mWidthAnimator != null) {
+            mWidthAnimator.cancel();
+        }
+
+        mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
+                isScrolling ? mMaxWidth : mMinWidth);
+        mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
+        mWidthAnimator.start();
+    }
+
+    /**
+     * Returns whether the specified point is inside the thumb bounds.
+     */
+    private boolean isNearThumb(int x, int y) {
+        int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
+
+        return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
+    }
+
+    /**
+     * Returns true if AllAppsTransitionController can handle vertical motion
+     * beginning at this point.
+     */
+    public boolean shouldBlockIntercept(int x, int y) {
+        return isNearThumb(x, y);
+    }
+
+    /**
+     * Returns whether the specified x position is near the scroll bar.
+     */
+    public boolean isNearScrollBar(int x) {
+        return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2;
+    }
+
+    private void animatePopupVisibility(boolean visible) {
+        if (mPopupVisible != visible) {
+            mPopupVisible = visible;
+            mPopupView.animate().cancel();
+            mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
+        }
+    }
+
+    private void updatePopupY(int lastTouchY) {
+        int height = mPopupView.getHeight();
+        float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
+                + mRv.getPaddingTop();
+        top = Utilities.boundToRange(top,
+                mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
+        mPopupView.setTranslationY(top);
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index d87e7fb..fff3472 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -77,7 +76,7 @@
     }
 
     public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme), attrs, defStyleAttr);
+        super(context, attrs, defStyleAttr);
         setWillNotDraw(false);
         mLauncher = Launcher.getLauncher(context);
         mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);