Merge "Polish for all apps physics." into ub-launcher3-dorval-polish
diff --git a/res/drawable-hdpi/all_apps_alpha_mask.png b/res/drawable-hdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..01e9e56
--- /dev/null
+++ b/res/drawable-hdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-mdpi/all_apps_alpha_mask.png b/res/drawable-mdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..f24e71d
--- /dev/null
+++ b/res/drawable-mdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-xhdpi/all_apps_alpha_mask.png b/res/drawable-xhdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..f479433
--- /dev/null
+++ b/res/drawable-xhdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-xxhdpi/all_apps_alpha_mask.png b/res/drawable-xxhdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..d5facd7
--- /dev/null
+++ b/res/drawable-xxhdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable/all_apps_alpha_mask.png b/res/drawable-xxxhdpi/all_apps_alpha_mask.png
old mode 100644
new mode 100755
similarity index 100%
rename from res/drawable/all_apps_alpha_mask.png
rename to res/drawable-xxxhdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable/gutter_horizontal.xml b/res/drawable/gutter_horizontal.xml
new file mode 100644
index 0000000..95b03df
--- /dev/null
+++ b/res/drawable/gutter_horizontal.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This "gutter" has a shadow at the top and a subtler shadow on the bottom. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient android:type="linear"
+              android:angle="-90"
+              android:startColor="#ffE0E0E0"
+              android:centerColor="#ffffffff"
+              android:endColor="#ffF5F5F5" />
+</shape>
\ No newline at end of file
diff --git a/res/layout/notification.xml b/res/layout/notification.xml
index 085dfa9..4a02aa1 100644
--- a/res/layout/notification.xml
+++ b/res/layout/notification.xml
@@ -28,6 +28,13 @@
         android:orientation="vertical"
         android:clipChildren="false">
 
+        <View
+            android:id="@+id/gutter_top"
+            android:layout_width="match_parent"
+            android:layout_height="4dp"
+            android:theme="@style/PopupGutter"
+            android:visibility="gone" />
+
         <FrameLayout
             android:id="@+id/header"
             android:layout_width="match_parent"
@@ -35,22 +42,23 @@
             android:paddingStart="@dimen/notification_padding_start"
             android:paddingEnd="@dimen/notification_padding_end"
             android:background="?attr/popupColorPrimary"
-            android:elevation="@dimen/notification_elevation">
+            android:elevation="@dimen/notification_elevation"
+            android:layout_below="@id/gutter_top" >
             <TextView
                 android:id="@+id/notification_text"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:layout_gravity="start"
-                android:gravity="center_vertical"
+                android:gravity="bottom"
                 android:text="@string/notifications_header"
                 android:textSize="@dimen/notification_header_text_size"
-                android:textColor="?android:attr/textColorSecondary" />
+                android:textColor="?android:attr/textColorPrimary" />
             <TextView
                 android:id="@+id/notification_count"
                 android:layout_width="@dimen/notification_icon_size"
                 android:layout_height="match_parent"
                 android:layout_gravity="end"
-                android:gravity="center"
+                android:gravity="bottom|center_horizontal"
                 android:textSize="@dimen/notification_header_count_text_size"
                 android:fontFamily="sans-serif-medium"
                 android:textColor="?android:attr/textColorPrimary" />
@@ -76,6 +84,14 @@
             android:layout_height="@dimen/notification_footer_height"
             android:layout_below="@id/divider" />
 
+        <View
+            android:id="@+id/gutter_bottom"
+            android:layout_width="match_parent"
+            android:layout_height="4dp"
+            android:theme="@style/PopupGutter"
+            android:visibility="gone"
+            android:layout_below="@id/footer" />
+
     </RelativeLayout>
 
 </com.android.launcher3.notification.NotificationItemView>
diff --git a/res/layout/notification_main.xml b/res/layout/notification_main.xml
index 7a8cf6d..f681e8b 100644
--- a/res/layout/notification_main.xml
+++ b/res/layout/notification_main.xml
@@ -38,7 +38,7 @@
             android:layout_height="wrap_content"
             android:textAlignment="viewStart"
             android:fontFamily="sans-serif"
-            android:textSize="@dimen/notification_main_text_size"
+            android:textSize="@dimen/notification_main_title_size"
             android:textColor="?android:attr/textColorPrimary"
             android:lines="1"
             android:ellipsize="end" />
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 08073ce..0db0e61 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -27,8 +27,7 @@
     <!-- 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>
+    <dimen name="folder_preview_padding">5dp</dimen>
 
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a4dff71..980b714 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -213,6 +213,7 @@
     <dimen name="notification_footer_height">32dp</dimen>
     <dimen name="notification_header_text_size">13sp</dimen>
     <dimen name="notification_header_count_text_size">12sp</dimen>
+    <dimen name="notification_main_title_size">16sp</dimen>
     <dimen name="notification_main_text_size">14sp</dimen>
     <dimen name="notification_icon_size">24dp</dimen>
     <dimen name="notification_footer_icon_size">18dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d11b002..813fe8f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -140,6 +140,13 @@
     <style name="PopupItem">
         <item name="android:colorControlHighlight">?attr/popupColorTertiary</item>
     </style>
+    <style name="PopupGutter">
+        <item name="android:backgroundTintMode">multiply</item>
+        <item name="android:backgroundTint">?attr/popupColorSecondary</item>
+        <item name="android:background">@drawable/gutter_horizontal</item>
+        <item name="android:elevation">@dimen/notification_elevation</item>
+        <item name="android:outlineProvider">none</item>
+    </style>
 
     <!-- Drop targets -->
     <style name="DropTargetButtonBase">
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 6f2c897..aeb82b3 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.FolderIconPreviewVerifier;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.HolographicOutlineHelper;
@@ -438,6 +439,13 @@
         super.setTextColor(colors);
     }
 
+    public boolean shouldTextBeVisible() {
+        // Text should be visible everywhere but the hotseat.
+        Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
+        ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
+        return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+    }
+
     public void setTextVisibility(boolean visible) {
         if (visible) {
             super.setTextColor(mTextColor);
@@ -459,7 +467,8 @@
      * @param fadeIn Whether the text should fade in or fade out.
      */
     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
-        return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, fadeIn ? Color.alpha(mTextColor) : 0);
+        int toAlpha = shouldTextBeVisible() && fadeIn ? Color.alpha(mTextColor) : 0;
+        return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, toAlpha);
     }
 
     @Override
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index d99a30a..42b64ea 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -606,7 +606,7 @@
         // Hotseat icons - remove text
         if (child instanceof BubbleTextView) {
             BubbleTextView bubbleChild = (BubbleTextView) child;
-            bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
+            bubbleChild.setTextVisibility(bubbleChild.shouldTextBeVisible());
         }
 
         child.setScaleX(mChildScale);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index dcfb268..eef6510 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -344,7 +344,7 @@
         updateFolderCellSize(1f, dm, res);
 
         // Don't let the folder get too close to the edges of the screen.
-        int folderMargin = 4 * edgeMarginPx;
+        int folderMargin = edgeMarginPx;
 
         // Check if the icons fit within the available height.
         float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
@@ -497,7 +497,7 @@
             // In portrait, we want the pages spaced such that there is no
             // overhang of the previous / next page into the current page viewport.
             // We assume symmetrical padding in portrait mode.
