Merge "Add dark widgets theme" into ub-launcher3-dorval-polish
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 2e03576..2bbd76d 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -63,8 +63,8 @@
         <com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
             android:id="@+id/page_indicator"
             android:theme="@style/HomeScreenElementTheme"
-            android:layout_width="@dimen/dynamic_grid_page_indicator_height"
-            android:layout_height="@dimen/dynamic_grid_page_indicator_height"
+            android:layout_width="@dimen/dynamic_grid_page_indicator_size"
+            android:layout_height="@dimen/dynamic_grid_page_indicator_size"
             android:layout_gravity="bottom|left"/>
 
         <include layout="@layout/widgets_view"
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index ff605f4..a3c2535 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -53,9 +53,7 @@
             android:clipToPadding="false"
             android:overScrollMode="never"
             android:descendantFocusability="afterDescendants"
-            android:focusable="true"
-            android:paddingStart="@dimen/dynamic_grid_edge_margin"
-            android:paddingEnd="@dimen/dynamic_grid_edge_margin" />
+            android:focusable="true" />
 
         <!-- Fast scroller popup -->
         <TextView
diff --git a/res/layout/app_icon.xml b/res/layout/app_icon.xml
index fa6eb89..52df694 100644
--- a/res/layout/app_icon.xml
+++ b/res/layout/app_icon.xml
@@ -14,4 +14,4 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.BubbleTextView style="@style/BaseIcon.Workspace" />
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" />
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index ccc6b01..4093744 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -20,7 +20,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:focusable="true" >
-    <com.android.launcher3.BubbleTextView
+    <com.android.launcher3.views.DoubleShadowBubbleTextView
         style="@style/BaseIcon.Workspace"
         android:id="@+id/folder_icon_name"
         android:focusable="false"
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
index c5ef6e0..14ff2bd 100644
--- a/res/layout/page_indicator.xml
+++ b/res/layout/page_indicator.xml
@@ -18,7 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:theme="@style/HomeScreenElementTheme"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/dynamic_grid_page_indicator_height">
+    android:layout_height="@dimen/dynamic_grid_page_indicator_size">
         <ImageView
             android:id="@+id/all_apps_handle"
             android:layout_width="48dp"
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index cde6540..4d80aac 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -24,6 +24,7 @@
 
     <com.android.launcher3.folder.FolderPagedView
         android:id="@+id/folder_content"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:paddingLeft="8dp"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 2063f32..e91f966 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -24,6 +24,7 @@
 
     <com.android.launcher3.folder.FolderPagedView
         android:id="@+id/folder_content"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:paddingLeft="8dp"
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 1062269..4cd03ce 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -42,7 +42,6 @@
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="16sp"
         android:textAlignment="viewStart"
-        launcher:customShadows="false"
         launcher:deferShadowGeneration="true"
         launcher:iconDisplay="widget_section"
         launcher:iconSizeOverride="@dimen/widget_section_icon_size"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index b40aa14..08073ce 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -24,4 +24,15 @@
     <dimen name="fastscroll_popup_padding">10dp</dimen>
     <dimen name="fastscroll_popup_text_size">24dp</dimen>
 
+    <!-- Dynamic grid -->
+    <dimen name="dynamic_grid_overview_bar_item_width">120dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_size">24dp</dimen>
+    <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
+    <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
+
+    <!-- Hotseat -->
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">6dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">24dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">32dp</dimen>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index fc95002..3839eb1 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -44,10 +44,15 @@
             <enum name="shortcut_popup" value="4" />
         </attr>
         <attr name="deferShadowGeneration" format="boolean" />
-        <attr name="customShadows" format="boolean" />
         <attr name="centerVertically" format="boolean" />
+    </declare-styleable>
+
+    <declare-styleable name="ShadowInfo">
         <attr name="ambientShadowColor" format="color" />
+        <attr name="ambientShadowBlur" format="dimension" />
         <attr name="keyShadowColor" format="color" />
+        <attr name="keyShadowBlur" format="dimension" />
+        <attr name="keyShadowOffset" format="dimension" />
     </declare-styleable>
 
     <!-- PagedView specific attributes. These attributes are used to customize
@@ -112,9 +117,9 @@
         <attr name="numHotseatIcons" format="integer" />
 
         <attr name="iconSize" format="float" />
+        <!-- landscapeIconSize defaults to iconSize, if not specified -->
+        <attr name="landscapeIconSize" format="float" />
         <attr name="iconTextSize" format="float" />
-        <!-- hotseatIconSize defaults to iconSize, if not specified -->
-        <attr name="hotseatIconSize" format="float" />
 
         <attr name="defaultLayoutId" format="reference" />
     </declare-styleable>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b90de82..71f9edc 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -17,10 +17,9 @@
 <resources>
 <!-- Dynamic Grid -->
     <dimen name="dynamic_grid_edge_margin">16dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_height">32dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_size">32dp</dimen>
     <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_gutter_width_left_nav_bar">38dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_gutter_width_right_nav_bar">48dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_gutter_width">50dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
     <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
     <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
@@ -38,7 +37,10 @@
     <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
     <dimen name="dynamic_grid_hotseat_height">80dp</dimen>
-    <dimen name="dynamic_grid_hotseat_land_gutter_width">24dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">0dp</dimen>
 
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
@@ -74,7 +76,7 @@
     <dimen name="all_apps_caret_stroke_width">2dp</dimen>
     <dimen name="all_apps_caret_shadow_spread">1dp</dimen>
     <dimen name="all_apps_caret_size">13dp</dimen>
-    <dimen name="all_apps_caret_workspace_offset">4dp</dimen>
+    <dimen name="all_apps_caret_workspace_offset">18dp</dimen>
 
 <!-- Search bar in All Apps -->
     <dimen name="all_apps_header_max_elevation">3dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index bc12bb6..d11b002 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -123,16 +123,17 @@
 
         <!-- No shadows in the base theme -->
         <item name="android:shadowRadius">0</item>
-        <item name="customShadows">false</item>
     </style>
 
     <!-- Icon displayed on the worksapce -->
     <style name="BaseIcon.Workspace">
-        <item name="customShadows">true</item>
         <item name="android:shadowRadius">2.0</item>
         <item name="android:shadowColor">?attr/workspaceShadowColor</item>
-        <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
         <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
+        <item name="ambientShadowBlur">2.5dp</item>
+        <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
+        <item name="keyShadowBlur">1dp</item>
+        <item name="keyShadowOffset">.5dp</item>
     </style>
 
     <!-- Theme for the popup container -->
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index aeda1a2..c582fc5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -29,7 +29,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="3"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_3x3"
         />
 
@@ -45,7 +44,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="3"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_3x3"
         />
 
@@ -61,7 +59,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -77,7 +74,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -93,7 +89,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -106,10 +101,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="60"
+        launcher:iconSize="54"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="56"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -122,10 +116,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="60"
+        launcher:iconSize="54"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="56"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -138,10 +131,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="64"
+        launcher:iconSize="56"
         launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="56"
         launcher:defaultLayoutId="@xml/default_workspace_5x5"
         />
 
@@ -154,10 +146,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="5"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="72"
+        launcher:iconSize="64"
         launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="7"
-        launcher:hotseatIconSize="60"
         launcher:defaultLayoutId="@xml/default_workspace_5x6"
         />
 
@@ -173,7 +164,6 @@
         launcher:iconSize="76"
         launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="7"
-        launcher:hotseatIconSize="76"
         launcher:defaultLayoutId="@xml/default_workspace_5x6"
         />
 
@@ -189,7 +179,6 @@
         launcher:iconSize="100"
         launcher:iconTextSize="20.0"
         launcher:numHotseatIcons="7"
-        launcher:hotseatIconSize="72"
         launcher:defaultLayoutId="@xml/default_workspace_5x6"
         />
 
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/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index ac7cbaf..c55a586 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -113,20 +113,18 @@
      * Calculate the background padding as it can change due to insets/content padding change.
      */
     private void updatePaddings() {
-        Context context = getContext();
-        int paddingLeft;
-        int paddingRight;
-        int paddingTop;
-        int paddingBottom;
-
-        DeviceProfile grid = Launcher.getLauncher(context).getDeviceProfile();
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
         int[] padding = grid.getContainerPadding();
-        paddingLeft = padding[0] + grid.edgeMarginPx;
-        paddingRight = padding[1] + grid.edgeMarginPx;
+
+        int paddingLeft = padding[0];
+        int paddingRight = padding[1];
+        int paddingTop = 0;
+        int paddingBottom = 0;
+
         if (!grid.isVerticalBarLayout()) {
+            paddingLeft += grid.edgeMarginPx;
+            paddingRight += grid.edgeMarginPx;
             paddingTop = paddingBottom = grid.edgeMarginPx;
-        } else {
-            paddingTop = paddingBottom = 0;
         }
         updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
     }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index cb3cbd2..6f2c897 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -27,10 +26,8 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
 import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -48,7 +45,6 @@
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.folder.FolderIconPreviewVerifier;
-import com.android.launcher3.folder.FolderPagedView;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.HolographicOutlineHelper;
 import com.android.launcher3.graphics.IconPalette;
@@ -64,11 +60,6 @@
  */
 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
 
-    // Dimensions in DP
-    private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
-    private static final float KEY_SHADOW_RADIUS = 1f;
-    private static final float KEY_SHADOW_OFFSET = 0.5f;
-
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
@@ -78,22 +69,15 @@
     private final Launcher mLauncher;
     private Drawable mIcon;
     private final boolean mCenterVertically;
-    private final Drawable mBackground;
-    private OnLongClickListener mOnLongClickListener;
+
     private final CheckLongPressHelper mLongPressHelper;
     private final HolographicOutlineHelper mOutlineHelper;
     private final StylusEventHelper mStylusEventHelper;
-    private final int mAmbientShadowColor;
-    private final int mKeyShadowColor;
-
-    private boolean mBackgroundSizeChanged;
+    private final float mSlop;
 
     private Bitmap mPressedBackground;
 
-    private float mSlop;
-
     private final boolean mDeferShadowGenerationOnTouch;
-    private final boolean mCustomShadowsEnabled;
     private final boolean mLayoutHorizontal;
     private final int mIconSize;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -122,7 +106,7 @@
         }
     };
 
-    private static final Property<BubbleTextView, Integer> TEXT_ALPHA_PROPERTY
+    public static final Property<BubbleTextView, Integer> TEXT_ALPHA_PROPERTY
             = new Property<BubbleTextView, Integer>(Integer.class, "textAlpha") {
         @Override
         public Integer get(BubbleTextView bubbleTextView) {
@@ -156,15 +140,13 @@
         super(context, attrs, defStyle);
         mLauncher = Launcher.getLauncher(context);
         DeviceProfile grid = mLauncher.getDeviceProfile();
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
-        mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, false);
         mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
         mDeferShadowGenerationOnTouch =
                 a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
-        mAmbientShadowColor = a.getColor(R.styleable.BubbleTextView_ambientShadowColor, 0x33000000);
-        mKeyShadowColor = a.getColor(R.styleable.BubbleTextView_keyShadowColor, 0x66000000);
 
         int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
         int defaultIconSize = grid.iconSizePx;
@@ -186,40 +168,18 @@
                 defaultIconSize);
         a.recycle();
 