-            return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left + 1);
+            return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left / 2 + 1);
         }
     }
 
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index a51ddd4..3cbc989 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -23,7 +23,6 @@
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
-
 import com.android.launcher3.util.Thunk;
 
 /*
@@ -33,10 +32,11 @@
  */
 public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
     implements ValueAnimator.AnimatorUpdateListener {
+    private static final String TAG = "FirstFrameAnimatorHlpr";
     private static final boolean DEBUG = false;
     private static final int MAX_DELAY = 1000;
     private static final int IDEAL_FRAME_DURATION = 16;
-    private View mTarget;
+    private final View mTarget;
     private long mStartFrame;
     private long mStartTime = -1;
     private boolean mHandlingOnAnimationUpdate;
@@ -77,7 +77,7 @@
                     sGlobalFrameCounter++;
                     if (DEBUG) {
                         long newTime = System.currentTimeMillis();
-                        Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+                        Log.d(TAG, "TICK " + (newTime - mTime));
                         mTime = newTime;
                     }
                 }
@@ -139,7 +139,7 @@
 
     public void print(ValueAnimator animation) {
         float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
-        Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+        Log.d(TAG, sGlobalFrameCounter +
               "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
               mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
     }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 383e6ef..5e06763 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -41,7 +41,6 @@
 import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
-
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
@@ -52,7 +51,6 @@
 import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.SQLiteCacheHelper;
 import com.android.launcher3.util.Thunk;
-
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -91,7 +89,7 @@
 
     private final Context mContext;
     private final PackageManager mPackageManager;
-    private IconProvider mIconProvider;
+    private final IconProvider mIconProvider;
     @Thunk final UserManagerCompat mUserManager;
     private final LauncherAppsCompat mLauncherApps;
     private final HashMap<ComponentKey, CacheEntry> mCache =
@@ -193,7 +191,7 @@
      * Remove any records for the supplied package name from memory.
      */
     private void removeFromMemCacheLocked(String packageName, UserHandle user) {
-        HashSet<ComponentKey> forDeletion = new HashSet<ComponentKey>();
+        HashSet<ComponentKey> forDeletion = new HashSet<>();
         for (ComponentKey key: mCache.keySet()) {
             if (key.componentName.getPackageName().equals(packageName)
                     && key.user.equals(user)) {
@@ -219,7 +217,6 @@
             }
         } catch (NameNotFoundException e) {
             Log.d(TAG, "Package not found", e);
-            return;
         }
     }
 
@@ -264,7 +261,7 @@
             Set<String> ignorePackages) {
         long userSerial = mUserManager.getSerialNumberForUser(user);
         PackageManager pm = mContext.getPackageManager();
-        HashMap<String, PackageInfo> pkgInfoMap = new HashMap<String, PackageInfo>();
+        HashMap<String, PackageInfo> pkgInfoMap = new HashMap<>();
         for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
             pkgInfoMap.put(info.packageName, info);
         }
@@ -274,7 +271,7 @@
             componentMap.put(app.getComponentName(), app);
         }
 
-        HashSet<Integer> itemsToRemove = new HashSet<Integer>();
+        HashSet<Integer> itemsToRemove = new HashSet<>();
         Stack<LauncherActivityInfo> appsToUpdate = new Stack<>();
 
         Cursor c = null;
@@ -704,7 +701,7 @@
         private final HashMap<String, PackageInfo> mPkgInfoMap;
         private final Stack<LauncherActivityInfo> mAppsToAdd;
         private final Stack<LauncherActivityInfo> mAppsToUpdate;
-        private final HashSet<String> mUpdatedPackages = new HashSet<String>();
+        private final HashSet<String> mUpdatedPackages = new HashSet<>();
 
         @Thunk SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
                 Stack<LauncherActivityInfo> appsToAdd,
@@ -753,7 +750,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 16;
+        private final static int DB_VERSION = 17;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 255677a..ed225c9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -49,7 +48,6 @@
 
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.LauncherEdgeEffect;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
@@ -70,6 +68,11 @@
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
     protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
 
+    // Overscroll constants
+    private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
+    private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
+    private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
     private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
     // The page is moved more than halfway, automatically move to the next page on touch up.
     private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
@@ -145,6 +148,13 @@
 
     protected boolean mWasInOverscroll = false;
 
+    // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
+    // it is equal to the scaled overscroll position. We use a separate value so as to prevent
+    // the screens from continuing to translate beyond the normal bounds.
+    protected int mOverScrollX;
+
+    protected int mUnboundedScrollX;
+
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
     protected PageIndicator mPageIndicator;
@@ -184,10 +194,6 @@
     protected final Rect mInsets = new Rect();
     protected final boolean mIsRtl;
 
-    // Edge effect
-    private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
-    private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
-
     public PagedView(Context context) {
         this(context, null);
     }
@@ -229,8 +235,6 @@
         setWillNotDraw(false);
 
         int edgeEffectColor = Themes.getAttrColor(getContext(), android.R.attr.colorEdgeEffect);
-        mEdgeGlowLeft.setColor(edgeEffectColor);
-        mEdgeGlowRight.setColor(edgeEffectColor);
     }
 
     protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -476,7 +480,7 @@
     }
 
     protected int getUnboundedScrollX() {
-        return getScrollX();
+        return mUnboundedScrollX;
     }
 
     @Override
@@ -499,6 +503,8 @@
             x = Math.max(x, mFreeScrollMinScrollX);
         }
 
+        mUnboundedScrollX = x;
+
         boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
         boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
         if (isXBeforeFirstPage) {
@@ -526,6 +532,7 @@
                 overScroll(0);
                 mWasInOverscroll = false;
             }
+            mOverScrollX = x;
             super.scrollTo(x, y);
         }
 
@@ -565,7 +572,8 @@
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
             if (getUnboundedScrollX() != mScroller.getCurrX()
-                    || getScrollY() != mScroller.getCurrY()) {
+                    || getScrollY() != mScroller.getCurrY()
+                    || mOverScrollX != mScroller.getCurrX()) {
                 float scaleX = mFreeScroll ? getScaleX() : 1f;
                 int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX));
                 scrollTo(scrollX, mScroller.getCurrY());