-        if (mCustomShadowsEnabled) {
-            // Draw the background itself as the parent is drawn twice.
-            mBackground = getBackground();
-            setBackground(null);
-
-            // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
-            float density = getResources().getDisplayMetrics().density;
-            setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, mAmbientShadowColor);
-        } else {
-            mBackground = null;
-        }
-
         mLongPressHelper = new CheckLongPressHelper(this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
         setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info) {
         applyFromShortcutInfo(info, false);
     }
 
-    @Override
-    public void invalidateDrawable(@NonNull Drawable drawable) {
-        super.invalidateDrawable(drawable);
-
-        if (getParent() != null
-                && getParent().getParent() != null
-                && getParent().getParent().getParent() instanceof FolderPagedView) {
-            ((FolderPagedView) getParent().getParent().getParent()).onIconInvalidated(this);
-        }
-    }
-
     public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info.iconBitmap, info);
         setTag(info);
@@ -275,19 +235,6 @@
     }
 
     @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
-            mBackgroundSizeChanged = true;
-        }
-        return super.setFrame(left, top, right, bottom);
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mBackground || super.verifyDrawable(who);
-    }
-
-    @Override
     public void setTag(Object tag) {
         if (tag != null) {
             LauncherModel.checkItemInfo((ItemInfo) tag);
@@ -316,21 +263,6 @@
         return mIcon;
     }
 
-    /** Returns whether the layout is horizontal. */
-    public boolean isLayoutHorizontal() {
-        return mLayoutHorizontal;
-    }
-
-    @Override
-    public void setOnLongClickListener(OnLongClickListener l) {
-        super.setOnLongClickListener(l);
-        mOnLongClickListener = l;
-    }
-
-    public OnLongClickListener getOnLongClickListener() {
-        return mOnLongClickListener;
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // Call the superclass onTouchEvent first, because sometimes it changes the state to
@@ -428,54 +360,14 @@
         return result;
     }
 
+    @SuppressWarnings("wrongcall")
+    protected void drawWithoutBadge(Canvas canvas) {
+        super.onDraw(canvas);
+    }
+
     @Override
-    public void draw(Canvas canvas) {
-        if (!mCustomShadowsEnabled) {
-            super.draw(canvas);
-            drawBadgeIfNecessary(canvas);
-            return;
-        }
-
-        final Drawable background = mBackground;
-        if (background != null) {
-            final int scrollX = getScrollX();
-            final int scrollY = getScrollY();
-
-            if (mBackgroundSizeChanged) {
-                background.setBounds(0, 0,  getRight() - getLeft(), getBottom() - getTop());
-                mBackgroundSizeChanged = false;
-            }
-
-            if ((scrollX | scrollY) == 0) {
-                background.draw(canvas);
-            } else {
-                canvas.translate(scrollX, scrollY);
-                background.draw(canvas);
-                canvas.translate(-scrollX, -scrollY);
-            }
-        }
-
-        // If text is transparent, don't draw any shadow
-        if ((getCurrentTextColor() >> 24) == 0) {
-            getPaint().clearShadowLayer();
-            super.draw(canvas);
-            drawBadgeIfNecessary(canvas);
-            return;
-        }
-
-        // We enhance the shadow by drawing the shadow twice
-        float density = getResources().getDisplayMetrics().density;
-        getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, mAmbientShadowColor);
-        super.draw(canvas);
-        canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
-                getScrollX() + getWidth(),
-                getScrollY() + getHeight(), Region.Op.INTERSECT);
-        getPaint().setShadowLayer(
-                density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, mKeyShadowColor);
-        super.draw(canvas);
-        canvas.restore();
-
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
         drawBadgeIfNecessary(canvas);
     }
 
@@ -483,7 +375,7 @@
      * Draws the icon badge in the top right corner of the icon bounds.
      * @param canvas The canvas to draw to.
      */
-    private void drawBadgeIfNecessary(Canvas canvas) {
+    protected void drawBadgeIfNecessary(Canvas canvas) {
         if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
             getIconBounds(mTempIconBounds);
             mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
@@ -522,14 +414,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (mBackground != null) mBackground.setCallback(this);
-        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mCenterVertically) {
             Paint.FontMetrics fm = getPaint().getFontMetrics();
@@ -543,12 +427,6 @@
     }
 
     @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mBackground != null) mBackground.setCallback(null);
-    }
-
-    @Override
     public void setTextColor(int color) {
         mTextColor = color;
         super.setTextColor(color);
@@ -580,7 +458,7 @@
      * Creates an animator to fade the text in or out.
      * @param fadeIn Whether the text should fade in or fade out.
      */
-    public Animator createTextAlphaAnimator(boolean fadeIn) {
+    public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
         return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, fadeIn ? Color.alpha(mTextColor) : 0);
     }
 
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index d0d33a0..d99a30a 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -53,6 +53,7 @@
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
@@ -102,8 +103,8 @@
     private OnTouchListener mInterceptTouchListener;
     private final StylusEventHelper mStylusEventHelper;
 
-    private final ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<>();
-    final FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
+    private final ArrayList<PreviewBackground> mFolderBackgrounds = new ArrayList<>();
+    final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
 
     private float mBackgroundAlpha;
 
@@ -153,7 +154,7 @@
 
     @ContainerType private final int mContainerType;
 
-    private final float mChildScale;
+    private final float mChildScale = 1f;
 
     public static final int MODE_SHOW_REORDER_HINT = 0;
     public static final int MODE_DRAG_OVER = 1;
@@ -217,8 +218,6 @@
         mFolderLeaveBehind.delegateCellX = -1;
         mFolderLeaveBehind.delegateCellY = -1;
 
-        mChildScale = mContainerType == HOTSEAT ? grid.inv.hotseatScale : 1f;
-
         setAlwaysDrawnWithCacheEnabled(false);
         final Resources res = getResources();
 
@@ -495,7 +494,7 @@
         }
 
         for (int i = 0; i < mFolderBackgrounds.size(); i++) {
-            FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+            PreviewBackground bg = mFolderBackgrounds.get(i);
             cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
             canvas.save();
             canvas.translate(mTempLocation[0], mTempLocation[1]);
@@ -521,7 +520,7 @@
         super.dispatchDraw(canvas);
 
         for (int i = 0; i < mFolderBackgrounds.size(); i++) {
-            FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+            PreviewBackground bg = mFolderBackgrounds.get(i);
             if (bg.isClipping) {
                 cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
                 canvas.save();
@@ -532,10 +531,10 @@
         }
     }
 
-    public void addFolderBackground(FolderIcon.PreviewBackground bg) {
+    public void addFolderBackground(PreviewBackground bg) {
         mFolderBackgrounds.add(bg);
     }
-    public void removeFolderBackground(FolderIcon.PreviewBackground bg) {
+    public void removeFolderBackground(PreviewBackground bg) {
         mFolderBackgrounds.remove(bg);
     }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 0a346a6..dcfb268 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -32,7 +33,6 @@
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.config.FeatureFlags;
 
 import java.util.ArrayList;
 
@@ -80,9 +80,8 @@
     public final int workspaceSpringLoadedBottomSpace;
 
     // Page indicator
-    private final int pageIndicatorHeightPx;
-    private final int pageIndicatorLandGutterLeftNavBarPx;
-    private final int pageIndicatorLandGutterRightNavBarPx;
+    private final int pageIndicatorSizePx;
+    private final int pageIndicatorLandGutterPx;
     private final int pageIndicatorLandWorkspaceOffsetPx;
 
     // Workspace icons
@@ -93,7 +92,7 @@
 
     public int cellWidthPx;
     public int cellHeightPx;
-    public int cellPaddingXPx;
+    public int workspaceCellPaddingXPx;
 
     // Folder
     public int folderBackgroundOffset;
@@ -110,15 +109,17 @@
     public int folderChildDrawablePaddingPx;
 
     // Hotseat
-    public int hotseatCellWidthPx;
     public int hotseatCellHeightPx;
-    public int hotseatIconSizePx;
     public int hotseatBarHeightPx;
     public int hotseatBarTopPaddingPx;
+    public int hotseatBarLeftNavBarRightPaddingPx;
+    public int hotseatBarRightNavBarRightPaddingPx;
     public int hotseatBarBottomPaddingPx;
-    public int hotseatLandGutterPx;
+    public int hotseatLandLeftNavBarGutterPx;
+    public int hotseatLandRightNavBarGutterPx;
 
     // All apps
+    public int allAppsCellHeightPx;
     public int allAppsNumCols;
     public int allAppsNumPredictiveCols;
     public int allAppsButtonVisualSize;
@@ -160,19 +161,22 @@
         transposeLayoutWithOrientation =
                 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
 
+        context = getContext(context, isVerticalBarLayout()
+                ? Configuration.ORIENTATION_LANDSCAPE
+                : Configuration.ORIENTATION_PORTRAIT);
+        res = context.getResources();
+
+
         ComponentName cn = new ComponentName(context.getPackageName(),
                 this.getClass().getName());
         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
         desiredWorkspaceLeftRightMarginPx = edgeMarginPx;
-        pageIndicatorHeightPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
-        pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar);
+        pageIndicatorSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_size);
+        pageIndicatorLandGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_gutter_width);
         pageIndicatorLandWorkspaceOffsetPx =
                 res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
-        pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar);
         defaultPageSpacingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
         topWorkspacePadding =
@@ -182,8 +186,7 @@
         overviewModeMaxIconZoneHeightPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height);
         overviewModeBarItemWidthPx =
-                (int) (res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width)
-                        * (isLandscape ? 1.5f : 1f));
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width);
         overviewModeBarSpacerWidthPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width);
         overviewModeIconZoneRatio =
@@ -194,15 +197,25 @@
         workspaceSpringLoadedBottomSpace =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
 
-        cellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
+        workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
-        hotseatBarHeightPx = hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx +
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
-        hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_land_gutter_width);
+        hotseatBarLeftNavBarRightPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_left_nav_bar_right_padding);
+        hotseatBarRightNavBarRightPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_right_nav_bar_right_padding);
+        hotseatBarHeightPx = isVerticalBarLayout()
+                ? res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height)
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height)
+                        + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
+
+        hotseatLandLeftNavBarGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_left_nav_bar_gutter_width);
+        hotseatLandRightNavBarGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_right_nav_bar_gutter_width);
 
         // Determine sizes.
         widthPx = width;
@@ -263,7 +276,7 @@
     private void computeAllAppsButtonSize(Context context) {
         Resources res = context.getResources();
         float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
-        allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
+        allAppsButtonVisualSize = (int) (iconSizePx * (1 - padding)) - context.getResources()
                         .getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
     }
 
@@ -281,25 +294,33 @@
     }
 
     private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
-        iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+        float invIconSizePx = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+        iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
-        hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
+
+        // All apps
+        allAppsIconTextSizePx = iconTextSizePx;
         allAppsIconSizePx = iconSizePx;
         allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