@@ -973,47 +981,6 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (getPageCount() > 0) {
-            if (!mEdgeGlowLeft.isFinished()) {
-                final int restoreCount = canvas.save();
-                Rect display = mViewport;
-                canvas.translate(display.left, display.top);
-                canvas.rotate(270);
-
-                getEdgeVerticalPosition(sTmpIntPoint);
-                canvas.translate(display.top - sTmpIntPoint[1], 0);
-                mEdgeGlowLeft.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
-                if (mEdgeGlowLeft.draw(canvas)) {
-                    postInvalidateOnAnimation();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mEdgeGlowRight.isFinished()) {
-                final int restoreCount = canvas.save();
-                Rect display = mViewport;
-                canvas.translate(display.left + mPageScrolls[mIsRtl ? 0 : (getPageCount() - 1)], display.top);
-                canvas.rotate(90);
-
-                getEdgeVerticalPosition(sTmpIntPoint);
-
-                canvas.translate(sTmpIntPoint[0] - display.top, -display.width());
-                mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
-                if (mEdgeGlowRight.draw(canvas)) {
-                    postInvalidateOnAnimation();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-        }
-    }
-
-    /**
-     * Returns the top and bottom position for the edge effect.
-     */
-    protected abstract void getEdgeVerticalPosition(int[] pos);
-
-    @Override
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
         int page = indexToPage(indexOfChild(child));
         if (page != mCurrentPage || !mScroller.isFinished()) {
@@ -1335,6 +1302,29 @@
         }
     }
 
+    // This curve determines how the effect of scrolling over the limits of the page dimishes
+    // as the user pulls further and further from the bounds
+    private float overScrollInfluenceCurve(float f) {
+        f -= 1.0f;
+        return f * f * f + 1.0f;
+    }
+
+    protected float acceleratedOverFactor(float amount) {
+        int screenSize = getViewportWidth();
+
+        // We want to reach the max over scroll effect when the user has
+        // over scrolled half the size of the screen
+        float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
+
+        if (Float.compare(f, 0f) == 0) return 0;
+
+        // Clamp this factor, f, to -1 < f < 1
+        if (Math.abs(f) >= 1) {
+            f /= Math.abs(f);
+        }
+        return f;
+    }
+
     // While layout transitions are occurring, a child's position may stray from its baseline
     // position. This method returns the magnitude of this stray at any given time.
     public int getLayoutTransitionOffsetForPage(int index) {
@@ -1356,13 +1346,25 @@
 
     protected void dampedOverScroll(float amount) {
         int screenSize = getViewportWidth();
+
         float f = (amount / screenSize);
-        if (f < 0) {
-            mEdgeGlowLeft.onPull(-f);
-        } else if (f > 0) {
-            mEdgeGlowRight.onPull(f);
+
+        if (Float.compare(f, 0f) == 0) return;
+
+        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+        // Clamp this factor, f, to -1 < f < 1
+        if (Math.abs(f) >= 1) {
+            f /= Math.abs(f);
+        }
+
+        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
+        if (amount < 0) {
+            mOverScrollX = overScrollAmount;
+            super.scrollTo(mOverScrollX, getScrollY());
         } else {
-            return;
+            mOverScrollX = mMaxScrollX + overScrollAmount;
+            super.scrollTo(mOverScrollX, getScrollY());
         }
         invalidate();
     }
@@ -1371,6 +1373,14 @@
         dampedOverScroll(amount);
     }
 
+    protected float maxOverScroll() {
+        // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
+        // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
+        float f = 1.0f;
+        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+        return OVERSCROLL_DAMP_FACTOR * f;
+    }
+
     /**
      * return true if freescroll has been enabled, false otherwise
      */
@@ -1715,8 +1725,6 @@
         mCancelTap = false;
         mTouchState = TOUCH_STATE_REST;
         mActivePointerId = INVALID_POINTER;
-        mEdgeGlowLeft.onRelease();
-        mEdgeGlowRight.onRelease();
     }
 
     /**
@@ -1830,7 +1838,18 @@
     }
 
     protected void snapToDestination() {
-        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
+    }
+
+    protected boolean isInOverScroll() {
+        return (mOverScrollX > mMaxScrollX || mOverScrollX < 0);
+    }
+
+    protected int getPageSnapDuration() {
+        if (isInOverScroll()) {
+            return OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION;
+        }
+        return PAGE_SNAP_ANIMATION_DURATION;
     }
 
     public static class ScrollInterpolator implements Interpolator {
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index ad1be7e..a65ea9b 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 = 8;
+        private static final int DB_VERSION = 9;
 
         private static final String TABLE_NAME = "shortcut_and_widget_previews";
         private static final String COLUMN_COMPONENT = "componentName";
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 767e332..b1db7e8 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -300,8 +300,7 @@
     boolean mScrollInteractionBegan;
     boolean mStartedSendingScrollEvents;
     float mLastOverlayScroll = 0;
-    // Total over scrollX in the overlay direction.
-    private int mUnboundedScrollX;
+
     private boolean mForceDrawAdjacentPages = false;
     // Total over scrollX in the overlay direction.
     private float mOverlayTranslation;
@@ -1321,18 +1320,10 @@
         onOverlayScrollChanged(0);
     }
 
-    @Override
-    protected int getUnboundedScrollX() {
-        if (isScrollingOverlay()) {
-            return mUnboundedScrollX;
-        }
-
-        return super.getUnboundedScrollX();
-    }
 
     private boolean isScrollingOverlay() {
         return mLauncherOverlay != null &&
-                ((mIsRtl && mUnboundedScrollX > mMaxScrollX) || (!mIsRtl && mUnboundedScrollX < 0));
+                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
     }
 
     @Override
@@ -1352,12 +1343,6 @@
     }
 
     @Override
-    public void scrollTo(int x, int y) {
-        mUnboundedScrollX = x;
-        super.scrollTo(x, y);
-    }
-
-    @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
 
@@ -1530,13 +1515,6 @@
     }
 
     @Override
-    protected void getEdgeVerticalPosition(int[] pos) {
-        View child = getChildAt(getPageCount() - 1);
-        pos[0] = child.getTop();
-        pos[1] = child.getBottom();
-    }
-
-    @Override
     protected void notifyPageSwitchListener() {
         super.notifyPageSwitchListener();
 
@@ -3814,7 +3792,7 @@
                 ItemInfo info = (ItemInfo) item.getTag();
                 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
                     FolderIcon folder = (FolderIcon) item;
-                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                    ArrayList<View> folderChildren = folder.getFolder().getItemsInRankOrder();
                     // map over all the children in the folder
                     final int childCount = folderChildren.size();
                     for (int childIdx = 0; childIdx < childCount; childIdx++) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3433533..6cd086b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -369,7 +369,7 @@
 
         Folder folder = Folder.getOpen(mLauncher);
         if (folder != null) {
-            if (!folder.getItemsInReadingOrder().contains(item)) {
+            if (!folder.getItemsInRankOrder().contains(item)) {
                 folder.close(true);
                 folder = null;
             }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 1054a56..ba4fbe0 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -481,19 +481,17 @@
          *     5 6 7 8 9
          */
         private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
-            int appPosition = position;
-            int numDividerViews = 1 + (numPredictedApps == 0 ? 0 : 1);
-
-            int allAppsStartAt = numDividerViews + numPredictedApps;
-            if (numDividerViews == 1 || position < allAppsStartAt) {
-                appPosition -= 1;
-            } else {
-                // We cannot assume that the predicted row will always be full.
-                int numPredictedAppsOffset = appsPerRow - numPredictedApps;
-                appPosition = position + numPredictedAppsOffset - numDividerViews;
+            if (position < numPredictedApps) {
+                // Predicted apps are first in the adapter.
+                return position;
             }
 
-            return appPosition;
+            // There is at most 1 divider view between the predicted apps and the alphabetical apps.
+            int numDividerViews = numPredictedApps == 0 ? 0 : 1;
+
+            // This offset takes into consideration an incomplete row of predicted apps.
+            int numPredictedAppsOffset = appsPerRow - numPredictedApps;
+            return position + numPredictedAppsOffset - numDividerViews;
         }
 
         /**
diff --git a/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
index 7c5fa1c..d01b26c 100644
--- a/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
@@ -56,7 +56,7 @@
 
     @Override
     public boolean shouldRemoveElevationDuringAnimation() {
-        return true;
+        return false;
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 3c7c698..f68b394 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -133,7 +133,7 @@
     private final Alarm mOnScrollHintAlarm = new Alarm();
     @Thunk final Alarm mScrollPauseAlarm = new Alarm();
 
-    @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
+    @Thunk final ArrayList<View> mItemsInRankOrder = new ArrayList<>();
 
     private AnimatorSet mCurrentAnimator;
 
@@ -284,7 +284,7 @@
         if (tag instanceof ShortcutInfo) {
             ShortcutInfo item = (ShortcutInfo) tag;
 
-            mEmptyCellRank = item.rank;
+            mEmptyCellRank = mContent.getReadingOrderPosForRank(item.rank);
             mCurrentDragView = v;
 
             mDragController.addDragListener(this);
@@ -705,7 +705,7 @@
     }
 
     public void beginExternalDrag() {
-        mEmptyCellRank = mContent.allocateRankForNewItem();
+        mEmptyCellRank = mContent.getReadingOrderPosForRank(mContent.allocateRankForNewItem());
         mIsExternalDrag = true;
         mDragInProgress = true;
 
@@ -997,7 +997,7 @@
             ShortcutInfo info = (ShortcutInfo) d.dragInfo;
             View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
                     ? mCurrentDragView : mContent.createNewView(info);
-            ArrayList<View> views = getItemsInReadingOrder();
+            ArrayList<View> views = getItemsInRankOrder();
             views.add(info.rank, icon);
             mContent.arrangeChildren(views, views.size());
             mItemsInvalidated = true;
@@ -1072,7 +1072,7 @@
     }
 
     private void updateItemLocationsInDatabaseBatch() {
-        ArrayList<View> list = getItemsInReadingOrder();
+        ArrayList<View> list = getItemsInRankOrder();
         ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
         for (int i = 0; i < list.size(); i++) {
             View v = list.get(i);
@@ -1231,7 +1231,7 @@
      * otherwise it is ignored.
      */
     public void rearrangeChildren(int itemCount) {
-        ArrayList<View> views = getItemsInReadingOrder();
+        ArrayList<View> views = getItemsInRankOrder();
         mContent.arrangeChildren(views, Math.max(itemCount, views.size()));
         mItemsInvalidated = true;
     }
@@ -1376,24 +1376,20 @@
 
             View currentDragView;
             if (mIsExternalDrag) {
-                currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
-
                 // Actually move the item in the database if it was an external drag. Call this
                 // before creating the view, so that ShortcutInfo is updated appropriately.
-                mLauncher.getModelWriter().addOrMoveItemInDatabase(
-                        si, mInfo.id, 0, si.cellX, si.cellY);
-
-                // We only need to update the locations if it doesn't get handled in
-                // #onDropCompleted.
-                if (d.dragSource != this) {
-                    updateItemLocationsInDatabaseBatch();
-                }
-                mIsExternalDrag = false;
-            } else {
-                currentDragView = mCurrentDragView;
-                mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+                mLauncher.getModelWriter().addOrMoveItemInDatabase(si, mInfo.id, 0, si.cellX, si.cellY);
             }
 
+            currentDragView = mIsExternalDrag
+                    ? mContent.createNewView(si)
+                    : mCurrentDragView;
+            mIsExternalDrag = false;
+
+            // Note: addViewForRankDuringDragAndDrop handles rearranging the children.
+            mContent.addViewForRankDuringDragAndDrop(currentDragView, si, mEmptyCellRank);
+            mItemsInvalidated = true;
+
             if (d.dragView.hasDrawn()) {
                 // Temporarily reset the scale such that the animation target gets calculated
                 // correctly.
@@ -1410,9 +1406,6 @@
                 currentDragView.setVisibility(VISIBLE);
             }
 
-            mItemsInvalidated = true;
-            rearrangeChildren();
-
             // Temporarily suppress the listener, as we did all the work already here.
             try (SuppressInfoChanges s = new SuppressInfoChanges()) {
                 mInfo.add(si, false);
@@ -1450,7 +1443,7 @@
         mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
                 item.cellY);
 
-        ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
+        ArrayList<View> items = new ArrayList<>(getItemsInRankOrder());
         items.add(rank, view);
         mContent.arrangeChildren(items, items.size());
         mItemsInvalidated = true;
@@ -1497,24 +1490,34 @@
     public void onTitleChanged(CharSequence title) {
     }
 
-    public ArrayList<View> getItemsInReadingOrder() {
+    public ArrayList<View> getItemsInRankOrder() {
         if (mItemsInvalidated) {
-            mItemsInReadingOrder.clear();
-            mContent.iterateOverItems(new ItemOperator() {
+            mItemsInRankOrder.clear();
+            mItemsInRankOrder.addAll(getItemsInReadingOrder());
+            mItemsInRankOrder.sort(VIEW_RANK_COMPARATOR);
 
-                @Override
-                public boolean evaluate(ItemInfo info, View view) {
-                    mItemsInReadingOrder.add(view);
-                    return false;
-                }
-            });
             mItemsInvalidated = false;
         }
-        return mItemsInReadingOrder;
+        return mItemsInRankOrder;
+    }
+
+    /**
+     * This is an expensive call. Consider using {@link #getItemsInRankOrder()} instead.
+     */
+    public ArrayList<View> getItemsInReadingOrder() {
+        final ArrayList<View> itemsInReadingOrder = new ArrayList<>();
+        mContent.iterateOverItems(new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View view) {
+                itemsInReadingOrder.add(view);
+                return false;
+            }
+        });
+        return itemsInReadingOrder;
     }
 
     public List<BubbleTextView> getItemsOnCurrentPage() {
-        ArrayList<View> allItems = getItemsInReadingOrder();
+        ArrayList<View> allItems = getItemsInRankOrder();
         int currentPage = mContent.getCurrentPage();
         int lastPage = mContent.getPageCount() - 1;
         int totalItemsInFolder = allItems.size();
@@ -1622,6 +1625,13 @@
         }
     };
 
+    public static final Comparator<View> VIEW_RANK_COMPARATOR = new Comparator<View>() {
+        @Override
+        public int compare(View lhs, View rhs) {
+            return ITEM_POS_COMPARATOR.compare((ItemInfo) lhs.getTag(), (ItemInfo) rhs.getTag());
+        }
+    };
+
     /**
      * Temporary resource held while we don't want to handle info changes
      */
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 3648c60..80eb74d 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -198,8 +198,14 @@
         play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
         play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
         play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
-        play(a, new RoundedRectRevealOutlineProvider(initialRadius, finalRadius, startRect,
-                endRect).createRevealAnimator(mFolder, !mIsOpening));
+        RoundedRectRevealOutlineProvider outlineProvider = new RoundedRectRevealOutlineProvider(
+                initialRadius, finalRadius, startRect, endRect) {
+            @Override
+            public boolean shouldRemoveElevationDuringAnimation() {
+                return true;
+            }
+        };
+        play(a, outlineProvider.createRevealAnimator(mFolder, !mIsOpening));
 
         // Animate the elevation midway so that the shadow is not noticeable in the background.
         int midDuration = mDuration / 2;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 1cc285e..3a0e71f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -575,7 +575,7 @@
         mPreviewVerifier.setFolderInfo(mFolder.getInfo());
 
         List<BubbleTextView> itemsToDisplay = new ArrayList<>();