-        allAppsIconTextSizePx = iconTextSizePx;
+        allAppsCellHeightPx = getCellSize().y;
+
+        if (isVerticalBarLayout()) {
+            // Always hide the Workspace text with vertical bar layout.
+            iconTextSizePx = 0;
+            allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
+        }
 
         cellWidthPx = iconSizePx + iconDrawablePaddingPx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx
                 + Utilities.calculateTextHeight(iconTextSizePx);
 
         // Hotseat
-        hotseatCellWidthPx = cellWidthPx;
         hotseatCellHeightPx = iconSizePx + iconDrawablePaddingPx;
 
         if (!isVerticalBarLayout()) {
             int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
-                    - pageIndicatorHeightPx - topWorkspacePadding;
+                    - pageIndicatorSizePx - topWorkspacePadding;
             float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
             workspaceSpringLoadShrinkFactor = Math.min(
                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -403,22 +424,25 @@
 
     /**
      * Returns the workspace padding in the specified orientation.
-     * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such
-     * this value is not reliable.
-     * Use {@link #getTotalWorkspacePadding()} instead.
      */
     public Rect getWorkspacePadding(Rect recycle) {
         Rect padding = recycle == null ? new Rect() : recycle;
         if (isVerticalBarLayout()) {
             if (mInsets.left > 0) {
-                padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0,
-                        hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx);
+                padding.set(mInsets.left + pageIndicatorLandGutterPx,
+                        0,
+                        hotseatBarHeightPx + hotseatLandLeftNavBarGutterPx
+                                + hotseatBarLeftNavBarRightPaddingPx - mInsets.left,
+                        edgeMarginPx);
             } else {
-                padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
-                        hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
+                padding.set(pageIndicatorLandGutterPx,
+                        0,
+                        hotseatBarHeightPx + hotseatLandRightNavBarGutterPx
+                                + hotseatBarRightNavBarRightPaddingPx,
+                        edgeMarginPx);
             }
         } else {
-            int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
+            int paddingBottom = hotseatBarHeightPx + pageIndicatorSizePx;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
@@ -460,7 +484,7 @@
             return new Rect(mInsets.left,
                     mInsets.top + dropTargetBarSizePx + edgeMarginPx,
                     mInsets.left + availableWidthPx,
-                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
+                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorSizePx -
                             edgeMarginPx);
         }
     }
@@ -550,7 +574,12 @@
             lp.gravity = Gravity.RIGHT;
             lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
             lp.height = LayoutParams.MATCH_PARENT;
-            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
+
+            int paddingRight = mInsets.left > 0
+                    ? hotseatBarLeftNavBarRightPaddingPx
+                    : hotseatBarRightNavBarRightPaddingPx;
+
+            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right + paddingRight,
                     workspacePadding.bottom);
         } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
@@ -578,17 +607,15 @@
             lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
             if (isVerticalBarLayout()) {
                 if (mInsets.left > 0) {
-                    lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx -
-                            lp.width - pageIndicatorLandWorkspaceOffsetPx;
-                } else if (mInsets.right > 0) {
-                    lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width -
-                            pageIndicatorLandWorkspaceOffsetPx;
+                    lp.leftMargin = mInsets.left;
+                } else {
+                    lp.leftMargin = pageIndicatorLandWorkspaceOffsetPx;
                 }
                 lp.bottomMargin = workspacePadding.bottom;
             } else {
                 // Put the page indicators above the hotseat
                 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-                lp.height = pageIndicatorHeightPx;
+                lp.height = pageIndicatorSizePx;
                 lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
             }
             pageIndicator.setLayoutParams(lp);
@@ -607,6 +634,12 @@
             overviewMode.setLayoutParams(lp);
         }
 
+        // Layout the AllAppsRecyclerView
+        View view = launcher.findViewById(R.id.apps_list_view);
+        int paddingLeftRight = hasVerticalBarLayout ? 0 : edgeMarginPx;
+        view.setPadding(paddingLeftRight, view.getPaddingTop(), paddingLeftRight,
+                view.getPaddingBottom());
+
         if (notifyListeners) {
             for (int i = mListeners.size() - 1; i >= 0; i--) {
                 mListeners.get(i).onLauncherLayoutChanged();
@@ -650,9 +683,8 @@
         }
 
         // In landscape, we match the width of the workspace
-        int padding = (pageIndicatorLandGutterRightNavBarPx +
-                hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
-        return new int[]{ padding, padding };
+        Rect padding = getWorkspacePadding(null);
+        return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
     }
 
     public boolean shouldIgnoreLongPressToOverview(float touchX) {
@@ -661,4 +693,11 @@
         boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
         return !inMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
     }
+
+    private static Context getContext(Context c, int orientation) {
+        Configuration context = new Configuration(c.getResources().getConfiguration());
+        context.orientation = orientation;
+        return c.createConfigurationContext(context);
+
+    }
 }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 199baaf..1272e0a 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -36,8 +36,6 @@
 
 public class FastBitmapDrawable extends Drawable {
 
-    private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
-
     private static final float PRESSED_BRIGHTNESS = 100f / 255f;
     private static final float DISABLED_DESATURATION = 1f;
     private static final float DISABLED_BRIGHTNESS = 0.5f;
@@ -107,17 +105,6 @@
 
     @Override
     public void draw(Canvas canvas) {
-        drawInternal(canvas);
-    }
-
-    public void drawWithBrightness(Canvas canvas, float brightness) {
-        float oldBrightness = getBrightness();
-        setBrightness(brightness);
-        drawInternal(canvas);
-        setBrightness(oldBrightness);
-    }
-
-    protected void drawInternal(Canvas canvas) {
         canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
     }
 
@@ -185,6 +172,11 @@
     }
 
     @Override
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    @Override
     protected boolean onStateChange(int[] state) {
         boolean isPressed = false;
         for (int s : state) {
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 09ca5c5..383e6ef 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -170,7 +170,11 @@
     }
 
     public Drawable getFullResIcon(LauncherActivityInfo info) {
-        return mIconProvider.getIcon(info, mIconDpi);
+        return getFullResIcon(info, true);
+    }
+
+    public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
+        return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
     }
 
     protected Bitmap makeDefaultIcon(UserHandle user) {
@@ -749,7 +753,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 15;
+        private final static int DB_VERSION = 16;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index a5d3990..6872515 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -24,8 +24,11 @@
         return mSystemState;
     }
 
-
-    public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
+    /**
+     * @param flattenDrawable true if the caller does not care about the specification of the
+     *                        original icon as long as the flattened version looks the same.
+     */
+    public Drawable getIcon(LauncherActivityInfo info, int iconDpi, boolean flattenDrawable) {
         return info.getIcon(iconDpi);
     }
 }
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d224615..d7bebd1 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -76,6 +76,7 @@
     public int numFolderRows;
     public int numFolderColumns;
     public float iconSize;
+    public float landscapeIconSize;
     public int iconBitmapSize;
     public int fillResIconDpi;
     public float iconTextSize;
@@ -84,8 +85,6 @@
      * Number of icons inside the hotseat area.
      */
     public int numHotseatIcons;
-    float hotseatIconSize;
-    public float hotseatScale;
     int defaultLayoutId;
 
     public DeviceProfile landscapeProfile;
@@ -99,12 +98,12 @@
     public InvariantDeviceProfile(InvariantDeviceProfile p) {
         this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
                 p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
-                p.iconSize, p.iconTextSize, p.numHotseatIcons, p.hotseatIconSize,
+                p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
                 p.defaultLayoutId);
     }
 
     InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
-            float is, float its, int hs, float his, int dlId) {
+            float is, float lis, float its, int hs, int dlId) {
         name = n;
         minWidthDps = w;
         minHeightDps = h;
@@ -114,12 +113,10 @@
         numFolderColumns = fc;
         minAllAppsPredictionColumns = maapc;
         iconSize = is;
+        landscapeIconSize = lis;
         iconTextSize = its;
         numHotseatIcons = hs;
-        hotseatIconSize = his;
         defaultLayoutId = dlId;
-
-        hotseatScale = hotseatIconSize / iconSize;
     }
 
     @TargetApi(23)
@@ -152,17 +149,15 @@
         minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
 
         iconSize = interpolatedDeviceProfileOut.iconSize;
+        landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
         iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
         iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
-        hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
         // If the partner customization apk contains any grid overrides, apply them
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, dm);
 
-        hotseatScale = hotseatIconSize / iconSize;
-
         Point realSize = new Point();
         display.getRealSize(realSize);
         // The real size never changes. smallSide and largeSide will remain the
@@ -210,9 +205,9 @@
                             a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
                             a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
                             iconSize,
+                            a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
                             a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
                             a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
-                            a.getFloat(R.styleable.InvariantDeviceProfile_hotseatIconSize, iconSize),
                             a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0)));
                     a.recycle();
                 }
@@ -304,14 +299,14 @@
 
     private void add(InvariantDeviceProfile p) {
         iconSize += p.iconSize;
+        landscapeIconSize += p.landscapeIconSize;
         iconTextSize += p.iconTextSize;
-        hotseatIconSize += p.hotseatIconSize;
     }
 
     private InvariantDeviceProfile multiply(float w) {
         iconSize *= w;
+        landscapeIconSize *= w;
         iconTextSize *= w;
-        hotseatIconSize *= w;
         return this;
     }
 
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/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 0902b20..b7b75f8 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -26,7 +26,6 @@
 import android.preference.PreferenceFragment;
 import android.provider.Settings;
 import android.provider.Settings.System;
-import android.support.v4.os.BuildCompat;
 
 import com.android.launcher3.graphics.IconShapeOverride;
 
@@ -85,7 +84,7 @@
             }
 
             Preference iconBadgingPref = findPreference(ICON_BADGING_PREFERENCE_KEY);
-            if (!BuildCompat.isAtLeastO()) {
+            if (!Utilities.isAtLeastO()) {
                 getPreferenceScreen().removePreference(
                         findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
                 getPreferenceScreen().removePreference(iconBadgingPref);
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 3a3e13d..fd708c0 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -120,7 +120,9 @@
                 // Center the icon/folder
                 int cHeight = getCellContentHeight();
                 int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
-                int cellPaddingX = profile.cellPaddingXPx;
+                int cellPaddingX = mContainerType == CellLayout.WORKSPACE
+                        ? profile.workspaceCellPaddingXPx
+                        : (int) (profile.edgeMarginPx / 2f);
                 child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
             }
         } else {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c2987f8..9100fe2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -38,7 +38,6 @@
 import android.os.DeadObjectException;
 import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
-import android.support.v4.os.BuildCompat;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -86,7 +85,7 @@
     private static final Matrix sInverseMatrix = new Matrix();
 
     public static boolean isAtLeastO() {
-        return BuildCompat.isAtLeastO();
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
     }
 
     public static final boolean ATLEAST_NOUGAT_MR1 =
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 14ba2b7..ad1be7e 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -111,7 +111,7 @@
      * sizes (landscape vs portrait).
      */
     private static class CacheDb extends SQLiteCacheHelper {
-        private static final int DB_VERSION = 7;
+        private static final int DB_VERSION = 8;
 
         private static final String TABLE_NAME = "shortcut_and_widget_previews";
         private static final String COLUMN_COMPONENT = "componentName";
@@ -471,7 +471,7 @@
         RectF boxRect = drawBoxWithShadow(c, size, size);
 
         Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow(
-                mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, Build.VERSION_CODES.O);
+                mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, 0);
         Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
 
         boxRect.set(0, 0, iconSize, iconSize);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b955fe1..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;
@@ -71,6 +70,7 @@
 import com.android.launcher3.dragndrop.SpringLoadedDragController;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.popup.PopupContainerWithArrow;
@@ -254,7 +254,7 @@
     public static final int REORDER_TIMEOUT = 350;
     private final Alarm mFolderCreationAlarm = new Alarm();
     private final Alarm mReorderAlarm = new Alarm();
-    private FolderIcon.PreviewBackground mFolderCreateBg;
+    private PreviewBackground mFolderCreateBg;
     private FolderIcon mDragOverFolderIcon = null;
     private boolean mCreateUserFolderOnDrop = false;
     private boolean mAddToExistingFolderOnDrop = false;
@@ -2082,27 +2082,9 @@
         }
     }
 