-        List<View> allItems = mFolder.getItemsInReadingOrder();
+        List<View> allItems = mFolder.getItemsInRankOrder();
         int numItems = allItems.size();
         for (int rank = 0; rank < numItems; ++rank) {
             if (mPreviewVerifier.isItemInPreview(rank)) {
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
index de962b0..d0d8e79 100644
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -25,40 +25,15 @@
  */
 public class FolderIconPreviewVerifier {
 
-    private final int mMaxGridCountX;
-    private final int mMaxGridCountY;
-    private final int mMaxItemsPerPage;
-    private final int[] mGridSize = new int[2];
-
-    private int mGridCountX;
-    private boolean mDisplayingUpperLeftQuadrant = false;
-
     public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
-        mMaxGridCountX = profile.numFolderColumns;
-        mMaxGridCountY = profile.numFolderRows;
-        mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
+        // b/37570804
     }
 
     public void setFolderInfo(FolderInfo info) {
-        int numItemsInFolder = info.contents.size();
-        mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
-                && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON
-                && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW;
-
-        if (mDisplayingUpperLeftQuadrant) {
-            FolderPagedView.calculateGridSize(info.contents.size(), 0, 0, mMaxGridCountX,
-                    mMaxGridCountY, mMaxItemsPerPage, mGridSize);
-            mGridCountX = mGridSize[0];
-        }
+        // b/37570804
     }
 
     public boolean isItemInPreview(int rank) {
-        if (mDisplayingUpperLeftQuadrant) {
-            // Returns true iff the icon is in the 2x2 upper left quadrant of the Folder.
-            int col = rank % mGridCountX;
-            int row = rank / mGridCountX;
-            return col < 2 && row < 2;
-        }
         return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
     }
 }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index d0ac9f4..21631fa 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -201,18 +201,29 @@
     }
 
     public void allocateSpaceForRank(int rank) {
-        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
+        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInRankOrder());
         views.add(rank, null);
         arrangeChildren(views, views.size(), false);
     }
 
+    private ArrayList<View> createListWithViewAtPos(ArrayList<View> list, View v, int position) {
+        ArrayList<View> newList = new ArrayList<>(list.size() + 1);
+        newList.addAll(list);
+        newList.add(position, v);
+        return newList;
+    }
+
     /**
-     * Create space for a new item at the end, and returns the rank for that item.
+     * Create space for a new item and returns the rank for that item.
      * Also sets the current page to the last page.
      */
     public int allocateRankForNewItem() {
-        int rank = getItemCount();
-        allocateSpaceForRank(rank);
+        ArrayList<View> rankOrder = mFolder.getItemsInRankOrder();
+        int rank = rankOrder.size();
+
+        ArrayList<View> views = createListWithViewAtPos(rankOrder, null, rank);
+        arrangeChildren(views, views.size(), false);
+
         setCurrentPage(rank / mMaxItemsPerPage);
         return rank;
     }
@@ -229,20 +240,59 @@
      * related attributes. It assumes that {@param item} is already attached to the view.
      */
     public void addViewForRank(View view, ShortcutInfo item, int rank) {
-        int pagePos = rank % mMaxItemsPerPage;
-        int pageNo = rank / mMaxItemsPerPage;
+        updateShortcutInfoWithRank(item, rank);
 
-        item.rank = rank;
-        item.cellX = pagePos % mGridCountX;
-        item.cellY = pagePos / mGridCountX;
+        ArrayList<View> views = createListWithViewAtPos(mFolder.getItemsInRankOrder(), view, rank);
+        arrangeChildren(views, views.size(), false);
+    }
+
+    /**
+     * Similar to {@link #addViewForRank(View, ShortcutInfo, int)}}, but specific to real time
+     * reorder.
+     *
+     * The difference here is that during real time reorder, we are moving the Views in a contained
+     * order.
+     */
+    public void addViewForRankDuringReorder(View view, ShortcutInfo item, int rank) {
+        updateShortcutInfoWithRank(item, rank);
 
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
         lp.cellX = item.cellX;
         lp.cellY = item.cellY;
+
+        int pageNo = rank / mMaxItemsPerPage;
         getPageAt(pageNo).addViewToCellLayout(
                 view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
     }
 
+    /**
+     * Similar to {@link #addViewForRank(View, ShortcutInfo, int)}, but specific to drag and drop.
+     *
+     * The difference is that we handle the drag and drop by adjusting the reading order of the
+     * children, rather than based on their rank.
+     */
+    public void addViewForRankDuringDragAndDrop(View view, ShortcutInfo item, int readingRank) {
+        updateShortcutInfoWithRank(item, readingRank);
+
+        ArrayList<View> views = createListWithViewAtPos(mFolder.getItemsInReadingOrder(), view,
+                readingRank);
+
+        int numItems = views.size();
+        ArrayList<View> rankOrder = new ArrayList<>(numItems);
+        for (int i = 0; i < numItems; ++i) {
+            rankOrder.add(views.get(getReadingOrderPosForRank(i)));
+        }
+
+        arrangeChildren(rankOrder, numItems, false);
+    }
+
+    private void updateShortcutInfoWithRank(ShortcutInfo info, int rank) {
+        info.rank = rank;
+        getCellXYPositionForRank(rank, sTmpArray);
+        info.cellX = sTmpArray[0];
+        info.cellY = sTmpArray[1];
+    }
+
     @SuppressLint("InflateParams")
     public View createNewView(ShortcutInfo item) {
         final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
@@ -310,18 +360,19 @@
      * It essentially removes all views from all the pages and then adds them again in appropriate
      * page.
      *
-     * @param list the ordered list of children.
+     * @param rankOrderedList the rank-ordered list of children.
      * @param itemCount if greater than the total children count, empty spaces are left
      * at the end, otherwise it is ignored.
      *
      */
-    public void arrangeChildren(ArrayList<View> list, int itemCount) {
-        arrangeChildren(list, itemCount, true);
+    public void arrangeChildren(ArrayList<View> rankOrderedList, int itemCount) {
+        arrangeChildren(rankOrderedList, itemCount, true);
     }
 
     @SuppressLint("RtlHardcoded")
-    private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
-        ArrayList<CellLayout> pages = new ArrayList<>();
+    private void arrangeChildren(ArrayList<View> rankOrderedList, int itemCount,
+            boolean saveChanges) {
+        ArrayList<CellLayout> pages = new ArrayList<CellLayout>();
         for (int i = 0; i < getChildCount(); i++) {
             CellLayout page = (CellLayout) getChildAt(i);
             page.removeAllViews();
@@ -339,7 +390,7 @@
                 Launcher.getLauncher(getContext()).getDeviceProfile().inv);
         rank = 0;
         for (int i = 0; i < itemCount; i++) {
-            View v = list.size() > i ? list.get(i) : null;
+            View v = rankOrderedList.size() > i ? rankOrderedList.get(i) : null;
             if (currentPage == null || position >= mMaxItemsPerPage) {
                 // Next page
                 if (pageItr.hasNext()) {
@@ -352,8 +403,10 @@
 
             if (v != null) {
                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
-                newX = position % mGridCountX;
-                newY = position / mGridCountX;
+                getCellXYPositionForRank(rank, sTmpArray);
+                newX = sTmpArray[0];
+                newY = sTmpArray[1];
+
                 ItemInfo info = (ItemInfo) v.getTag();
                 if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
                     info.cellX = newX;
@@ -651,7 +704,7 @@
             if (v != null) {
                 if (pageToAnimate != p) {
                     page.removeView(v);
-                    addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart);
+                    addViewForRankDuringReorder(v, (ShortcutInfo) v.getTag(), moveStart);
                 } else {
                     // Do a fake animation before removing it.
                     final int newRank = moveStart;
@@ -664,14 +717,14 @@
                             mPendingAnimations.remove(v);
                             v.setTranslationX(oldTranslateX);
                             ((CellLayout) v.getParent().getParent()).removeView(v);
-                            addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
+                            addViewForRankDuringReorder(v, (ShortcutInfo) v.getTag(), newRank);
                         }
                     };
                     v.animate()
-                        .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
-                        .setDuration(REORDER_ANIMATION_DURATION)
-                        .setStartDelay(0)
-                        .withEndAction(endAction);
+                            .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
+                            .setDuration(REORDER_ANIMATION_DURATION)
+                            .setStartDelay(0)
+                            .withEndAction(endAction);
                     mPendingAnimations.put(v, endAction);
                 }
             }
@@ -702,9 +755,87 @@
         return mMaxItemsPerPage;
     }
 
-    @Override
-    protected void getEdgeVerticalPosition(int[] pos) {
-        pos[0] = 0;
-        pos[1] = getViewportHeight();
+    /**
+     * Returns the reading order position for a given rank.
+     *
+     * ie. For the permutation below, rank 0 returns 0, rank 1 returns 1, rank 4 returns 2,
+     *                                rank 2 returns 3, rank 3 returns 4, rank 5 returns 5.
+     *
+     *     R0 R1 R4
+     *     R2 R3 R5
+     */
+    public int getReadingOrderPosForRank(int rank) {
+        if (rank >= mMaxItemsPerPage) {
+            return rank;
+        }
+
+        getCellXYPositionForRank(rank, sTmpArray);
+        return sTmpArray[0] + (mGridCountX * sTmpArray[1]);
+    }
+
+    /**
+     * Returns the cell XY position for a Folder item with the given rank.
+     */
+    public void getCellXYPositionForRank(int rank, int[] outXY) {
+        boolean onFirstPage = rank < mMaxItemsPerPage;
+
+        if (onFirstPage && mGridCountX == 3) {
+            outXY[0] = FolderPermutation.THREE_COLS[rank][0];
+            outXY[1] = FolderPermutation.THREE_COLS[rank][1];
+        } else if (onFirstPage && mGridCountX == 4) {
+            outXY[0] = FolderPermutation.FOUR_COLS[rank][0];
+            outXY[1] = FolderPermutation.FOUR_COLS[rank][1];
+        } else if (onFirstPage && mGridCountX == 5) {
+            outXY[0] = FolderPermutation.FIVE_COLS[rank][0];
+            outXY[1] = FolderPermutation.FIVE_COLS[rank][1];
+        } else {
+            outXY[0] = (rank % mMaxItemsPerPage) % mGridCountX;
+            outXY[1] = (rank % mMaxItemsPerPage) / mGridCountX;
+        }
+    }
+
+    /**
+     * Provides the mapping between a folder item's rank and its cell location, based on the
+     * number of columns.
+     *
+     * We use this mapping, rather than the regular reading order, to preserve the items in the
+     * upper left quadrant of the Folder. This allows a smooth transition between the FolderIcon
+     * and the opened Folder.
+     *
+     * TODO: We will replace these hard coded tables with an algorithm b/62986680
+     */
+    private static class FolderPermutation {
+        /**
+         *  R0 R1 R4
+         *  R2 R3 R5
+         *  R6 R7 R8
+         */
+        static final int[][] THREE_COLS = new int[][] {
+                {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {0, 2}, {1, 2}, {2, 2}
+        };
+
+        /**
+         * R0  R1  R4  R6
+         * R2  R3  R5  R7
+         * R8  R9  R10 R11
+         * R12 R13 R14 R15
+         */
+        static final int[][] FOUR_COLS = new int[][] {
+                {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {3, 1}, {0, 2}, {1, 2},
+                {2, 2}, {3, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}
+        };
+
+        /**
+         * R0  R1  R4  R6  R12
+         * R2  R3  R5  R7  R13
+         * R8  R9  R10 R11 R14
+         * R15 R16 R17 R18 R19
+         * R20 R21 R22 R23 R24
+         */
+        static final int[][] FIVE_COLS = new int[][] {
+                {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {3, 1}, {0, 2}, {1, 2},
+                {2, 2}, {3, 2}, {4, 0}, {4, 1}, {4, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3},
+                {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}
+        };
     }
 }
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 44ebbcd..eb6a6d5 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -88,7 +88,7 @@
     public boolean isClipping = true;
 
     // Drawing / animation configurations
-    private static final float ACCEPT_SCALE_FACTOR = 1.25f;
+    private static final float ACCEPT_SCALE_FACTOR = 1.20f;
     private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
 
     // Expressed on a scale from 0 to 255.
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1a2c04d..bc7da9b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -184,9 +184,13 @@
                 icon = LauncherIcons.createIconBitmap(
                         BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
             } catch (Exception e) {
+                Log.e(TAG, "Failed to load icon for info " + info, e);
                 return null;
             }
         }
+        if (icon == null) {
+            Log.e(TAG, "Failed to load icon for info " + info);
+        }
         return icon;
     }
 
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 0b08ef8..0cd5a4c 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -48,6 +48,7 @@
 
     private static final Rect sTempRect = new Rect();
 
+    private TextView mHeaderText;
     private TextView mHeaderCount;
     private NotificationMainView mMainView;
     private NotificationFooterLayout mFooter;
@@ -70,6 +71,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mHeaderText = (TextView) findViewById(R.id.notification_text);
         mHeaderCount = (TextView) findViewById(R.id.notification_count);
         mMainView = (NotificationMainView) findViewById(R.id.main_view);
         mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
@@ -106,6 +108,7 @@
                         IconPalette.resolveContrastColor(getContext(), palette.dominantColor,
                                 Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
             }
+            mHeaderText.setTextColor(mNotificationHeaderTextColor);
             mHeaderCount.setTextColor(mNotificationHeaderTextColor);
         }
     }
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index baaa66a..8121fd5 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -127,8 +127,9 @@
         }
         sNotificationsChangedListener = listener;
 
-        if (sNotificationListenerInstance != null) {
-            sNotificationListenerInstance.onNotificationFullRefresh();
+        NotificationListener notificationListener = getInstanceIfConnected();
+        if (notificationListener != null) {
+            notificationListener.onNotificationFullRefresh();
         }
     }
 
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 77375fa..8107625 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -53,7 +53,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -285,9 +284,11 @@
                 int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
                 if (shouldUnroundTopCorners) {
                     roundedCorners &= ~ROUNDED_TOP_CORNERS;
+                    mNotificationItemView.findViewById(R.id.gutter_top).setVisibility(VISIBLE);
                 }
                 if (shouldUnroundBottomCorners) {
                     roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+                    mNotificationItemView.findViewById(R.id.gutter_bottom).setVisibility(VISIBLE);
                 }
                 int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary);
                 mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners);