-    /**
-     * 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;
 
-        // Make sure the drag was started by a long press as opposed to a long click.
-        if (!child.isInTouchMode()) {
-            return;
-        }
-
         mDragInfo = cellInfo;
         child.setVisibility(INVISIBLE);
 
@@ -2379,7 +2361,7 @@
                 // In order to keep everything continuous, we hand off the currently rendered
                 // folder background to the newly created icon. This preserves animation state.
                 fi.setFolderBackground(mFolderCreateBg);
-                mFolderCreateBg = new FolderIcon.PreviewBackground();
+                mFolderCreateBg = new PreviewBackground();
                 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
                         postAnimationRunnable);
             } else {
@@ -3060,7 +3042,7 @@
         final int cellX;
         final int cellY;
 
-        final FolderIcon.PreviewBackground bg = new FolderIcon.PreviewBackground();
+        final PreviewBackground bg = new PreviewBackground();
 
         public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
             this.layout = layout;
@@ -3869,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 051c161..4954e0c 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -299,10 +299,7 @@
 
     @Override
     public boolean onLongClick(final View v) {
-        // Return early if this is not initiated from a touch
-        if (!v.isInTouchMode()) return false;
         // When we have exited all apps or are in transition, disregard long clicks
-
         if (!mLauncher.isAppsViewVisible() ||
                 mLauncher.getWorkspace().isSwitchingState()) return false;
         // Return if global dragging is not enabled or we are already dragging
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 9c7372f..d6514a8 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.support.animation.DynamicAnimation;
 import android.support.animation.SpringAnimation;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
@@ -298,8 +297,8 @@
                 icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
                 icon.setOnFocusChangeListener(mIconFocusListener);
 
-                // Ensure the all apps icon height matches the workspace icons
-                icon.getLayoutParams().height = getCellSize().y;
+                // Ensure the all apps icon height matches the workspace icons in portrait mode.
+                icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
                 return new ViewHolder(icon);
             case VIEW_TYPE_DISCOVERY_ITEM:
                 AppDiscoveryItemView appDiscoveryItemView = (AppDiscoveryItemView) mLayoutInflater
@@ -336,10 +335,6 @@
         }
     }
 
-    private Point getCellSize() {
-        return mLauncher.getDeviceProfile().getCellSize();
-    }
-
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
         switch (holder.getItemViewType()) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index faadce4..4d112c6 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -49,7 +49,7 @@
     private static final boolean DBG = false;
 
     private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f);
-    private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(.5f);
+    private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f);
     private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
     private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
     private final VerticalPullDetector.ScrollInterpolator mScrollInterpolator
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
index 7800c01..13c4f63 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -17,7 +17,7 @@
     private static final boolean DBG = false;
     private static final String TAG = "VerticalPullDetector";
 
-    private float mTouchSlop;
+    private final float mTouchSlop;
 
     private int mScrollConditions;
     public static final int DIRECTION_UP = 1 << 0;
@@ -47,8 +47,6 @@
         SETTLING       // onDragEnd
     }
 
-    ;
-
     //------------------- ScrollState transition diagram -----------------------------------
     //
     // IDLE ->      (mDisplacement > mTouchSlop) -> DRAGGING
@@ -110,7 +108,7 @@
     private boolean mIgnoreSlopWhenSettling;
 
     /* Client of this gesture detector can register a callback. */
-    Listener mListener;
+    private Listener mListener;
 
     public void setListener(Listener l) {
         mListener = l;
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 8e5452e..3f06ec9 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;
 
 /**
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 647c315..cc3e5a7 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -29,23 +29,22 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-
+import android.util.ArrayMap;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
-
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 public class LauncherAppsCompatVL extends LauncherAppsCompat {
 
     protected final LauncherApps mLauncherApps;
     protected final Context mContext;
 
-    private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks = new HashMap<>();
+    private final ArrayMap<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks =
+        new ArrayMap<>();
 
     LauncherAppsCompatVL(Context context) {
         mContext = context;
@@ -131,43 +130,52 @@
     }
 
     private static class WrappedCallback extends LauncherApps.Callback {
-        private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
+        private final LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
 
         public WrappedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
             mCallback = callback;
         }
 
+        @Override
         public void onPackageRemoved(String packageName, UserHandle user) {
             mCallback.onPackageRemoved(packageName, user);
         }
 
+        @Override
         public void onPackageAdded(String packageName, UserHandle user) {
             mCallback.onPackageAdded(packageName, user);
         }
 
+        @Override
         public void onPackageChanged(String packageName, UserHandle user) {
             mCallback.onPackageChanged(packageName, user);
         }
 
+        @Override
         public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
             mCallback.onPackagesAvailable(packageNames, user, replacing);
         }
 
+        @Override
         public void onPackagesUnavailable(String[] packageNames, UserHandle user,
                 boolean replacing) {
             mCallback.onPackagesUnavailable(packageNames, user, replacing);
         }
 
+        @Override
         public void onPackagesSuspended(String[] packageNames, UserHandle user) {
             mCallback.onPackagesSuspended(packageNames, user);
         }
 
+        @Override
         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
             mCallback.onPackagesUnsuspended(packageNames, user);
         }
 
-        public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
-                UserHandle user) {
+        @Override
+        public void onShortcutsChanged(@NonNull String packageName,
+            @NonNull List<ShortcutInfo> shortcuts,
+            @NonNull UserHandle user) {
             List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
             for (ShortcutInfo shortcutInfo : shortcuts) {
                 shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 29789c8..01893bd 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -84,6 +84,8 @@
     private int mPendingBindWidgetId;
     private Bundle mWidgetOptions;
 
+    private boolean mFinishOnPause = false;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -163,6 +165,7 @@
 
         startActivity(homeIntent,
                 ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+        mFinishOnPause = true;
 
         // Start a system drag and drop. We use a transparent bitmap as preview for system drag
         // as the preview is handled internally by launcher.
@@ -182,6 +185,14 @@
         return false;
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mFinishOnPause) {
+            finish();
+        }
+    }
+
     private void setupShortcut() {
         PinShortcutRequestActivityInfo shortcutInfo =
                 new PinShortcutRequestActivityInfo(mRequest, this);
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/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 9626cc6..022b3b8 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -29,6 +29,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Matrix;
@@ -39,6 +40,7 @@
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -50,6 +52,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
@@ -59,12 +62,19 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.IconNormalizer;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.Arrays;
+import java.util.List;
 
 public class DragView extends FrameLayout {
     public static final int COLOR_CHANGE_DURATION = 120;
@@ -195,7 +205,8 @@
         if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) {
             return;
         }
-        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
+                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             return;
         }
         // Load the adaptive icon on a background thread and add the view in ui thread.
@@ -204,10 +215,9 @@
             @Override
             public void run() {
                 LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
-                LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(mLauncher)
-                        .resolveActivity(info.getIntent(), info.user);
-                Drawable dr =  (activityInfo != null) ? appState.getIconCache()
-                        .getFullResIcon(activityInfo) : null;
+                Object[] outObj = new Object[1];
+                Drawable dr = getFullDrawable(info, appState, outObj);
+
                 if (dr instanceof AdaptiveIconDrawable) {
                     int w = mBitmap.getWidth();
                     int h = mBitmap.getHeight();
@@ -224,10 +234,7 @@
                     mSpringX = setupSpringAnimation(-w/4, w/4, DynamicAnimation.TRANSLATION_X);
                     mSpringY = setupSpringAnimation(-h/4, h/4, DynamicAnimation.TRANSLATION_Y);
 
-                    mBadge = mLauncher.getPackageManager()
-                            .getUserBadgedIcon(new FixedSizeEmptyDrawable(
-                                    appState.getInvariantDeviceProfile().iconBitmapSize),
-                                    activityInfo.getUser());
+                    mBadge = getBadge(info, appState, outObj[0]);
                     int blurMargin = (int) blurSizeOutline / 2;
                     mBadge.setBounds(blurMargin, blurMargin, w - blurMargin, h - blurMargin);
 
@@ -239,12 +246,84 @@
                             addView(mBgImageView);
                             addView(mFgImageView);
                             setWillNotDraw(true);
+
+                            if (info.isDisabled()) {
+                                FastBitmapDrawable d = new FastBitmapDrawable(null);
+                                d.setIsDisabled(true);
+                                ColorFilter cf = d.getColorFilter();
+                                mBgImageView.setColorFilter(cf);
+                                mFgImageView.setColorFilter(cf);
+                                mBadge.setColorFilter(cf);
+                            }
                         }
                     });
                 }
             }});
     }
 
+    /**
+     * Returns the full drawable for {@param info}.
+     * @param outObj this is set to the internal data associated with {@param info},
+     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
+     */
+    private Drawable getFullDrawable(ItemInfo info, LauncherAppState appState, Object[] outObj) {
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+            LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(mLauncher)
+                    .resolveActivity(info.getIntent(), info.user);
+            outObj[0] = activityInfo;
+            return (activityInfo != null) ? appState.getIconCache()
+                    .getFullResIcon(activityInfo, false) : null;
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            if (info instanceof PendingAddShortcutInfo) {
+                ShortcutConfigActivityInfo activityInfo =
+                        ((PendingAddShortcutInfo) info).activityInfo;
+                outObj[0] = activityInfo;
+                return activityInfo.getFullResIcon(appState.getIconCache());
+            }
+            ShortcutKey key = ShortcutKey.fromItemInfo(info);
+            DeepShortcutManager sm = DeepShortcutManager.getInstance(mLauncher);
+            List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+                    key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
+            if (si.isEmpty()) {
+                return null;
+            } else {
+                outObj[0] = si.get(0);
+                return sm.getShortcutIconDrawable(si.get(0),
+                        appState.getInvariantDeviceProfile().fillResIconDpi);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * For apps icons and shortcut icons that have badges, this method creates a drawable that can
+     * later on be rendered on top of the layers for the badges. For app icons, work profile badges
+     * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
+     * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
+     **/
+
+    @TargetApi(Build.VERSION_CODES.O)
+    private Drawable getBadge(ItemInfo info, LauncherAppState appState, Object obj) {
+        int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            if (info.id == ItemInfo.NO_ID || !(obj instanceof ShortcutInfoCompat)) {
+                // The item is not yet added on home screen.
+                return new FixedSizeEmptyDrawable(iconSize);
+            }
+            ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
+            Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
+
+            float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
+            float insetFraction = (iconSize - badgeSize) / iconSize;
+            return new InsetDrawable(new FastBitmapDrawable(badge),
+                    insetFraction, insetFraction, 0, 0);
+        } else {
+            return mLauncher.getPackageManager()
+                    .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
+        }
+    }
+
     private ImageView setupImageView(Drawable drawable, float normalizationScale) {
         FrameLayout.LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
         ImageView imageViewOut = new ImageView(getContext());
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
index 92cb5dc..cc0e0be 100644
--- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java
+++ b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
@@ -29,6 +29,7 @@
 import android.support.v7.graphics.Palette;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.List;
 
@@ -47,6 +48,9 @@
      * Launcher will be notified in Launcher#onSettingsChanged(String, String).
      */
     public static void startColorExtractionServiceIfNecessary(final Context context) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            return;
+        }
         // Run on a background thread, since the service is asynchronous anyway.
         Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
@@ -60,6 +64,9 @@
 
     /** Starts the {@link ColorExtractionService} without checking the wallpaper id */
     public static void startColorExtractionService(Context context) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            return;
+        }
         JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
                 Context.JOB_SCHEDULER_SERVICE);
         jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID,
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 0df787a..ff357c0 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -20,7 +20,7 @@
     private float mBaselineIconScale;
 
     @Override