@@ -308,9 +309,15 @@
                 }
                 if (itemTypeToPopulate != PopupPopulator.Item.SYSTEM_SHORTCUT_ICON
                         && numNotifications > 0) {
+                    int prevHeight = item.getLayoutParams().height;
                     // Condense shortcuts height when there are notifications.
                     item.getLayoutParams().height = res.getDimensionPixelSize(
                             R.dimen.bg_popup_item_condensed_height);
+                    if (item instanceof DeepShortcutView) {
+                        float iconScale = (float) item.getLayoutParams().height / prevHeight;
+                        ((DeepShortcutView) item).getIconView().setScaleX(iconScale);
+                        ((DeepShortcutView) item).getIconView().setScaleY(iconScale);
+                    }
                 }
                 mShortcutsItemView.addShortcutView(item, itemTypeToPopulate);
                 if (shouldUnroundBottomCorners) {
@@ -320,8 +327,7 @@
                 addView(item);
             }
         }
-        int backgroundColor = Themes.getAttrColor(mLauncher, mNotificationItemView == null
-                ? R.attr.popupColorPrimary : R.attr.popupColorSecondary);
+        int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
         mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
         if (numNotifications > 0) {
             mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
@@ -864,11 +870,14 @@
         }
         final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
                 radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
-        revealAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
+        long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+        revealAnim.setDuration(revealDuration);
         revealAnim.setInterpolator(new AccelerateDecelerateInterpolator());
 
         // Animate original icon's text back in.
-        closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
+        Animator fadeText = mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */);
+        fadeText.setDuration(revealDuration);
+        closeAnim.play(fadeText);
 
         closeAnim.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -897,9 +906,7 @@
         }
         mIsOpen = false;
         mDeferContainerRemoval = false;
-        boolean isInHotseat = ((ItemInfo) mOriginalIcon.getTag()).container
-                == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-        mOriginalIcon.setTextVisibility(!isInHotseat);
+        mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
         mOriginalIcon.forceHideBadge(false);
         mLauncher.getDragController().removeDragListener(this);
         mLauncher.getDragLayer().removeView(this);
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
index 8785a56..f6fffe0 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
@@ -280,6 +281,9 @@
             // Make sure the text and icon stay centered in the shortcut.
             animation.play(translateYFrom(shortcut.getBubbleText(), heightDiff / 2 * fromDir));
             animation.play(translateYFrom(shortcut.getIconView(), heightDiff / 2 * fromDir));
+            // Scale icons back up to full size.
+            animation.play(LauncherAnimUtils.ofPropertyValuesHolder(shortcut.getIconView(),
+                    new PropertyListBuilder().scale(1f).build()));
         }
         return animation;
     }
diff --git a/src/com/android/launcher3/util/LauncherEdgeEffect.java b/src/com/android/launcher3/util/LauncherEdgeEffect.java
deleted file mode 100644
index 3e3b255..0000000
--- a/src/com/android/launcher3/util/LauncherEdgeEffect.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-/**
- * This class differs from the framework {@link android.widget.EdgeEffect}:
- *   1) It does not use PorterDuffXfermode
- *   2) The width to radius factor is smaller (0.5 instead of 0.75)
- */
-public class LauncherEdgeEffect {
-
-    // Time it will take the effect to fully recede in ms
-    private static final int RECEDE_TIME = 600;
-
-    // Time it will take before a pulled glow begins receding in ms
-    private static final int PULL_TIME = 167;
-
-    // Time it will take in ms for a pulled glow to decay to partial strength before release
-    private static final int PULL_DECAY_TIME = 2000;
-
-    private static final float MAX_ALPHA = 0.5f;
-
-    private static final float MAX_GLOW_SCALE = 2.f;
-
-    private static final float PULL_GLOW_BEGIN = 0.f;
-
-    // Minimum velocity that will be absorbed
-    private static final int MIN_VELOCITY = 100;
-    // Maximum velocity, clamps at this value
-    private static final int MAX_VELOCITY = 10000;
-
-    private static final float EPSILON = 0.001f;
-
-    private static final double ANGLE = Math.PI / 6;
-    private static final float SIN = (float) Math.sin(ANGLE);
-    private static final float COS = (float) Math.cos(ANGLE);
-
-    private float mGlowAlpha;
-    private float mGlowScaleY;
-
-    private float mGlowAlphaStart;
-    private float mGlowAlphaFinish;
-    private float mGlowScaleYStart;
-    private float mGlowScaleYFinish;
-
-    private long mStartTime;
-    private float mDuration;
-
-    private final Interpolator mInterpolator;
-
-    private static final int STATE_IDLE = 0;
-    private static final int STATE_PULL = 1;
-    private static final int STATE_ABSORB = 2;
-    private static final int STATE_RECEDE = 3;
-    private static final int STATE_PULL_DECAY = 4;
-
-    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
-
-    private static final int VELOCITY_GLOW_FACTOR = 6;
-
-    private int mState = STATE_IDLE;
-
-    private float mPullDistance;
-
-    private final Rect mBounds = new Rect();
-    private final Paint mPaint = new Paint();
-    private float mRadius;
-    private float mBaseGlowScale;
-    private float mDisplacement = 0.5f;
-    private float mTargetDisplacement = 0.5f;
-
-    /**
-     * Construct a new EdgeEffect with a theme appropriate for the provided context.
-     */
-    public LauncherEdgeEffect() {
-        mPaint.setAntiAlias(true);
-        mPaint.setStyle(Paint.Style.FILL);
-        mInterpolator = new DecelerateInterpolator();
-    }
-
-    /**
-     * Set the size of this edge effect in pixels.
-     *
-     * @param width Effect width in pixels
-     * @param height Effect height in pixels
-     */
-    public void setSize(int width, int height) {
-        final float r = width * 0.5f / SIN;
-        final float y = COS * r;
-        final float h = r - y;
-        final float or = height * 0.75f / SIN;
-        final float oy = COS * or;
-        final float oh = or - oy;
-
-        mRadius = r;
-        mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
-
-        mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
-    }
-
-    /**
-     * Reports if this EdgeEffect's animation is finished. If this method returns false
-     * after a call to {@link #draw(Canvas)} the host widget should schedule another
-     * drawing pass to continue the animation.
-     *
-     * @return true if animation is finished, false if drawing should continue on the next frame.
-     */
-    public boolean isFinished() {
-        return mState == STATE_IDLE;
-    }
-
-    /**
-     * Immediately finish the current animation.
-     * After this call {@link #isFinished()} will return true.
-     */
-    public void finish() {
-        mState = STATE_IDLE;
-    }
-
-    /**
-     * A view should call this when content is pulled away from an edge by the user.
-     * This will update the state of the current visual effect and its associated animation.
-     * The host view should always {@link android.view.View#invalidate()} after this
-     * and draw the results accordingly.
-     *
-     * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
-     * of the pull point is known.</p>
-     *
-     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
-     *                      1.f (full length of the view) or negative values to express change
-     *                      back toward the edge reached to initiate the effect.
-     */
-    public void onPull(float deltaDistance) {
-        onPull(deltaDistance, 0.5f);
-    }
-
-    /**
-     * A view should call this when content is pulled away from an edge by the user.
-     * This will update the state of the current visual effect and its associated animation.
-     * The host view should always {@link android.view.View#invalidate()} after this
-     * and draw the results accordingly.
-     *
-     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
-     *                      1.f (full length of the view) or negative values to express change
-     *                      back toward the edge reached to initiate the effect.
-     * @param displacement The displacement from the starting side of the effect of the point
-     *                     initiating the pull. In the case of touch this is the finger position.
-     *                     Values may be from 0-1.
-     */
-    public void onPull(float deltaDistance, float displacement) {
-        final long now = AnimationUtils.currentAnimationTimeMillis();
-        mTargetDisplacement = displacement;
-        if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
-            return;
-        }
-        if (mState != STATE_PULL) {
-            mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
-        }
-        mState = STATE_PULL;
-
-        mStartTime = now;
-        mDuration = PULL_TIME;
-
-        mPullDistance += deltaDistance;
-
-        final float absdd = Math.abs(deltaDistance);
-        mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
-                mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
-
-        if (mPullDistance == 0) {
-            mGlowScaleY = mGlowScaleYStart = 0;
-        } else {
-            final float scale = (float) (Math.max(0, 1 - 1 /
-                    Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d);
-
-            mGlowScaleY = mGlowScaleYStart = scale;
-        }
-
-        mGlowAlphaFinish = mGlowAlpha;
-        mGlowScaleYFinish = mGlowScaleY;
-    }
-
-    /**
-     * Call when the object is released after being pulled.
-     * This will begin the "decay" phase of the effect. After calling this method
-     * the host view should {@link android.view.View#invalidate()} and thereby
-     * draw the results accordingly.
-     */
-    public void onRelease() {
-        mPullDistance = 0;
-
-        if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
-            return;
-        }
-
-        mState = STATE_RECEDE;
-        mGlowAlphaStart = mGlowAlpha;
-        mGlowScaleYStart = mGlowScaleY;
-
-        mGlowAlphaFinish = 0.f;
-        mGlowScaleYFinish = 0.f;
-
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = RECEDE_TIME;
-    }
-
-    /**
-     * Call when the effect absorbs an impact at the given velocity.
-     * Used when a fling reaches the scroll boundary.
-     *
-     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
-     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
-     * to use here.</p>
-     *
-     * @param velocity Velocity at impact in pixels per second.
-     */
-    public void onAbsorb(int velocity) {
-        mState = STATE_ABSORB;
-        velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
-
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = 0.15f + (velocity * 0.02f);
-
-        // The glow depends more on the velocity, and therefore starts out
-        // nearly invisible.
-        mGlowAlphaStart = 0.3f;
-        mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
-
-
-        // Growth for the size of the glow should be quadratic to properly
-        // respond
-        // to a user's scrolling speed. The faster the scrolling speed, the more
-        // intense the effect should be for both the size and the saturation.
-        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
-        // Alpha should change for the glow as well as size.
-        mGlowAlphaFinish = Math.max(
-                mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
-        mTargetDisplacement = 0.5f;
-    }
-
-    /**
-     * Set the color of this edge effect in argb.
-     *
-     * @param color Color in argb
-     */
-    public void setColor(int color) {
-        mPaint.setColor(color);
-    }
-
-    /**
-     * Return the color of this edge effect in argb.
-     * @return The color of this edge effect in argb
-     */
-    public int getColor() {
-        return mPaint.getColor();
-    }
-
-    /**
-     * Draw into the provided canvas. Assumes that the canvas has been rotated
-     * accordingly and the size has been set. The effect will be drawn the full
-     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
-     * 1.f of height.
-     *
-     * @param canvas Canvas to draw into
-     * @return true if drawing should continue beyond this frame to continue the
-     *         animation
-     */
-    public boolean draw(Canvas canvas) {
-        update();
-
-        final float centerX = mBounds.centerX();
-        final float centerY = mBounds.height() - mRadius;
-
-        canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
-
-        final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
-        float translateX = mBounds.width() * displacement / 2;
-        mPaint.setAlpha((int) (0xff * mGlowAlpha));
-        canvas.drawCircle(centerX + translateX, centerY, mRadius, mPaint);
-
-        boolean oneLastFrame = false;
-        if (mState == STATE_RECEDE && mGlowScaleY == 0) {
-            mState = STATE_IDLE;
-            oneLastFrame = true;
-        }
-
-        return mState != STATE_IDLE || oneLastFrame;
-    }
-
-    /**
-     * Return the maximum height that the edge effect will be drawn at given the original
-     * {@link #setSize(int, int) input size}.
-     * @return The maximum height of the edge effect
-     */
-    public int getMaxHeight() {
-        return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
-    }
-
-    private void update() {
-        final long time = AnimationUtils.currentAnimationTimeMillis();
-        final float t = Math.min((time - mStartTime) / mDuration, 1.f);
-
-        final float interp = mInterpolator.getInterpolation(t);
-
-        mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
-        mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
-        mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
-
-        if (t >= 1.f - EPSILON) {
-            switch (mState) {
-                case STATE_ABSORB:
-                    mState = STATE_RECEDE;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = RECEDE_TIME;
-
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After absorb, the glow should fade to nothing.
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
-                    break;
-                case STATE_PULL:
-                    mState = STATE_PULL_DECAY;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = PULL_DECAY_TIME;
-
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After pull, the glow should fade to nothing.
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
-                    break;
-                case STATE_PULL_DECAY:
-                    mState = STATE_RECEDE;
-                    break;
-                case STATE_RECEDE:
-                    mState = STATE_IDLE;
-                    break;
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index fff3472..a754375 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -44,10 +44,12 @@
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.GradientView;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TouchController;
 
 import java.util.List;
@@ -70,6 +72,7 @@
     private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
     private Rect mInsets;
     private VerticalPullDetector mVerticalPullDetector;
+    private GradientView mGradientBackground;
 
     public WidgetsBottomSheet(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -86,6 +89,7 @@
         mInsets = new Rect();
         mVerticalPullDetector = new VerticalPullDetector(context);
         mVerticalPullDetector.setListener(this);
+        mGradientBackground = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
     }
 
     @Override
@@ -178,8 +182,10 @@
             return;
         }
         mIsOpen = true;
+        boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
         mLauncher.getSystemUiController().updateUiState(
-                SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, SystemUiController.FLAG_LIGHT_NAV);
+                SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
+                isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
         if (animate) {
             mOpenCloseAnimator.setValues(new PropertyListBuilder()
                     .translationY(mTranslationYOpen).build());
@@ -267,6 +273,13 @@
     }
 
     @Override
+    public void setTranslationY(float translationY) {
+        super.setTranslationY(translationY);
+        if (mGradientBackground == null) return;
+        mGradientBackground.setProgress((mTranslationYClosed - translationY) / mTranslationYRange);
+    }
+
+    @Override
     public void onDragEnd(float velocity, boolean fling) {
         if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) {
             mScrollInterpolator.setVelocityAtZero(velocity);