-    public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+    public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
         mAvailableSpace = availableSpace;
         mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f;
         mIconSize = intrinsicIconSize;
@@ -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 2838351..aad8123 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -283,9 +283,6 @@
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
             ShortcutInfo item = (ShortcutInfo) tag;
-            if (!v.isInTouchMode()) {
-                return false;
-            }
 
             mEmptyCellRank = item.rank;
             mCurrentDragView = v;
@@ -335,6 +332,7 @@
         if (mIsExternalDrag && mDragInProgress) {
             completeDragExit();
         }
+        mDragInProgress = false;
         mDragController.removeDragListener(this);
     }
 
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 74e8d3b..3648c60 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PropertyResetListener;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Themes;
@@ -57,7 +58,7 @@
     private GradientDrawable mFolderBackground;
 
     private FolderIcon mFolderIcon;
-    private FolderIcon.PreviewBackground mPreviewBackground;
+    private PreviewBackground mPreviewBackground;
 
     private Context mContext;
     private Launcher mLauncher;
@@ -70,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") {
@@ -87,23 +87,6 @@
                 }
             };
 
-    private static final Property<List<BubbleTextView>, Integer> ITEMS_TEXT_COLOR_PROPERTY =
-            new Property<List<BubbleTextView>, Integer>(Integer.class, "textColor") {
-                @Override
-                public Integer get(List<BubbleTextView> items) {
-                    return items.get(0).getCurrentTextColor();
-                }
-
-                @Override
-                public void set(List<BubbleTextView> items, Integer color) {
-                    int size = items.size();
-
-                    for (int i = 0; i < size; ++i) {
-                        items.get(i).setTextColor(color);
-                    }
-                }
-            };
-
     public FolderAnimationManager(Folder folder, boolean isOpening) {
         mFolder = folder;
         mContent = folder.mContent;
@@ -183,12 +166,6 @@
                 ColorUtils.setAlphaComponent(finalColor, mPreviewBackground.getBackgroundAlpha());
         mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
 
-        // Initialize the Folder items' text.
-        final List<BubbleTextView> items = mFolder.getItemsOnCurrentPage();
-        final int finalTextColor = Themes.getAttrColor(mContext, android.R.attr.textColorSecondary);
-        ITEMS_TEXT_COLOR_PROPERTY.set(items, mIsOpening ? Color.TRANSPARENT
-                : finalTextColor);
-
         // Set up the reveal animation that clips the Folder.
         int totalOffsetX = paddingOffsetX + previewItemOffsetX;
         Rect startRect = new Rect(
@@ -203,30 +180,41 @@
         // Create the animators.
         AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
 
+        // Initialize the Folder items' text.
+        PropertyResetListener colorResetListener = new PropertyResetListener(
+                BubbleTextView.TEXT_ALPHA_PROPERTY,
+                Color.alpha(Themes.getAttrColor(mContext, android.R.attr.textColorSecondary)));
+        for (BubbleTextView icon : mFolder.getItemsOnCurrentPage()) {
+            if (mIsOpening) {
+                icon.setTextVisibility(false);
+            }
+            ObjectAnimator anim = icon.createTextAlphaAnimator(mIsOpening);
+            anim.addListener(colorResetListener);
+            play(a, anim);
+        }
+
         play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
         play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
         play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
-        play(a, getAnimator(items, ITEMS_TEXT_COLOR_PROPERTY, Color.TRANSPARENT, finalTextColor));
         play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
         play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
         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);
-                ITEMS_TEXT_COLOR_PROPERTY.set(items, finalTextColor);
                 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();
-                }
             }
         });
 
@@ -335,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);
     }
 
@@ -355,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 b6f05f3..1cc285e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -19,24 +19,14 @@
 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.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.RadialGradient;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
-import android.support.v4.graphics.ColorUtils;
+import android.support.annotation.NonNull;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.LayoutInflater;
@@ -56,7 +46,6 @@
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.FolderInfo.FolderListener;
 import com.android.launcher3.ItemInfo;
@@ -75,8 +64,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.PreloadIconDrawable;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -100,8 +87,6 @@
     private CheckLongPressHelper mLongPressHelper;
     private StylusEventHelper mStylusEventHelper;
 
-    // The number of icons to display in the
-    private static final int CONSUMPTION_ANIMATION_DURATION = 100;
     private static final int DROP_IN_ANIMATION_DURATION = 400;
     private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
     private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
@@ -116,7 +101,7 @@
 
     // These variables are all associated with the drawing of the preview; they are stored
     // as member variables for shared usage and to avoid computation on each frame
-    private int mIntrinsicIconSize = -1;
+    private float mIntrinsicIconSize = -1;
     private int mTotalWidth = -1;
     private int mPrevTopPadding = -1;
 
@@ -132,7 +117,7 @@
 
     FolderIconPreviewVerifier mPreviewVerifier;
     private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
+    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<>();
     private Drawable mReferenceDrawable = null;
 
     private Alarm mOpenAlarm = new Alarm();
@@ -240,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) {
@@ -436,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);
@@ -478,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
@@ -510,410 +461,16 @@
         Drawable d = params.drawable;
 
         if (d != null) {
-            mTempBounds.set(d.getBounds());
-            d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
-            boolean isPreloadIcon = d instanceof PreloadIconDrawable;
-            if (!isPreloadIcon && d instanceof FastBitmapDrawable) {
-                FastBitmapDrawable fd = (FastBitmapDrawable) d;
-                fd.drawWithBrightness(canvas, params.overlayAlpha);
-            } else {
-                d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
-                        PorterDuff.Mode.SRC_ATOP);
-                d.draw(canvas);
-                d.clearColorFilter();
-            }
-            d.setBounds(mTempBounds);
+            Rect bounds = d.getBounds();
+            canvas.save();
+            canvas.translate(-bounds.left, -bounds.top);
+            canvas.scale(mIntrinsicIconSize / bounds.width(), mIntrinsicIconSize / bounds.height());
+            d.draw(canvas);
+            canvas.restore();
         }
         canvas.restore();
     }
 
-    /**
-     * This object represents a FolderIcon preview background. It stores drawing / measurement
-     * information, handles drawing, and animation (accept state <--> rest state).
-     */
-    public static class PreviewBackground {
-
-        private final PorterDuffXfermode mClipPorterDuffXfermode
-                = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
-        // Create a RadialGradient such that it draws a black circle and then extends with
-        // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
-        // just at the edge quickly change it to transparent.
-        private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
-                new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
-                new float[] {0, 0.999f, 1},
-                Shader.TileMode.CLAMP);
-
-        private final PorterDuffXfermode mShadowPorterDuffXfermode
-                = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
-        private RadialGradient mShadowShader = null;
-
-        private final Matrix mShaderMatrix = new Matrix();
-        private final Path mPath = new Path();
-
-        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
-        private float mScale = 1f;
-        private float mColorMultiplier = 1f;
-        private int mBgColor;
-        private float mStrokeWidth;
-        private int mStrokeAlpha = MAX_BG_OPACITY;
-        private int mShadowAlpha = 255;
-        private View mInvalidateDelegate;
-
-        public int previewSize;
-        private int basePreviewOffsetX;
-        private int basePreviewOffsetY;
-
-        private CellLayout mDrawingDelegate;
-        public int delegateCellX;
-        public int delegateCellY;
-
-        // When the PreviewBackground is drawn under an icon (for creating a folder) the border
-        // should not occlude the icon
-        public boolean isClipping = true;
-
-        // Drawing / animation configurations
-        private static final float ACCEPT_SCALE_FACTOR = 1.25f;
-        private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
-
-        // Expressed on a scale from 0 to 255.
-        private static final int BG_OPACITY = 160;
-        private static final int MAX_BG_OPACITY = 225;
-        private static final int SHADOW_OPACITY = 40;
-
-        ValueAnimator mScaleAnimator;
-        ObjectAnimator mStrokeAlphaAnimator;
-        ObjectAnimator mShadowAnimator;
-
-        private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
-                new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
-                    @Override
-                    public Integer get(PreviewBackground previewBackground) {
-                        return previewBackground.mStrokeAlpha;
-                    }
-
-                    @Override
-                    public void set(PreviewBackground previewBackground, Integer alpha) {
-                        previewBackground.mStrokeAlpha = alpha;
-                        previewBackground.invalidate();
-                    }
-                };
-
-        private static final Property<PreviewBackground, Integer> SHADOW_ALPHA =
-                new Property<PreviewBackground, Integer>(Integer.class, "shadowAlpha") {
-                    @Override
-                    public Integer get(PreviewBackground previewBackground) {
-                        return previewBackground.mShadowAlpha;
-                    }
-
-                    @Override
-                    public void set(PreviewBackground previewBackground, Integer alpha) {
-                        previewBackground.mShadowAlpha = alpha;
-                        previewBackground.invalidate();
-                    }
-                };
-
-        public void setup(Launcher launcher, View invalidateDelegate,
-                   int availableSpace, int topPadding) {
-            mInvalidateDelegate = invalidateDelegate;
-            mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
-
-            DeviceProfile grid = launcher.getDeviceProfile();
-            final int previewSize = grid.folderIconSizePx;
-            final int previewPadding = grid.folderIconPreviewPadding;
-
-            this.previewSize = (previewSize - 2 * previewPadding);
-
-            basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
-            basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
-
-            // Stroke width is 1dp
-            mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
-
-            float radius = getScaledRadius();
-            float shadowRadius = radius + mStrokeWidth;
-            int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
-            mShadowShader = new RadialGradient(0, 0, 1,
-                    new int[] {shadowColor, Color.TRANSPARENT},
-                    new float[] {radius / shadowRadius, 1},
-                    Shader.TileMode.CLAMP);
-
-            invalidate();
-        }
-
-        int getRadius() {
-            return previewSize / 2;
-        }
-
-        int getScaledRadius() {
-            return (int) (mScale * getRadius());
-        }
-
-        int getOffsetX() {
-            return basePreviewOffsetX - (getScaledRadius() - getRadius());
-        }
-
-        int getOffsetY() {
-            return basePreviewOffsetY - (getScaledRadius() - getRadius());
-        }
-
-        /**
-         * Returns the progress of the scale animation, where 0 means the scale is at 1f
-         * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
-         */
-        float getScaleProgress() {
-            return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
-        }
-
-        void invalidate() {
-            if (mInvalidateDelegate != null) {
-                mInvalidateDelegate.invalidate();
-            }
-
-            if (mDrawingDelegate != null) {
-                mDrawingDelegate.invalidate();
-            }
-        }
-
-        void setInvalidateDelegate(View invalidateDelegate) {
-            mInvalidateDelegate = invalidateDelegate;
-            invalidate();
-        }
-
-        public void drawBackground(Canvas canvas) {
-            mPaint.setStyle(Paint.Style.FILL);
-            int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-            mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
-
-            drawCircle(canvas, 0 /* deltaRadius */);
-
-            // Draw shadow.
-            if (mShadowShader == null) {
-                return;
-            }
-            float radius = getScaledRadius();
-            float shadowRadius = radius + mStrokeWidth;
-            mPaint.setColor(Color.BLACK);
-            int offsetX = getOffsetX();
-            int offsetY = getOffsetY();
-            final int saveCount;
-            if (canvas.isHardwareAccelerated()) {
-                saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
-                        offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
-                        null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
-
-            } else {
-                saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-                clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
-            }
-
-            mShaderMatrix.setScale(shadowRadius, shadowRadius);
-            mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
-            mShadowShader.setLocalMatrix(mShaderMatrix);
-            mPaint.setAlpha(mShadowAlpha);
-            mPaint.setShader(mShadowShader);
-            canvas.drawPaint(mPaint);
-            mPaint.setAlpha(255);
-            mPaint.setShader(null);
-            if (canvas.isHardwareAccelerated()) {
-                mPaint.setXfermode(mShadowPorterDuffXfermode);
-                canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
-                mPaint.setXfermode(null);
-            }
-
-            canvas.restoreToCount(saveCount);
-        }
-
-        public void fadeInBackgroundShadow() {
-            if (mShadowAnimator != null) {
-                mShadowAnimator.cancel();
-            }
-            mShadowAnimator = ObjectAnimator
-                    .ofInt(this, SHADOW_ALPHA, 0, 255)
-                    .setDuration(100);
-            mShadowAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mShadowAnimator = null;
-                }
-            });
-            mShadowAnimator.start();
-        }
-
-        public void animateBackgroundStroke() {
-            if (mStrokeAlphaAnimator != null) {
-                mStrokeAlphaAnimator.cancel();
-            }
-            mStrokeAlphaAnimator = ObjectAnimator
-                    .ofInt(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
-                    .setDuration(100);
-            mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mStrokeAlphaAnimator = null;
-                }
-            });
-            mStrokeAlphaAnimator.start();
-        }
-
-        public void drawBackgroundStroke(Canvas canvas) {
-            mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
-            mPaint.setStyle(Paint.Style.STROKE);
-            mPaint.setStrokeWidth(mStrokeWidth);
-            drawCircle(canvas, 1 /* deltaRadius */);
-        }
-
-        public void drawLeaveBehind(Canvas canvas) {
-            float originalScale = mScale;
-            mScale = 0.5f;
-
-            mPaint.setStyle(Paint.Style.FILL);
-            mPaint.setColor(Color.argb(160, 245, 245, 245));
-            drawCircle(canvas, 0 /* deltaRadius */);
-
-            mScale = originalScale;
-        }
-
-        private void drawCircle(Canvas canvas,float deltaRadius) {
-            float radius = getScaledRadius();
-            canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
-                    radius - deltaRadius, mPaint);
-        }
-
-        // It is the callers responsibility to save and restore the canvas layers.
-        private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
-            mPath.reset();
-            float r = getScaledRadius();
-            mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
-            canvas.clipPath(mPath, op);
-        }
-
-        // It is the callers responsibility to save and restore the canvas layers.
-        private void clipCanvasHardware(Canvas canvas) {
-            mPaint.setColor(Color.BLACK);
-            mPaint.setXfermode(mClipPorterDuffXfermode);
-
-            float radius = getScaledRadius();
-            mShaderMatrix.setScale(radius, radius);
-            mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
-            mClipShader.setLocalMatrix(mShaderMatrix);
-            mPaint.setShader(mClipShader);
-            canvas.drawPaint(mPaint);
-            mPaint.setXfermode(null);
-            mPaint.setShader(null);
-        }
-
-        private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
-            if (mDrawingDelegate != delegate) {
-                delegate.addFolderBackground(this);
-            }
-
-            mDrawingDelegate = delegate;
-            delegateCellX = cellX;
-            delegateCellY = cellY;
-
-            invalidate();
-        }
-
-        private void clearDrawingDelegate() {
-            if (mDrawingDelegate != null) {
-                mDrawingDelegate.removeFolderBackground(this);
-            }
-
-            mDrawingDelegate = null;
-            invalidate();
-        }
-
-        private boolean drawingDelegated() {
-            return mDrawingDelegate != null;
-        }
-
-        private void animateScale(float finalScale, float finalMultiplier,
-                final Runnable onStart, final Runnable onEnd) {
-            final float scale0 = mScale;
-            final float scale1 = finalScale;
-
-            final float bgMultiplier0 = mColorMultiplier;
-            final float bgMultiplier1 = finalMultiplier;
-
-            if (mScaleAnimator != null) {
-                mScaleAnimator.cancel();
-            }
-
-            mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-
-            mScaleAnimator.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float prog = animation.getAnimatedFraction();
-                    mScale = prog * scale1 + (1 - prog) * scale0;
-                    mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
-                    invalidate();
-                }
-            });
-            mScaleAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    if (onStart != null) {
-                        onStart.run();
-                    }
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (onEnd != null) {
-                        onEnd.run();
-                    }
-                    mScaleAnimator = null;
-                }
-            });
-
-            mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
-            mScaleAnimator.start();
-        }
-
-        public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
-            Runnable onStart = new Runnable() {
-                @Override
-                public void run() {
-                    delegateDrawing(cl, cellX, cellY);
-                }
-            };
-            animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
-        }
-
-        public void animateToRest() {
-            // This can be called multiple times -- we need to make sure the drawing delegate
-            // is saved and restored at the beginning of the animation, since cancelling the
-            // existing animation can clear the delgate.
-            final CellLayout cl = mDrawingDelegate;
-            final int cellX = delegateCellX;
-            final int cellY = delegateCellY;
-
-            Runnable onStart = new Runnable() {
-                @Override
-                public void run() {
-                    delegateDrawing(cl, cellX, cellY);
-                }
-            };
-            Runnable onEnd = new Runnable() {
-                @Override
-                public void run() {
-                    clearDrawingDelegate();
-                }
-            };
-            animateScale(1f, 1f, onStart, onEnd);
-        }
-
-        public int getBackgroundAlpha() {
-            return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-        }
-
-        public float getStrokeWidth() {
-            return mStrokeWidth;
-        }
-    }
-
     public void setFolderBackground(PreviewBackground bg) {
         mBackground = bg;
         mBackground.setInvalidateDelegate(this);
@@ -989,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();
@@ -1107,6 +589,16 @@
         return itemsToDisplay;
     }
 
+    @Override
+    protected boolean verifyDrawable(@NonNull Drawable who) {
+        for (int i = 0; i < mDrawingParams.size(); i++) {
+            if (mDrawingParams.get(i).drawable == who) {
+                return true;
+            }
+        }
+        return super.verifyDrawable(who);
+    }
+
     private void updateItemDrawingParams(boolean animate) {
         List<BubbleTextView> items = getItemsToDisplay();
         int nItemsInPreview = items.size();
@@ -1125,13 +617,19 @@
             PreviewItemDrawingParams p = mDrawingParams.get(i);
             p.drawable = items.get(i).getCompoundDrawables()[1];
 
+            if (p.drawable != null && !mFolder.isOpen()) {
+                // Set the callback to FolderIcon as it is responsible to drawing the icon. The
+                // callback will be release when the folder is opened.
+                p.drawable.setCallback(this);
+            }
+
             if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
                 computePreviewItemDrawingParams(i, nItemsInPreview, p);
                 if (mReferenceDrawable == null) {
                     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) {
@@ -1292,10 +790,10 @@
         }
     }
 
-    public interface PreviewLayoutRule {
+    interface PreviewLayoutRule {
         PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
             PreviewItemDrawingParams params);
-        void init(int availableSpace, int intrinsicIconSize, boolean rtl);
+        void init(int availableSpace, float intrinsicIconSize, boolean rtl);
         float scaleForItem(int index, int totalNumItems);
         float getIconSize();
         int maxNumItems();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 87e42eb..d0ac9f4 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,8 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -46,7 +48,6 @@
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
@@ -72,7 +73,7 @@
     private final LayoutInflater mInflater;
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
 
-    @Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
+    @Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>();
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private final int mMaxCountX;
@@ -111,7 +112,7 @@
     public void setFolder(Folder folder) {
         mFolder = folder;
         mKeyListener = new PagedFolderKeyEventListener(folder);
-        mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
+        mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
         initParentViews(folder);
     }
 
@@ -180,21 +181,13 @@
         super.dispatchDraw(canvas);
     }
 
-    public void onIconInvalidated(BubbleTextView icon) {
-        FolderIcon folderIcon = mFolder.mFolderIcon;
-        if (icon.getTag() instanceof ItemInfo
-                && folderIcon.mPreviewVerifier.isItemInPreview(((ItemInfo) icon.getTag()).rank)) {
-            folderIcon.invalidate();
-        }
-    }
-
     /**
      * Binds items to the layout.
      * @return list of items that could not be bound, probably because we hit the max size limit.
      */
     public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
-        ArrayList<View> icons = new ArrayList<View>();
-        ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
+        ArrayList<View> icons = new ArrayList<>();
+        ArrayList<ShortcutInfo> extra = new ArrayList<>();
 
         for (ShortcutInfo item : items) {
             if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
@@ -328,7 +321,7 @@
 
     @SuppressLint("RtlHardcoded")
     private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
-        ArrayList<CellLayout> pages = new ArrayList<CellLayout>();
+        ArrayList<CellLayout> pages = new ArrayList<>();
         for (int i = 0; i < getChildCount(); i++) {
             CellLayout page = (CellLayout) getChildAt(i);
             page.removeAllViews();
@@ -535,7 +528,7 @@
      */
     public void completePendingPageChanges() {
         if (!mPendingAnimations.isEmpty()) {
-            HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations);
+            ArrayMap<View, Runnable> pendingViews = new ArrayMap<>(mPendingAnimations);
             for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) {
                 e.getKey().animate().cancel();
                 e.getValue().run();
@@ -564,7 +557,14 @@
         if (page != null) {
             ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
             for (int i = parent.getChildCount() - 1; i >= 0; i--) {
-                ((BubbleTextView) parent.getChildAt(i)).verifyHighRes();
+                BubbleTextView icon = ((BubbleTextView) parent.getChildAt(i));
+                icon.verifyHighRes();
+                // Set the callback back to the actual icon, in case
+                // it was captured by the FolderIcon
+                Drawable d = icon.getCompoundDrawables()[1];
+                if (d != null) {
+                    d.setCallback(icon);
+                }
             }
         }
     }
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/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
new file mode 100644
index 0000000..44ebbcd
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -0,0 +1,430 @@
+/*
+ * 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.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RadialGradient;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.util.Themes;
+
+/**
+ * This object represents a FolderIcon preview background. It stores drawing / measurement
+ * information, handles drawing, and animation (accept state <--> rest state).
+ */
+public class PreviewBackground {
+
+    private static final int CONSUMPTION_ANIMATION_DURATION = 100;
+
+    private final PorterDuffXfermode mClipPorterDuffXfermode
+            = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
+    // Create a RadialGradient such that it draws a black circle and then extends with
+    // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
+    // just at the edge quickly change it to transparent.
+    private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
+            new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
+            new float[] {0, 0.999f, 1},
+            Shader.TileMode.CLAMP);
+
+    private final PorterDuffXfermode mShadowPorterDuffXfermode
+            = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
+    private RadialGradient mShadowShader = null;
+
+    private final Matrix mShaderMatrix = new Matrix();
+    private final Path mPath = new Path();
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    float mScale = 1f;
+    private float mColorMultiplier = 1f;
+    private int mBgColor;
+    private float mStrokeWidth;
+    private int mStrokeAlpha = MAX_BG_OPACITY;
+    private int mShadowAlpha = 255;
+    private View mInvalidateDelegate;
+
+    int previewSize;
+    int basePreviewOffsetX;
+    int basePreviewOffsetY;
+
+    private CellLayout mDrawingDelegate;
+    public int delegateCellX;
+    public int delegateCellY;
+
+    // When the PreviewBackground is drawn under an icon (for creating a folder) the border
+    // should not occlude the icon
+    public boolean isClipping = true;
+
+    // Drawing / animation configurations
+    private static final float ACCEPT_SCALE_FACTOR = 1.25f;
+    private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
+
+    // Expressed on a scale from 0 to 255.
+    private static final int BG_OPACITY = 160;
+    private static final int MAX_BG_OPACITY = 225;
+    private static final int SHADOW_OPACITY = 40;
+
+    private ValueAnimator mScaleAnimator;
+    private ObjectAnimator mStrokeAlphaAnimator;
+    private ObjectAnimator mShadowAnimator;
+
+    private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
+            new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
+                @Override
+                public Integer get(PreviewBackground previewBackground) {
+                    return previewBackground.mStrokeAlpha;
+                }
+
+                @Override
+                public void set(PreviewBackground previewBackground, Integer alpha) {
+                    previewBackground.mStrokeAlpha = alpha;
+                    previewBackground.invalidate();
+                }
+            };
+
+    private static final Property<PreviewBackground, Integer> SHADOW_ALPHA =
+            new Property<PreviewBackground, Integer>(Integer.class, "shadowAlpha") {
+                @Override
+                public Integer get(PreviewBackground previewBackground) {
+                    return previewBackground.mShadowAlpha;
+                }
+
+                @Override
+                public void set(PreviewBackground previewBackground, Integer alpha) {
+                    previewBackground.mShadowAlpha = alpha;
+                    previewBackground.invalidate();
+                }
+            };
+
+    public void setup(Launcher launcher, View invalidateDelegate,
+                      int availableSpace, int topPadding) {
+        mInvalidateDelegate = invalidateDelegate;
+        mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
+
+        DeviceProfile grid = launcher.getDeviceProfile();
+        final int previewSize = grid.folderIconSizePx;
+        final int previewPadding = grid.folderIconPreviewPadding;
+
+        this.previewSize = (previewSize - 2 * previewPadding);
+
+        basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
+        basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+
+        // Stroke width is 1dp
+        mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
+
+        float radius = getScaledRadius();
+        float shadowRadius = radius + mStrokeWidth;
+        int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
+        mShadowShader = new RadialGradient(0, 0, 1,
+                new int[] {shadowColor, Color.TRANSPARENT},
+                new float[] {radius / shadowRadius, 1},
+                Shader.TileMode.CLAMP);
+
+        invalidate();
+    }
+
+    int getRadius() {
+        return previewSize / 2;
+    }
+
+    int getScaledRadius() {
+        return (int) (mScale * getRadius());
+    }
+
+    int getOffsetX() {
+        return basePreviewOffsetX - (getScaledRadius() - getRadius());
+    }
+
+    int getOffsetY() {
+        return basePreviewOffsetY - (getScaledRadius() - getRadius());
+    }
+
+    /**
+     * Returns the progress of the scale animation, where 0 means the scale is at 1f
+     * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
+     */
+    float getScaleProgress() {
+        return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
+    }
+
+    void invalidate() {
+        if (mInvalidateDelegate != null) {
+            mInvalidateDelegate.invalidate();
+        }
+
+        if (mDrawingDelegate != null) {
+            mDrawingDelegate.invalidate();
+        }
+    }
+
+    void setInvalidateDelegate(View invalidateDelegate) {
+        mInvalidateDelegate = invalidateDelegate;
+        invalidate();
+    }
+
+    public void drawBackground(Canvas canvas) {
+        mPaint.setStyle(Paint.Style.FILL);
+        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+        mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
+
+        drawCircle(canvas, 0 /* deltaRadius */);
+
+        // Draw shadow.
+        if (mShadowShader == null) {
+            return;
+        }
+        float radius = getScaledRadius();
+        float shadowRadius = radius + mStrokeWidth;
+        mPaint.setColor(Color.BLACK);
+        int offsetX = getOffsetX();
+        int offsetY = getOffsetY();
+        final int saveCount;
+        if (canvas.isHardwareAccelerated()) {
+            saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
+                    offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
+                    null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+
+        } else {
+            saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+            clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
+        }
+
+        mShaderMatrix.setScale(shadowRadius, shadowRadius);
+        mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
+        mShadowShader.setLocalMatrix(mShaderMatrix);
+        mPaint.setAlpha(mShadowAlpha);
+        mPaint.setShader(mShadowShader);
+        canvas.drawPaint(mPaint);
+        mPaint.setAlpha(255);
+        mPaint.setShader(null);
+        if (canvas.isHardwareAccelerated()) {
+            mPaint.setXfermode(mShadowPorterDuffXfermode);
+            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
+            mPaint.setXfermode(null);
+        }
+
+        canvas.restoreToCount(saveCount);
+    }
+
+    public void fadeInBackgroundShadow() {
+        if (mShadowAnimator != null) {
+            mShadowAnimator.cancel();
+        }
+        mShadowAnimator = ObjectAnimator
+                .ofInt(this, SHADOW_ALPHA, 0, 255)
+                .setDuration(100);
+        mShadowAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mShadowAnimator = null;
+            }
+        });
+        mShadowAnimator.start();
+    }
+
+    public void animateBackgroundStroke() {
+        if (mStrokeAlphaAnimator != null) {
+            mStrokeAlphaAnimator.cancel();
+        }
+        mStrokeAlphaAnimator = ObjectAnimator
+                .ofInt(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
+                .setDuration(100);
+        mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStrokeAlphaAnimator = null;
+            }
+        });
+        mStrokeAlphaAnimator.start();
+    }
+
+    public void drawBackgroundStroke(Canvas canvas) {
+        mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(mStrokeWidth);
+        drawCircle(canvas, 1 /* deltaRadius */);
+    }
+
+    public void drawLeaveBehind(Canvas canvas) {
+        float originalScale = mScale;
+        mScale = 0.5f;
+
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setColor(Color.argb(160, 245, 245, 245));
+        drawCircle(canvas, 0 /* deltaRadius */);
+
+        mScale = originalScale;
+    }
+
+    private void drawCircle(Canvas canvas,float deltaRadius) {
+        float radius = getScaledRadius();
+        canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
+                radius - deltaRadius, mPaint);
+    }
+
+    // It is the callers responsibility to save and restore the canvas layers.
+    void clipCanvasSoftware(Canvas canvas, Region.Op op) {
+        mPath.reset();
+        float r = getScaledRadius();
+        mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
+        canvas.clipPath(mPath, op);
+    }
+
+    // It is the callers responsibility to save and restore the canvas layers.
+    void clipCanvasHardware(Canvas canvas) {
+        mPaint.setColor(Color.BLACK);
+        mPaint.setXfermode(mClipPorterDuffXfermode);
+
+        float radius = getScaledRadius();
+        mShaderMatrix.setScale(radius, radius);
+        mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
+        mClipShader.setLocalMatrix(mShaderMatrix);
+        mPaint.setShader(mClipShader);
+        canvas.drawPaint(mPaint);
+        mPaint.setXfermode(null);
+        mPaint.setShader(null);
+    }
+
+    private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
+        if (mDrawingDelegate != delegate) {
+            delegate.addFolderBackground(this);
+        }
+
+        mDrawingDelegate = delegate;
+        delegateCellX = cellX;
+        delegateCellY = cellY;
+
+        invalidate();
+    }
+
+    private void clearDrawingDelegate() {
+        if (mDrawingDelegate != null) {
+            mDrawingDelegate.removeFolderBackground(this);
+        }
+
+        mDrawingDelegate = null;
+        invalidate();
+    }
+
+    boolean drawingDelegated() {
+        return mDrawingDelegate != null;
+    }
+
+    private void animateScale(float finalScale, float finalMultiplier,
+                              final Runnable onStart, final Runnable onEnd) {
+        final float scale0 = mScale;
+        final float scale1 = finalScale;
+
+        final float bgMultiplier0 = mColorMultiplier;
+        final float bgMultiplier1 = finalMultiplier;
+
+        if (mScaleAnimator != null) {
+            mScaleAnimator.cancel();
+        }
+
+        mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+
+        mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float prog = animation.getAnimatedFraction();
+                mScale = prog * scale1 + (1 - prog) * scale0;
+                mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
+                invalidate();
+            }
+        });
+        mScaleAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (onStart != null) {
+                    onStart.run();
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onEnd != null) {
+                    onEnd.run();
+                }
+                mScaleAnimator = null;
+            }
+        });
+
+        mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
+        mScaleAnimator.start();
+    }
+
+    public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
+        Runnable onStart = new Runnable() {
+            @Override
+            public void run() {
+                delegateDrawing(cl, cellX, cellY);
+            }
+        };
+        animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
+    }
+
+    public void animateToRest() {
+        // This can be called multiple times -- we need to make sure the drawing delegate
+        // is saved and restored at the beginning of the animation, since cancelling the
+        // existing animation can clear the delgate.
+        final CellLayout cl = mDrawingDelegate;
+        final int cellX = delegateCellX;
+        final int cellY = delegateCellY;
+
+        Runnable onStart = new Runnable() {
+            @Override
+            public void run() {
+                delegateDrawing(cl, cellX, cellY);
+            }
+        };
+        Runnable onEnd = new Runnable() {
+            @Override
+            public void run() {
+                clearDrawingDelegate();
+            }
+        };
+        animateScale(1f, 1f, onStart, onEnd);
+    }
+
+    public int getBackgroundAlpha() {
+        return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+    }
+
+    public float getStrokeWidth() {
+        return mStrokeWidth;
+    }
+}
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 12bca5f..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;
@@ -35,7 +33,7 @@
     private float mMaxPerspectiveShift;
 
     @Override
-    public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+    public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
         mAvailableSpaceInPreview = availableSpace;
 
         // cos(45) = 0.707  + ~= 0.1) = 0.8f
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/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 19e57024..830ca82 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -322,14 +322,16 @@
         IconCache cache = app.getIconCache();
         Bitmap unbadgedBitmap = unbadgedDrawable == null
                 ? cache.getDefaultIcon(Process.myUserHandle())
-                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context,
-                Build.VERSION_CODES.O);
+                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context, 0);
 
         if (!badged) {
             return unbadgedBitmap;
         }
         unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap, context);
+        return badgeWithBitmap(unbadgedBitmap, getShortcutInfoBadge(shortcutInfo, cache), context);
+    }
 
+    public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
         final Bitmap badgeBitmap;
         ComponentName cn = shortcutInfo.getActivity();
         if (cn != null) {
@@ -347,7 +349,7 @@
             cache.getTitleAndIconForApp(pkgInfo, false);
             badgeBitmap = pkgInfo.iconBitmap;
         }
-        return badgeWithBitmap(unbadgedBitmap, badgeBitmap, context);
+        return badgeBitmap;
     }
 
     /**
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index deb5b63..06dc7ac 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -178,7 +178,7 @@
         Rect bounds = getBounds();
 
         canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
-        drawInternal(canvas);
+        super.draw(canvas);
         canvas.restoreToCount(saveCount);
     }
 
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 8dca699..baaa66a 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,25 +16,25 @@
 
 package com.android.launcher3.notification;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
 import android.text.TextUtils;
-
+import android.util.ArraySet;
+import android.util.Pair;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -44,6 +44,7 @@
  * as well and when this service first connects. An instance of NotificationListener,
  * and its methods for getting notifications, can be obtained via {@link #getInstanceIfConnected()}.
  */
+@TargetApi(Build.VERSION_CODES.O)
 public class NotificationListener extends NotificationListenerService {
 
     private static final int MSG_NOTIFICATION_POSTED = 1;
@@ -57,9 +58,9 @@
     private final Handler mWorkerHandler;
     private final Handler mUiHandler;
 
-    private Ranking mTempRanking = new Ranking();
+    private final Ranking mTempRanking = new Ranking();
 
-    private Handler.Callback mWorkerCallback = new Handler.Callback() {
+    private final Handler.Callback mWorkerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message message) {
             switch (message.what) {
@@ -80,7 +81,7 @@
         }
     };
 
-    private Handler.Callback mUiCallback = new Handler.Callback() {
+    private final Handler.Callback mUiCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message message) {
             switch (message.what) {
@@ -163,9 +164,9 @@
      * An object containing data to send to MSG_NOTIFICATION_POSTED targets.
      */
     private class NotificationPostedMsg {
-        PackageUserKey packageUserKey;
-        NotificationKeyData notificationKey;
-        boolean shouldBeFilteredOut;
+        final PackageUserKey packageUserKey;
+        final NotificationKeyData notificationKey;
+        final boolean shouldBeFilteredOut;
 
         NotificationPostedMsg(StatusBarNotification sbn) {
             packageUserKey = PackageUserKey.fromNotification(sbn);
@@ -189,7 +190,8 @@
         StatusBarNotification[] notifications = NotificationListener.this
                 .getActiveNotifications(NotificationKeyData.extractKeysOnly(keys)
                         .toArray(new String[keys.size()]));
-        return notifications == null ? Collections.EMPTY_LIST : Arrays.asList(notifications);
+        return notifications == null
+            ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
     }
 
     /**
@@ -201,7 +203,7 @@
     private List<StatusBarNotification> filterNotifications(
             StatusBarNotification[] notifications) {
         if (notifications == null) return null;
-        Set<Integer> removedNotifications = new HashSet<>();
+        Set<Integer> removedNotifications = new ArraySet<>();
         for (int i = 0; i < notifications.length; i++) {
             if (shouldBeFilteredOut(notifications[i])) {
                 removedNotifications.add(i);
diff --git a/src/com/android/launcher3/notification/SwipeHelper.java b/src/com/android/launcher3/notification/SwipeHelper.java
index 5f03252..ebbe5fc 100644
--- a/src/com/android/launcher3/notification/SwipeHelper.java
+++ b/src/com/android/launcher3/notification/SwipeHelper.java
@@ -24,23 +24,20 @@
 import android.content.Context;
 import android.graphics.RectF;
 import android.os.Handler;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
-
 import com.android.launcher3.R;
 
-import java.util.HashMap;
-
 /**
  * This class was copied from com.android.systemui.
  */
 public class SwipeHelper {
-    static final String TAG = "SwipeHelper";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "SwipeHelper";
     private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
     private static final boolean CONSTRAIN_SWIPE = true;
@@ -50,10 +47,10 @@
     public static final int X = 0;
     public static final int Y = 1;
 
-    private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
-    private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
-    private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
-    private int MAX_DISMISS_VELOCITY = 4000; // dp/sec
+    private static final float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
+    private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
+    private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
+    private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
 
     static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
@@ -61,12 +58,12 @@
     private float mMinSwipeProgress = 0f;
     private float mMaxSwipeProgress = 1f;
 
-    private FlingAnimationUtils mFlingAnimationUtils;
+    private final FlingAnimationUtils mFlingAnimationUtils;
     private float mPagingTouchSlop;
-    private Callback mCallback;
-    private Handler mHandler;
-    private int mSwipeDirection;
-    private VelocityTracker mVelocityTracker;
+    private final Callback mCallback;
+    private final Handler mHandler;
+    private final int mSwipeDirection;
+    private final VelocityTracker mVelocityTracker;
 
     private float mInitialTouchPos;
     private float mPerpendicularInitialTouchPos;
@@ -80,14 +77,14 @@
     private boolean mLongPressSent;
     private LongPressListener mLongPressListener;
     private Runnable mWatchLongPress;
-    private long mLongPressTimeout;
+    private final long mLongPressTimeout;
 
     final private int[] mTmpPos = new int[2];
-    private int mFalsingThreshold;
+    private final int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
     private boolean mDisableHwLayers;
 
-    private HashMap<View, Animator> mDismissPendingMap = new HashMap<>();
+    private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>();
 
     public SwipeHelper(int swipeDirection, Callback callback, Context context) {
         mCallback = callback;
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index c5daca0..77375fa 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -459,7 +459,9 @@
         x += mIsLeftAligned ? xOffset : -xOffset;
 
         // Open above icon if there is room.
-        int iconHeight = icon.getIcon().getBounds().height();
+        int iconHeight = icon.getIcon() != null
+                ? icon.getIcon().getBounds().height()
+                : icon.getHeight();
         int y = mTempRect.top + icon.getPaddingTop() - height;
         mIsAboveIcon = y > dragLayer.getTop() + insets.top;
         if (!mIsAboveIcon) {
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
index 59a0386..8785a56 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
@@ -104,8 +104,8 @@
 
     @Override
     public boolean onLongClick(View v) {
-        // Return early if this is not initiated from a touch or not the correct view
-        if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
+        // Return early if not the correct view
+        if (!(v.getParent() instanceof DeepShortcutView)) return false;
         // Return early if global dragging is not enabled
         if (!mLauncher.isDraggingEnabled()) return false;
         // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
new file mode 100644
index 0000000..9c8457a
--- /dev/null
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.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.views;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Region;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+
+/**
+ * Extension of {@link BubbleTextView} which draws two shadows on the text (ambient and key shadows}
+ */
+public class DoubleShadowBubbleTextView extends BubbleTextView {
+
+    private final ShadowInfo mShadowInfo;
+
+    public DoubleShadowBubbleTextView(Context context) {
+        this(context, null);
+    }
+
+    public DoubleShadowBubbleTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DoubleShadowBubbleTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        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) {
+            getPaint().clearShadowLayer();
+            super.onDraw(canvas);
+            return;
+        }
+
+        // We enhance the shadow by drawing the shadow twice
+        getPaint().setShadowLayer(
+                mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
+
+        drawWithoutBadge(canvas);
+        canvas.save(Canvas.CLIP_SAVE_FLAG);
+        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
+                getScrollX() + getWidth(),
+                getScrollY() + getHeight(), Region.Op.INTERSECT);
+
+        getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f,
+                mShadowInfo.keyShadowOffset, mShadowInfo.keyShadowColor);
+        drawWithoutBadge(canvas);
+        canvas.restore();
+
+        drawBadgeIfNecessary(canvas);
+    }
+
+    public static class ShadowInfo {
+        public final float ambientShadowBlur;
+        public final int ambientShadowColor;
+
+        public final float keyShadowBlur;
+        public final float keyShadowOffset;
+        public final int keyShadowColor;
+
+        public ShadowInfo(Context c, AttributeSet attrs, int defStyle) {
+
+            TypedArray a = c.obtainStyledAttributes(
+                    attrs, R.styleable.ShadowInfo, defStyle, 0);
+
+            ambientShadowBlur = a.getDimension(R.styleable.ShadowInfo_ambientShadowBlur, 0);
+            ambientShadowColor = a.getColor(R.styleable.ShadowInfo_ambientShadowColor, 0);
+
+            keyShadowBlur = a.getDimension(R.styleable.ShadowInfo_keyShadowBlur, 0);
+            keyShadowOffset = a.getDimension(R.styleable.ShadowInfo_keyShadowOffset, 0);
+            keyShadowColor = a.getColor(R.styleable.ShadowInfo_keyShadowColor, 0);
+            a.recycle();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 6628971..19be28d 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -116,7 +116,7 @@
         } else {
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
             Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
-            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, Build.VERSION_CODES.O);
+            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0);
             mAddInfo.spanX = mAddInfo.spanY = 1;
             scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
 
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 4055c3f..fff3472 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -21,13 +21,13 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
@@ -80,7 +80,8 @@
         setWillNotDraw(false);
         mLauncher = Launcher.getLauncher(context);
         mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
-        mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
+        mFastOutSlowInInterpolator =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
         mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator();
         mInsets = new Rect();
         mVerticalPullDetector = new VerticalPullDetector(context);
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 4e296bf..14a9d17 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -139,8 +139,6 @@
         if (LOGD) {
             Log.d(TAG, String.format("onLongClick [v=%s]", v));
         }
-        // Return early if this is not initiated from a touch
-        if (!v.isInTouchMode()) return false;
         // When we  are in transition, disregard long clicks
         if (mLauncher.getWorkspace().isSwitchingState()) return false;
         // Return if global dragging is not enabled
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
index 6ca2121..1d6c18b 100644
--- a/tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -1,6 +1,7 @@
 package com.android.launcher3.util;
 
 import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 
 import com.android.launcher3.LauncherProvider;
@@ -43,5 +44,8 @@
 
         @Override
         protected void onEmptyDbCreated() { }
+
+        @Override
+        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
     }
 }
\ No newline at end of file