Merge "Add onWidgetsBound() callback to TaskMenuView" into ub-launcher3-master
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 587261d..222a3f4 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,4 +24,7 @@
     <dimen name="quickstep_fling_min_velocity">250dp</dimen>
 
     <dimen name="workspace_overview_offset_x">-30dp</dimen>
+
+    <!-- TODO: This can be calculated using other resource values -->
+    <dimen name="all_apps_search_box_full_height">90dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index a4851ba..20ee547 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -42,7 +42,7 @@
     };
 
     public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS);
+        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
@@ -61,6 +61,11 @@
     }
 
     @Override
+    public float getVerticalProgress(Launcher launcher) {
+        return 0f;
+    }
+
+    @Override
     public View getFinalFocus(Launcher launcher) {
         return launcher.getAppsView();
     }
@@ -75,4 +80,9 @@
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return PAGE_ALPHA_PROVIDER;
     }
+
+    @Override
+    public float getHoseatAlpha(Launcher launcher) {
+        return launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 1;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index f1da817..989803a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,10 +16,12 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 
 import android.graphics.Rect;
 import android.view.View;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
@@ -38,7 +40,7 @@
     private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
 
     public OverviewState(int id) {
-        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS);
+        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
@@ -59,11 +61,6 @@
     }
 
     @Override
-    public float getHoseatAlpha(Launcher launcher) {
-        return launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 1;
-    }
-
-    @Override
     public void onStateEnabled(Launcher launcher) {
         RecentsView rv = launcher.getOverviewPanel();
         rv.setOverviewStateEnabled(true);
@@ -76,27 +73,56 @@
     }
 
     @Override
+    public float getVerticalProgress(Launcher launcher) {
+        DeviceProfile grid = launcher.getDeviceProfile();
+        if (!grid.isVerticalBarLayout()) {
+            return 1f;
+        }
+
+        float total = grid.heightPx;
+        float searchHeight = total - grid.availableHeightPx +
+                launcher.getResources().getDimension(R.dimen.all_apps_search_box_full_height);
+        return 1 - (searchHeight / total);
+    }
+
+    @Override
     public View getFinalFocus(Launcher launcher) {
         return launcher.getOverviewPanel();
     }
 
+    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+        final int centerPage = launcher.getWorkspace().getNextPage();
+        return new PageAlphaProvider(ACCEL_2) {
+            @Override
+            public float getPageAlpha(int pageIndex) {
+                return  pageIndex != centerPage ? 0 : 1f;
+            }
+        };
+    }
+
     public static float[] getScaleAndTranslationForPageRect(Launcher launcher, float offsetX,
             Rect pageRect) {
         Workspace ws = launcher.getWorkspace();
         float childWidth = ws.getNormalChildWidth();
+        float childHeight = ws.getNormalChildHeight();
 
         Rect insets = launcher.getDragLayer().getInsets();
-        float scale = pageRect.width() / childWidth;
-
-        float translationX = offsetX / scale;
-        if (Utilities.isRtl(launcher.getResources())) {
-            translationX = -translationX;
-        }
+        float scale = Math.min(pageRect.width() / childWidth, pageRect.height() / childHeight);
 
         float halfHeight = ws.getHeight() / 2;
         float childTop = halfHeight - scale * (halfHeight - ws.getPaddingTop() - insets.top);
         float translationY = pageRect.top - childTop;
 
+        float halfWidth = ws.getWidth() / 2;
+        float translationX;
+        if (Utilities.isRtl(launcher.getResources())) {
+            float childRight = halfWidth + scale * (halfWidth - ws.getPaddingRight() - insets.right);
+            translationX = childRight - pageRect.right - offsetX / scale;
+        } else {
+            float childLeft = halfWidth - scale * (halfWidth - ws.getPaddingLeft() - insets.left);
+            translationX = pageRect.left - childLeft + offsetX / scale;
+        }
+
         return new float[] {scale, translationX, translationY};
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
index 92f89f6..3ae8f41 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
@@ -38,7 +38,15 @@
 
     @Override
     protected boolean shouldInterceptTouch(MotionEvent ev) {
-        return mLauncher.isInState(OVERVIEW) && mLauncher.getDragLayer().isEventOverHotseat(ev);
+        if (!mLauncher.isInState(OVERVIEW)) {
+            return false;
+        }
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            return ev.getY() >
+                    mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher);
+        } else {
+            return mLauncher.getDragLayer().isEventOverHotseat(ev);
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 1f6ffe9..d657e4e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -24,12 +24,14 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.PagedView;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.RecentsView;
 
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 
 public class RecentsViewStateController implements StateHandler {
@@ -62,6 +64,16 @@
     @Override
     public void setStateWithAnimation(final LauncherState toState,
             AnimatorSetBuilder builder, AnimationConfig config) {
+        // Scroll to the workspace card before changing to the NORMAL state.
+        int currPage = mRecentsView.getCurrentPage();
+        if (toState == NORMAL && currPage != 0 && !config.userControlled) {
+            int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION;
+            int durationPerPage = maxSnapDuration / 10;
+            int snapDuration = Math.min(maxSnapDuration, durationPerPage * currPage);
+            mRecentsView.snapToPage(0, snapDuration);
+            builder.setStartDelay(snapDuration);
+        }
+
         ObjectAnimator progressAnim =
                 mTransitionProgress.animateToValue(toState == OVERVIEW ? 1 : 0);
         progressAnim.setDuration(config.duration);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index 435d57e..f59f0de 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -29,7 +29,6 @@
 import android.support.animation.SpringAnimation;
 import android.util.Log;
 import android.view.MotionEvent;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
@@ -42,7 +41,6 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.SpringAnimationHandler;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -50,6 +48,10 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.FloatRange;
 import com.android.launcher3.util.TouchController;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.TouchInteractionService;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
 
 import java.util.ArrayList;
 
@@ -86,6 +88,8 @@
     private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0;
     private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1;
     private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
+    private static final int FLAG_RECENTS_PLAN_LOADING = 1 << 3;
+    private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
 
     private final Launcher mLauncher;
     private final SwipeDetector mDetector;
@@ -98,9 +102,10 @@
     private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder;
     private AnimatorSet mQuickOverviewAnimation;
     private boolean mAnimatingToOverview;
-    private TwoStateAnimationController mTwoStateAnimationController;
+    private CroppedAnimationController mCroppedAnimationController;
 
     private AnimatorPlaybackController mCurrentAnimation;
+    private LauncherState mFromState;
     private LauncherState mToState;
 
     private float mStartProgress;
@@ -240,11 +245,27 @@
                     + MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW;
 
             // Build current animation
+            mFromState = mLauncher.getStateManager().getState();
             mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
             mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder();
             mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
                     mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
 
+            if (TouchInteractionService.isConnected()) {
+                // Load recents plan
+                RecentsModel recentsModel = RecentsModel.getInstance(mLauncher);
+                if (recentsModel.getLastLoadPlan() != null) {
+                    onRecentsPlanLoaded(recentsModel.getLastLoadPlan());
+                } else {
+                    mDragPauseDetector.addDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+                }
+                // Reload again so that we get the latest list
+                // TODO: Use callback instead of polling everytime
+                recentsModel.loadTasks(-1, this::onRecentsPlanLoaded);
+            } else {
+                mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
+            }
+
             mCurrentAnimation.getTarget().addListener(this);
             mStartProgress = 0;
             mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
@@ -262,6 +283,14 @@
         }
     }
 
+    private void onRecentsPlanLoaded(RecentsTaskLoadPlan plan) {
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        recentsView.update(plan);
+        recentsView.initToPage(0);
+
+        mDragPauseDetector.clearDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+    }
+
     private float getShiftRange() {
         return mLauncher.getAllAppsController().getShiftRange();
     }
@@ -287,39 +316,19 @@
 
     @Override
     public void onDragEnd(float velocity, boolean fling) {
-        if (!fling && mDragPauseDetector.isEnabled() && mDragPauseDetector.isTriggered()) {
-            snapToOverview(velocity);
-            return;
-        }
-
         mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
 
-        final long animationDuration;
         final int logAction;
-        final LauncherState targetState;
+        LauncherState targetState;
         final float progress = mCurrentAnimation.getProgressFraction();
 
         if (fling) {
             logAction = Touch.FLING;
-            if (velocity < 0) {
-                targetState = ALL_APPS;
-                animationDuration = SwipeDetector.calculateDuration(velocity,
-                        mToState == ALL_APPS ? (1 - progress) : progress);
-            } else {
-                targetState = NORMAL;
-                animationDuration = SwipeDetector.calculateDuration(velocity,
-                        mToState == ALL_APPS ? progress : (1 - progress));
-            }
+            targetState = velocity < 0 ? ALL_APPS : NORMAL;
             // snap to top or bottom using the release velocity
         } else {
             logAction = Touch.SWIPE;
-            if (progress > SUCCESS_TRANSITION_PROGRESS) {
-                targetState = mToState;
-                animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
-            } else {
-                targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
-                animationDuration = SwipeDetector.calculateDuration(velocity, progress);
-            }
+            targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
         }
 
         if (fling && targetState == ALL_APPS) {
@@ -328,20 +337,38 @@
                 h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
             }
         }
-        mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+
+        float endProgress;
+
+        if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
+            targetState = OVERVIEW;
+            endProgress = OVERVIEW.getVerticalProgress(mLauncher);
+            if (mFromState == NORMAL) {
+                endProgress = 1 - endProgress;
+            }
+        } else if (targetState == mToState) {
+            endProgress = 1;
+        } else {
+            endProgress = 0;
+        }
+
+        LauncherState targetStateFinal = targetState;
+        mCurrentAnimation.setEndAction(() ->
+                onSwipeInteractionCompleted(targetStateFinal, logAction));
 
         float nextFrameProgress = Utilities.boundToRange(
                 progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
 
         ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
-        anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f);
-        anim.setDuration(animationDuration);
+        anim.setFloatValues(nextFrameProgress, endProgress);
+        anim.setDuration(
+                SwipeDetector.calculateDuration(velocity, Math.abs(endProgress - progress)));
         anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
         anim.start();
     }
 
     private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
-        if (targetState == mToState) {
+        if (targetState != mFromState) {
             // Transition complete. log the action
             mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
                     mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
@@ -354,33 +381,6 @@
         mLauncher.getStateManager().goToState(targetState, false /* animated */);
     }
 
-    private void snapToOverview(float velocity) {
-        mAnimatingToOverview = true;
-
-        final float progress = mCurrentAnimation.getProgressFraction();
-        float endProgress = mToState == NORMAL ? 1f : 0f;
-        long animationDuration = SwipeDetector.calculateDuration(
-                velocity, Math.abs(endProgress - progress));
-        float nextFrameProgress = Utilities.boundToRange(
-                progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
-
-        mCurrentAnimation.setEndAction(() -> {
-            // TODO: Add logging
-            clearState();
-            mLauncher.getStateManager().goToState(OVERVIEW, true /* animated */);
-        });
-
-        if (mTwoStateAnimationController != null) {
-            mTwoStateAnimationController.goBackToStart(endProgress);
-        }
-
-        ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
-        anim.setFloatValues(nextFrameProgress, endProgress);
-        anim.setDuration(animationDuration);
-        anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
-        anim.start();
-    }
-
     private void onDragPauseDetected() {
         final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1);
         twoStepAnimator.setDuration(mCurrentAnimation.getDuration());
@@ -409,33 +409,29 @@
         mQuickOverviewAnimation.start();
     }
 
-    private void onQuickOverviewAnimationComplete(ValueAnimator twoStepAnimator) {
+    private void onQuickOverviewAnimationComplete(ValueAnimator animator) {
         if (mAnimatingToOverview) {
             return;
         }
 
-        // The remaining state handlers are on the OVERVIEW state. Create two animations, one
-        // towards the NORMAL state and one towards ALL_APPS state and control them based on the
-        // swipe progress.
+        // For the remainder to the interaction, the user can either go to the ALL_APPS state or
+        // the OVERVIEW state.
+        // The remaining state handlers are on the OVERVIEW state. Create one animation towards the
+        // ALL_APPS state and only call it when the user moved above the current range.
         AnimationConfig config = new AnimationConfig();
         config.duration = (long) (2 * getShiftRange());
         config.userControlled = true;
 
-        LauncherState fromState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
-        AnimatorSetBuilder builderToTargetState = new AnimatorSetBuilder();
-        AnimatorSetBuilder builderToSourceState = new AnimatorSetBuilder();
-
+        AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder();
         StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
         for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
-            handlers[i].setStateWithAnimation(mToState, builderToTargetState, config);
-            handlers[i].setStateWithAnimation(fromState, builderToSourceState, config);
+            handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config);
         }
 
-        mTwoStateAnimationController = new TwoStateAnimationController(
-                AnimatorPlaybackController.wrap(builderToSourceState.build(), config.duration),
-                AnimatorPlaybackController.wrap(builderToTargetState.build(), config.duration),
-                twoStepAnimator.getAnimatedFraction());
-        twoStepAnimator.addUpdateListener(mTwoStateAnimationController);
+        mCroppedAnimationController = new CroppedAnimationController(
+                AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration),
+                new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0));
+        animator.addUpdateListener(mCroppedAnimationController);
     }
 
     private void clearState() {
@@ -450,69 +446,49 @@
             mQuickOverviewAnimation.cancel();
             mQuickOverviewAnimation = null;
         }
-        mTwoStateAnimationController = null;
+        mCroppedAnimationController = null;
         mAnimatingToOverview = false;
 
         mDetector.finishedScrolling();
     }
 
     /**
-     * {@link AnimatorUpdateListener} which interpolates two animations based the progress
+     * {@link AnimatorUpdateListener} which controls another animation for a fraction of range
      */
-    private static class TwoStateAnimationController implements AnimatorUpdateListener {
+    private static class CroppedAnimationController implements AnimatorUpdateListener {
 
-        private final AnimatorPlaybackController mControllerTowardsStart;
-        private final AnimatorPlaybackController mControllerTowardsEnd;
+        private final AnimatorPlaybackController mTarget;
+        private final FloatRange mRange;
 
-        private Interpolator mInterpolator = Interpolators.LINEAR;
-        private float mStartFraction;
-        private float mLastFraction;
-
-        TwoStateAnimationController(AnimatorPlaybackController controllerTowardsStart,
-                AnimatorPlaybackController controllerTowardsEnd, float startFraction) {
-            mControllerTowardsStart = controllerTowardsStart;
-            mControllerTowardsEnd = controllerTowardsEnd;
-            mLastFraction = mStartFraction = startFraction;
+        CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) {
+            mTarget = target;
+            mRange = range;
         }
 
+
         @Override
         public void onAnimationUpdate(ValueAnimator valueAnimator) {
-            mLastFraction = mInterpolator.getInterpolation(valueAnimator.getAnimatedFraction());
-            if (mLastFraction > mStartFraction) {
-                if (mStartFraction >= 1) {
-                    mControllerTowardsEnd.setPlayFraction(0);
-                } else {
-                    mControllerTowardsEnd.setPlayFraction(
-                            (mLastFraction - mStartFraction) / (1 - mStartFraction));
-                }
-            } else {
-                if (mStartFraction <= 0) {
-                    mControllerTowardsStart.setPlayFraction(0);
-                } else {
-                    mControllerTowardsStart.setPlayFraction(
-                            (mStartFraction - mLastFraction) / mStartFraction);
-                }
-            }
-        }
+            float fraction = valueAnimator.getAnimatedFraction();
 
-        /**
-         * Changes the interpolator such that from this point ({@link #mLastFraction}), the
-         * animation run towards {@link #mStartFraction}. This allows us to animate the UI back
-         * to the original point.
-         * @param endFraction expected end point for this animation. Should either be 0 or 1.
-         */
-        public void goBackToStart(float endFraction) {
-            if (mLastFraction == mStartFraction || mLastFraction == endFraction) {
-                mInterpolator = (v) -> mStartFraction;
-            } else if (mLastFraction > mStartFraction && endFraction < mStartFraction) {
-                mInterpolator = (v) -> Math.max(v, mStartFraction);
-            } else if (mLastFraction < mStartFraction && endFraction > mStartFraction) {
-                mInterpolator = (v) -> Math.min(mStartFraction, v);
+            if (mRange.start < mRange.end) {
+                if (fraction <= mRange.start) {
+                    mTarget.setPlayFraction(0);
+                } else if (fraction >= mRange.end) {
+                    mTarget.setPlayFraction(1);
+                } else {
+                    mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+                }
+            } else if (mRange.start > mRange.end) {
+                if (fraction >= mRange.start) {
+                    mTarget.setPlayFraction(0);
+                } else if (fraction <= mRange.end) {
+                    mTarget.setPlayFraction(1);
+                } else {
+                    mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+                }
             } else {
-                final float start = mLastFraction;
-                final float range = endFraction - mLastFraction;
-                mInterpolator = (v) ->
-                        SwipeDetector.interpolate(start, mStartFraction, (v - start) / range);
+                // mRange.start == mRange.end
+                mTarget.setPlayFraction(0);
             }
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 73bf85f..05bd171 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -37,15 +37,9 @@
     public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD;
 
     public static TouchController[] createTouchControllers(Launcher launcher) {
-
-        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            // TODO: Allow swipe up from overview in transposed layout
-            return new TouchController[] {new TwoStepSwipeController(launcher)};
-        } else {
-            return new TouchController[] {
-                    new TwoStepSwipeController(launcher),
-                    new OverviewSwipeUpController(launcher)};
-        }
+        return new TouchController[] {
+                new TwoStepSwipeController(launcher),
+                new OverviewSwipeUpController(launcher)};
     }
 
     public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
index 9f7cffe..ed60b84 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
@@ -54,6 +54,8 @@
     private View mWorkspaceClickTarget;
     private View mWidgetsButton;
 
+    private boolean mLayoutHorizontal;
+
     public WorkspaceCard(Context context) {
         super(context);
     }
@@ -97,30 +99,69 @@
             return;
         }
 
-        int childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
+        float workspaceWidth = mWorkspace.getNormalChildWidth();
+        float workspaceHeight = mWorkspace.getNormalChildHeight();
 
-        int pageHeight = mWorkspace.getNormalChildHeight() * widthSize /
-                mWorkspace.getNormalChildWidth();
-        mWorkspaceClickTarget.measure(childWidthSpec,
-                MeasureSpec.makeMeasureSpec(pageHeight, MeasureSpec.EXACTLY));
+        int availableWidth = widthSize - getPaddingLeft() - getPaddingRight();
+        float scaleX = availableWidth / workspaceWidth;
 
-        int buttonHeight = heightSize - pageHeight - getPaddingTop() - getPaddingBottom();
-        mWidgetsButton.measure(childWidthSpec,
-                MeasureSpec.makeMeasureSpec(buttonHeight, MeasureSpec.EXACTLY));
+        int availableHeight = heightSize - getPaddingTop() - getPaddingBottom();
+        float scaleY = availableHeight / workspaceHeight;
 
+        if (scaleX < scaleY) {
+            mLayoutHorizontal = false;
+            int childWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY);
+
+            int pageHeight = Math.round(workspaceHeight * scaleX);
+            mWorkspaceClickTarget.measure(childWidthSpec,
+                    MeasureSpec.makeMeasureSpec(pageHeight, MeasureSpec.EXACTLY));
+
+            int buttonHeight = availableHeight - pageHeight;
+            mWidgetsButton.measure(childWidthSpec,
+                    MeasureSpec.makeMeasureSpec(buttonHeight, MeasureSpec.EXACTLY));
+        } else {
+            mLayoutHorizontal = true;
+            int childHeightSpec = MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY);
+
+            int pageWidth = Math.round(workspaceWidth * scaleY);
+            mWorkspaceClickTarget.measure(
+                    MeasureSpec.makeMeasureSpec(pageWidth, MeasureSpec.EXACTLY), childHeightSpec);
+
+            int buttonWidth = availableWidth - pageWidth;
+            mWidgetsButton.measure(
+                    MeasureSpec.makeMeasureSpec(buttonWidth, MeasureSpec.EXACTLY), childHeightSpec);
+        }
         setMeasuredDimension(widthSize, heightSize);
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        int y1 = getPaddingTop();
-        int y2 = y1 + mWorkspaceClickTarget.getMeasuredHeight();
+        int x = getPaddingLeft();
+        int y = getPaddingTop();
 
-        mWorkspaceClickTarget.layout(getPaddingLeft(), y1,
-                mWorkspaceClickTarget.getMeasuredWidth(), y2);
-
-        mWidgetsButton.layout(getPaddingLeft(), y2, mWidgetsButton.getMeasuredWidth(),
-                mWidgetsButton.getMeasuredHeight() + y2);
+        if (mLayoutHorizontal) {
+            final View first, second;
+            if (Utilities.isRtl(getResources())) {
+                first = mWidgetsButton;
+                second = mWorkspaceClickTarget;
+            } else {
+                first = mWorkspaceClickTarget;
+                second = mWidgetsButton;
+            }
+            int x2 = x + first.getMeasuredWidth();
+            first.layout(x, y,
+                    x2, y + first.getMeasuredHeight());
+            second.layout(x2, y,
+                    x2 + second.getMeasuredWidth(),
+                    y + second.getMeasuredHeight());
+        } else {
+            int y2 = y + mWorkspaceClickTarget.getMeasuredHeight();
+            mWorkspaceClickTarget.layout(x, y,
+                    x + mWorkspaceClickTarget.getMeasuredWidth(), y2);
+            mWidgetsButton.layout(x, y2,
+                    x + mWidgetsButton.getMeasuredWidth(),
+                    y2 + mWidgetsButton.getMeasuredHeight());
+        }
 
         mUIDataValid = false;
     }
@@ -165,7 +206,7 @@
             mWorkspace.setScaleY(mEvaluatedFloats[0]);
             mWorkspace.setTranslationX(mEvaluatedFloats[1]);
             mWorkspace.setTranslationY(mEvaluatedFloats[2]);
-            translateX += mEvaluatedFloats[1];
+            translateX += mEvaluatedFloats[1] - mScaleAndTranslatePage0[1];
         }
 
         setTranslationX(translateX);
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index 095b445..3ca1865 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.states.InternalStateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.AllAppsScrim;
 import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -97,6 +98,7 @@
     private RecentsView mRecentsView;
     private RecentsViewStateController mStateController;
     private Hotseat mHotseat;
+    private AllAppsScrim mAllAppsScrim;
     private RecentsTaskLoadPlan mLoadPlan;
 
     private boolean mLauncherReady;
@@ -182,9 +184,13 @@
         mRecentsView = mLauncher.getOverviewPanel();
         mStateController = mRecentsView.getStateController();
         mHotseat = mLauncher.getHotseat();
+        mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
 
         // Optimization
-        mLauncher.getAppsView().setVisibility(View.GONE);
+        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            // All-apps search box is visible in vertical bar layout.
+            mLauncher.getAppsView().setVisibility(View.GONE);
+        }
         mStateController.setTransitionProgress(1);
         mStateController.setVisibility(false);
         TraceHelper.partitionSection("TouchInt", "Launcher on new intent");
@@ -222,7 +228,9 @@
         float shift = mCurrentShift.value * mActivityMultiplier.value;
         int hotseatSize = getHotseatSize();
 
-        mHotseat.setTranslationY((1 - shift) * hotseatSize);
+        float hotseatTranslation = (1 - shift) * hotseatSize;
+        mHotseat.setTranslationY(hotseatTranslation);
+        mAllAppsScrim.setTranslationY(hotseatTranslation);
 
         mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
 
@@ -324,6 +332,7 @@
     private void cleanupLauncher() {
         // TODO: These should be done as part of ActivityOptions#OnAnimationStarted
         mHotseat.setTranslationY(0);
+        mAllAppsScrim.setTranslationY(0);
         mLauncher.setOnResumeCallback(() -> mDragView.close(false));
     }
 
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
new file mode 100644
index 0000000..112f156
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.quickstep;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
+import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.system.BackgroundExecutor;
+
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+
+/**
+ * Singleton class to load and manage recents model.
+ */
+public class RecentsModel {
+
+    // We do not need any synchronization for this variable as its only written on UI thread.
+    private static RecentsModel INSTANCE;
+
+    public static RecentsModel getInstance(final Context context) {
+        if (INSTANCE == null) {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                INSTANCE = new RecentsModel(context.getApplicationContext());
+            } else {
+                try {
+                    return new MainThreadExecutor().submit(
+                            () -> RecentsModel.getInstance(context)).get();
+                } catch (InterruptedException|ExecutionException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+        return INSTANCE;
+    }
+
+    private final Context mContext;
+    private final RecentsTaskLoader mRecentsTaskLoader;
+    private final MainThreadExecutor mMainThreadExecutor;
+
+    private RecentsTaskLoadPlan mLastLoadPlan;
+    private RecentsModel(Context context) {
+        mContext = context;
+
+        Resources res = context.getResources();
+        mRecentsTaskLoader = new RecentsTaskLoader(mContext,
+                res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
+                res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
+        mRecentsTaskLoader.startLoader(mContext);
+
+        mMainThreadExecutor = new MainThreadExecutor();
+    }
+
+    public RecentsTaskLoader getRecentsTaskLoader() {
+        return mRecentsTaskLoader;
+    }
+
+    /**
+     * Preloads the task plan
+     * @param taskId The running task id or -1
+     * @param callback The callback to receive the task plan once its complete or null. This is
+     *                always called on the UI thread.
+     */
+    public void loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
+        BackgroundExecutor.get().submit(() -> {
+            // Preload the plan
+            RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
+            PreloadOptions opts = new PreloadOptions();
+            opts.loadTitles = false;
+            loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
+            // Set the load plan on UI thread
+            mMainThreadExecutor.execute(() -> {
+                mLastLoadPlan = loadPlan;
+                if (callback != null) {
+                    callback.accept(loadPlan);
+                }
+            });
+        });
+    }
+
+    public RecentsTaskLoadPlan getLastLoadPlan() {
+        return mLastLoadPlan;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 6161858..ff90ebe 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -41,6 +41,8 @@
 
 import java.util.ArrayList;
 
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
 /**
  * A list of recent tasks.
  */
@@ -146,7 +148,8 @@
     }
 
     public void update(RecentsTaskLoadPlan loadPlan) {
-        final RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
+        final RecentsTaskLoader loader = RecentsModel.getInstance(getContext())
+                .getRecentsTaskLoader();
         TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
         if (stack == null) {
             removeAllViews();
@@ -215,11 +218,30 @@
         Rect stableInsets = new Rect();
         WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
         Rect padding = profile.getWorkspacePadding(null);
-        float taskWidth = profile.getCurrentWidth() - stableInsets.left - stableInsets.right;
-        float taskHeight = profile.getCurrentHeight() - stableInsets.top - stableInsets.bottom;
-        float overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
-                - stableInsets.top;
-        float overviewWidth = taskWidth * overviewHeight / taskHeight;
+
+        float taskWidth = profile.widthPx - stableInsets.left - stableInsets.right;
+        float taskHeight = profile.heightPx - stableInsets.top - stableInsets.bottom;
+
+        float overviewHeight, overviewWidth;
+        if (profile.isVerticalBarLayout()) {
+            // Use the same padding on both sides for symmetry.
+            float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right);
+            float availableHeight = profile.availableHeightPx - padding.top - padding.bottom
+                    - stableInsets.top
+                    - profile.heightPx * (1 - OVERVIEW.getVerticalProgress(launcher));
+
+            float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+            overviewHeight = taskHeight * scaledRatio;
+            overviewWidth = taskWidth * scaledRatio;
+
+        } else {
+            overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
+                    - stableInsets.top;
+            overviewWidth = taskWidth * overviewHeight / taskHeight;
+        }
+
+        padding.bottom = profile.availableHeightPx - padding.top - stableInsets.top
+                - Math.round(overviewHeight);
         padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
         return padding;
     }
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
index 4a9bfea..473681f 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
@@ -161,7 +161,7 @@
                     // Scale the landscape thumbnail up to app size, then scale that to the task
                     // view size to match other portrait screenshots
                     mThumbnailScale = invThumbnailScale *
-                            ((float) getMeasuredWidth() / profile.getCurrentWidth());
+                            ((float) getMeasuredWidth() / profile.widthPx);
                 } else {
                     // Otherwise, scale the screenshot to fit 1:1 in the current orientation
                     mThumbnailScale = invThumbnailScale;
@@ -173,4 +173,10 @@
         }
         invalidate();
     }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        updateThumbnailMatrix();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index f457a59..4321791 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -28,10 +28,8 @@
 import android.app.ActivityOptions;
 import android.app.Service;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -39,7 +37,6 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.Display;
@@ -52,14 +49,9 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
 import com.android.launcher3.util.TraceHelper;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.BackgroundExecutor;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -74,8 +66,6 @@
 
     private static final String TAG = "TouchInteractionService";
 
-    private static RecentsTaskLoader sRecentsTaskLoader;
-
     private final IBinder mMyBinder = new IOverviewProxy.Stub() {
 
         @Override
@@ -93,12 +83,18 @@
             = this::handleTouchDownOnOtherActivity;
     private final Consumer<MotionEvent> mNoOpTouchConsumer = (ev) -> {};
 
+    private static boolean sConnected = false;
+
+    public static boolean isConnected() {
+        return sConnected;
+    }
+
     private ActivityManagerWrapper mAM;
     private RunningTaskInfo mRunningTask;
+    private RecentsModel mRecentsModel;
     private Intent mHomeIntent;
     private ComponentName mLauncher;
     private MotionEventQueue mEventQueue;
-    private MainThreadExecutor mMainThreadExecutor;
 
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
@@ -117,6 +113,7 @@
     public void onCreate() {
         super.onCreate();
         mAM = ActivityManagerWrapper.getInstance();
+        mRecentsModel = RecentsModel.getInstance(this);
 
         mHomeIntent = new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME)
@@ -126,16 +123,15 @@
         mLauncher = new ComponentName(getPackageName(), info.activityInfo.name);
         mHomeIntent.setComponent(mLauncher);
 
-        Resources res = getResources();
-        if (sRecentsTaskLoader == null) {
-            sRecentsTaskLoader = new RecentsTaskLoader(this,
-                    res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
-                    res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
-            sRecentsTaskLoader.startLoader(this);
-        }
-
-        mMainThreadExecutor = new MainThreadExecutor();
         mEventQueue = new MotionEventQueue(Choreographer.getInstance(), this::handleMotionEvent);
+        mRecentsModel.loadTasks(-1, null);
+        sConnected = true;
+    }
+
+    @Override
+    public void onDestroy() {
+        sConnected = false;
+        super.onDestroy();
     }
 
     @Override
@@ -144,10 +140,6 @@
         return mMyBinder;
     }
 
-    public static RecentsTaskLoader getRecentsTaskLoader() {
-        return sRecentsTaskLoader;
-    }
-
     private void handleMotionEvent(MotionEvent ev) {
         if (ev.getActionMasked() == ACTION_DOWN) {
             mRunningTask = mAM.getRunningTask();
@@ -255,12 +247,9 @@
         final NavBarSwipeInteractionHandler handler =
                 new NavBarSwipeInteractionHandler(mRunningTask, this);
 
-        // Preload and start the recents activity on a background thread
-        final Context context = this;
-        final RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(context);
-        final int taskId = mRunningTask.id;
         TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
 
+        // Start the recents activity on a background thread
         BackgroundExecutor.get().submit(() -> {
             // Get the snap shot before
             handler.setTaskSnapshot(getCurrentTaskSnapshot());
@@ -275,15 +264,10 @@
                     ActivityOptions.makeCustomAnimation(this, 0, 0), UserHandle.myUserId(),
                     null, null);
              */
-
-            // Preload the plan
-            RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
-            PreloadOptions opts = new PreloadOptions();
-            opts.loadTitles = false;
-            loadPlan.preloadPlan(opts, loader, taskId, UserHandle.myUserId());
-            // Set the load plan on UI thread
-            mMainThreadExecutor.execute(() -> handler.setRecentsTaskLoadPlan(loadPlan));
         });
+
+        // Preload the plan
+        mRecentsModel.loadTasks(mRunningTask.id, handler::setRecentsTaskLoadPlan);
         mInteractionHandler = handler;
     }
 
diff --git a/res/drawable/bg_deferred_app_widget.xml b/res/drawable/bg_deferred_app_widget.xml
new file mode 100644
index 0000000..07bae48
--- /dev/null
+++ b/res/drawable/bg_deferred_app_widget.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:inset="8dp">
+    <color android:color="#77000000" />
+</inset>
diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml
deleted file mode 100644
index 16aa2af..0000000
--- a/res/layout-land/all_apps_fast_scroller.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<merge
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto">
-    <!-- Fast scroller popup -->
-    <TextView
-        android:id="@+id/fast_scroller_popup"
-        style="@style/FastScrollerPopup"
-        android:layout_alignParentEnd="true"
-        android:layout_below="@+id/search_container_all_apps"
-        android:layout_marginTop="-5dp"
-        android:layout_marginEnd="-45dp" />
-
-    <com.android.launcher3.allapps.LandscapeFastScroller
-        android:id="@+id/fast_scroller"
-        android:layout_width="48dp"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentEnd="true"
-        android:layout_below="@+id/search_container_all_apps"
-        android:layout_marginEnd="-88dp"
-        android:layout_marginTop="14dp"
-        launcher:canThumbDetach="true" />
-
-</merge>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 0a4fec1..3422a98 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -57,8 +57,7 @@
         <com.android.launcher3.views.AllAppsScrim
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:id="@+id/all_apps_scrim"
-            launcher:layout_ignoreInsets="true" />
+            android:id="@+id/all_apps_scrim" />
 
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 33b350a..53743e8 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -51,8 +51,7 @@
         <com.android.launcher3.views.AllAppsScrim
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:id="@+id/all_apps_scrim"
-            launcher:layout_ignoreInsets="true" />
+            android:id="@+id/all_apps_scrim" />
 
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
diff --git a/res/layout-sw720dp/all_apps_fast_scroller.xml b/res/layout-sw720dp/all_apps_fast_scroller.xml
deleted file mode 100644
index 5537bc6..0000000
--- a/res/layout-sw720dp/all_apps_fast_scroller.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<merge
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto">
-    <!-- Fast scroller popup -->
-    <TextView
-        android:id="@+id/fast_scroller_popup"
-        style="@style/FastScrollerPopup"
-        android:layout_alignParentEnd="true"
-        android:layout_below="@+id/search_container_all_apps"
-        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
-
-    <com.android.launcher3.views.RecyclerViewFastScroller
-        android:id="@+id/fast_scroller"
-        android:layout_width="@dimen/fastscroll_width"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentEnd="true"
-        android:layout_below="@+id/search_container_all_apps"
-        android:layout_marginEnd="@dimen/fastscroll_end_margin"
-        launcher:canThumbDetach="true" />
-
-</merge>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index d7e931a..047577b 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -50,8 +50,7 @@
         <com.android.launcher3.views.AllAppsScrim
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:id="@+id/all_apps_scrim"
-            launcher:layout_ignoreInsets="true" />
+            android:id="@+id/all_apps_scrim" />
 
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index 2606b87..e3416ac 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -33,7 +33,7 @@
         android:scaleType="fitXY"
         android:src="@drawable/all_apps_divider"/>
 
-    <Switch
+    <com.android.launcher3.allapps.WorkModeSwitch
         android:id="@+id/work_mode_toggle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 92420a2..40ffffe 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -35,19 +35,8 @@
     <!-- Folders -->
     <dimen name="folder_preview_padding">2dp</dimen>
 
-    <!-- Page indicator -->
-    <dimen name="dynamic_grid_page_indicator_land_left_nav_bar_gutter_width">50dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_land_right_nav_bar_gutter_width">74dp</dimen>
-
     <!-- Hotseat -->
     <!-- Will be set to equal the hotseat icon size. -->
     <dimen name="dynamic_grid_hotseat_size">0dp</dimen>
-
-    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">50dp</dimen>
-    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_left_padding">44dp</dimen>
-    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
-
-    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">56dp</dimen>
-    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_left_padding">32dp</dimen>
-    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">6dp</dimen>
+    <dimen name="dynamic_grid_hotseat_side_padding">16dp</dimen>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 42fee80..4abdfd6 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -32,8 +32,6 @@
 
     <!-- Popup container -->
     <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
-    <color name="badge_color">#1DE9B6</color> <!-- Teal A400 -->
-    <color name="folder_badge_color">#1DE9B6</color> <!-- Teal A400 -->
 
     <color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
     <color name="legacy_icon_background">#FFFFFF</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 36eb34b..f53fe79 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,8 +22,6 @@
     <dimen name="dynamic_grid_edge_margin">8dp</dimen>
     <dimen name="dynamic_grid_min_page_indicator_size">24dp</dimen>
     <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_land_left_nav_bar_gutter_width">0dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_land_right_nav_bar_gutter_width">0dp</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>
@@ -42,13 +40,7 @@
     <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
     <dimen name="dynamic_grid_hotseat_size">80dp</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>
-    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_left_padding">0dp</dimen>
-    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_left_padding">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
     <!-- Hotseat/all-apps scrim -->
     <dimen name="all_apps_scrim_radius">10dp</dimen>
@@ -93,7 +85,6 @@
     <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
     <dimen name="all_apps_background_canvas_height">475dp</dimen>
-    <dimen name="all_apps_caret_workspace_offset">18dp</dimen>
     <dimen name="all_apps_header_tab_height">50dp</dimen>
     <dimen name="all_apps_tabs_indicator_height">2dp</dimen>
     <dimen name="all_apps_header_top_padding">36dp</dimen>
@@ -212,10 +203,6 @@
     <!-- (touch_size - icon_size) / 2 -->
     <dimen name="system_shortcut_header_icon_padding">12dp</dimen>
 
-<!-- Icon badges (with notification counts) -->
-    <dimen name="badge_small_padding">0dp</dimen>
-    <dimen name="badge_large_padding">3dp</dimen>
-
 <!-- Notifications -->
     <dimen name="bg_round_rect_radius">12dp</dimen>
     <dimen name="notification_padding_start">16dp</dimen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 9a6be0b..824040a 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -155,6 +155,7 @@
 
     public static void closeAllOpenViews(Launcher launcher, boolean animate) {
         closeOpenViews(launcher, animate, TYPE_ALL);
+        launcher.finishAutoCancelActionMode();
     }
 
     public static void closeAllOpenViews(Launcher launcher) {
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 1e95333..7648e30 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -19,6 +19,7 @@
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.FocusLogic;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
     private static final int SNAP_DURATION = 150;
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 162aa08..9775955 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -29,6 +29,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Build.VERSION;
 import android.os.Bundle;
 import android.os.Process;
 import android.text.TextUtils;
@@ -438,7 +439,7 @@
             // Auto installs should always support the current platform version.
             mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
                     LauncherIcons.createBadgedIconBitmap(
-                            icon, Process.myUserHandle(), mContext, Build.VERSION.SDK_INT)));
+                            icon, Process.myUserHandle(), mContext, VERSION.SDK_INT).icon));
             mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
             mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a590504..46d7227 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -88,7 +88,7 @@
 
     private BadgeInfo mBadgeInfo;
     private BadgeRenderer mBadgeRenderer;
-    private IconPalette mBadgePalette;
+    private int mBadgeColor;
     private float mBadgeScale;
     private boolean mForceHideBadge;
     private Point mTempSpaceForBadgeOffset = new Point();
@@ -183,7 +183,7 @@
      */
     public void reset() {
         mBadgeInfo = null;
-        mBadgePalette = null;
+        mBadgeColor = Color.TRANSPARENT;
         mBadgeScale = 0f;
         mForceHideBadge = false;
     }
@@ -193,7 +193,7 @@
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
-        applyIconAndLabel(info.iconBitmap, info);
+        applyIconAndLabel(info);
         setTag(info);
         if (promiseStateChanged || (info.hasPromiseIconUi())) {
             applyPromiseState(promiseStateChanged);
@@ -203,7 +203,7 @@
     }
 
     public void applyFromApplicationInfo(AppInfo info) {
-        applyIconAndLabel(info.iconBitmap, info);
+        applyIconAndLabel(info);
 
         // We don't need to check the info since it's not a ShortcutInfo
         super.setTag(info);
@@ -219,7 +219,7 @@
     }
 
     public void applyFromPackageItemInfo(PackageItemInfo info) {
-        applyIconAndLabel(info.iconBitmap, info);
+        applyIconAndLabel(info);
         // We don't need to check the info since it's not a ShortcutInfo
         super.setTag(info);
 
@@ -227,8 +227,10 @@
         verifyHighRes();
     }
 
-    private void applyIconAndLabel(Bitmap icon, ItemInfo info) {
-        FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(icon, info);
+    private void applyIconAndLabel(ItemInfoWithIcon info) {
+        FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
+        mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
+
         iconDrawable.setIsDisabled(info.isDisabled());
         setIcon(iconDrawable);
         setText(info.title);
@@ -401,7 +403,7 @@
             final int scrollX = getScrollX();
             final int scrollY = getScrollY();
             canvas.translate(scrollX, scrollY);
-            mBadgeRenderer.draw(canvas, mBadgePalette, mBadgeInfo, mTempIconBounds, mBadgeScale,
+            mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
                     mTempSpaceForBadgeOffset);
             canvas.translate(-scrollX, -scrollY);
         }
@@ -532,7 +534,7 @@
                     preloadDrawable.setLevel(progressLevel);
                 } else {
                     preloadDrawable = DrawableFactory.get(getContext())
-                            .newPendingIcon(info.iconBitmap, getContext());
+                            .newPendingIcon(info, getContext());
                     preloadDrawable.setLevel(progressLevel);
                     setIcon(preloadDrawable);
                 }
@@ -550,10 +552,6 @@
             float newBadgeScale = isBadged ? 1f : 0;
             mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
             if (wasBadged || isBadged) {
-                mBadgePalette = IconPalette.getBadgePalette(getResources());
-                if (mBadgePalette == null) {
-                    mBadgePalette = ((FastBitmapDrawable) mIcon).getIconPalette();
-                }
                 // Animate when a badge is first added or when it is removed.
                 if (animate && (wasBadged ^ isBadged) && isShown()) {
                     ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
@@ -565,10 +563,6 @@
         }
     }
 
-    public IconPalette getBadgePalette() {
-        return mBadgePalette;
-    }
-
     /**
      * Sets the icon for this view based on the layout direction.
      */
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 01b6ca9..5e4f670 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -60,6 +60,7 @@
 import com.android.launcher3.util.ParcelableSparseArray;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -846,10 +847,6 @@
         return super.verifyDrawable(who) || (who == mBackground);
     }
 
-    public void setShortcutAndWidgetAlpha(float alpha) {
-        mShortcutsAndWidgets.setAlpha(alpha);
-    }
-
     public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
         return mShortcutsAndWidgets;
     }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7b3da67..52b1249 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -80,9 +80,6 @@
 
     // Page indicator
     private int pageIndicatorSizePx;
-    private final int pageIndicatorLandLeftNavBarGutterPx;
-    private final int pageIndicatorLandRightNavBarGutterPx;
-    private final int pageIndicatorLandWorkspaceOffsetPx;
 
     // Workspace icons
     public int iconSizePx;
@@ -114,12 +111,7 @@
     public int hotseatBarSizePx;
     public int hotseatBarTopPaddingPx;
     public int hotseatBarBottomPaddingPx;
-
-    public int hotseatBarLeftNavBarLeftPaddingPx;
-    public int hotseatBarLeftNavBarRightPaddingPx;
-
-    public int hotseatBarRightNavBarLeftPaddingPx;
-    public int hotseatBarRightNavBarRightPaddingPx;
+    public int hotseatBarSidePaddingPx;
 
     // All apps
     public int allAppsCellHeightPx;
@@ -181,12 +173,6 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
         pageIndicatorSizePx = res.getDimensionPixelSize(
                 R.dimen.dynamic_grid_min_page_indicator_size);
-        pageIndicatorLandLeftNavBarGutterPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_page_indicator_land_left_nav_bar_gutter_width);
-        pageIndicatorLandRightNavBarGutterPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_page_indicator_land_right_nav_bar_gutter_width);
-        pageIndicatorLandWorkspaceOffsetPx =
-                res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
         defaultPageSpacingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
         topWorkspacePadding =
@@ -203,14 +189,8 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
-        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);
-        hotseatBarLeftNavBarLeftPaddingPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_hotseat_land_left_nav_bar_left_padding);
-        hotseatBarRightNavBarLeftPaddingPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_hotseat_land_right_nav_bar_left_padding);
+        hotseatBarSidePaddingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         hotseatBarSizePx = isVerticalBarLayout()
                 ? Utilities.pxFromDp(inv.iconSize, dm)
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
@@ -248,7 +228,7 @@
         computeAllAppsButtonSize(context);
 
         // This is done last, after iconSizePx is calculated above.
-        mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
+        mBadgeRenderer = new BadgeRenderer(iconSizePx);
     }
 
     DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
@@ -445,10 +425,9 @@
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
-                int width = getCurrentWidth();
                 // XXX: If the icon size changes across orientations, we will have to take
                 //      that into account here too.
-                gap = ((width - 2 * edgeMarginPx
+                gap = ((widthPx - 2 * edgeMarginPx
                         - (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)))
                         + edgeMarginPx;
             } else {
@@ -481,33 +460,28 @@
     public Rect getWorkspacePadding(Rect recycle) {
         Rect padding = recycle == null ? new Rect() : recycle;
         if (isVerticalBarLayout()) {
-            if (mInsets.left > 0) {
-                padding.set(mInsets.left + pageIndicatorLandLeftNavBarGutterPx,
-                        0,
-                        hotseatBarSizePx + hotseatBarLeftNavBarRightPaddingPx
-                                + hotseatBarLeftNavBarLeftPaddingPx
-                                - mInsets.left,
-                        edgeMarginPx);
+            padding.top = 0;
+            padding.bottom = edgeMarginPx;
+            padding.left = hotseatBarSidePaddingPx;
+            padding.right = hotseatBarSidePaddingPx;
+            if (mInsets.left > mInsets.right) {
+                padding.left += hotseatBarSizePx;
+                padding.right += pageIndicatorSizePx;
             } else {
-                padding.set(pageIndicatorLandRightNavBarGutterPx,
-                        0,
-                        hotseatBarSizePx + hotseatBarRightNavBarRightPaddingPx
-                                + hotseatBarRightNavBarLeftPaddingPx,
-                        edgeMarginPx);
+                padding.left += pageIndicatorSizePx;
+                padding.right += hotseatBarSizePx;
             }
         } else {
             int paddingBottom = hotseatBarSizePx + pageIndicatorSizePx;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
-                int width = getCurrentWidth();
-                int height = getCurrentHeight();
                 // The amount of screen space available for left/right padding.
-                int availablePaddingX = Math.max(0, width - ((inv.numColumns * cellWidthPx) +
+                int availablePaddingX = Math.max(0, widthPx - ((inv.numColumns * cellWidthPx) +
                         ((inv.numColumns - 1) * cellWidthPx)));
                 availablePaddingX = (int) Math.min(availablePaddingX,
-                            width * MAX_HORIZONTAL_PADDING_PERCENT);
-                int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
+                            widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
+                int availablePaddingY = Math.max(0, heightPx - topWorkspacePadding - paddingBottom
                         - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
                         - hotseatBarBottomPaddingPx);
                 padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
@@ -575,6 +549,12 @@
         return isVerticalBarLayout() || isLargeTablet;
     }
 
+    private static void setLayoutGravity(View v, int gravity) {
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+        lp.gravity = gravity;
+        v.setLayoutParams(lp);
+    }
+
     public void layout(Launcher launcher, boolean notifyListeners) {
         FrameLayout.LayoutParams lp;
         boolean hasVerticalBarLayout = isVerticalBarLayout();
@@ -604,31 +584,30 @@
         // icons in the hotseat are a different size, and so don't line up perfectly. To account for
         // this, we pad the left and right of the hotseat with half of the difference of a workspace
         // cell vs a hotseat cell.
-        float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns;
-        float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons;
+        float workspaceCellWidth = (float) widthPx / inv.numColumns;
+        float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons;
         int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
         float scrimMargin = launcher.getResources().getDimension(R.dimen.all_apps_scrim_margin);
 
         if (hasVerticalBarLayout) {
-            // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
-            //                     screen regardless of RTL
-            int paddingRight = mInsets.left > 0
-                    ? hotseatBarLeftNavBarRightPaddingPx
-                    : hotseatBarRightNavBarRightPaddingPx;
-            int paddingLeft = mInsets.left > 0
-                    ? hotseatBarLeftNavBarLeftPaddingPx
-                    : hotseatBarRightNavBarLeftPaddingPx;
-
-            lp.gravity = Gravity.RIGHT;
-            lp.width = hotseatBarSizePx + mInsets.left + mInsets.right
-                    + paddingLeft + paddingRight;
+            // Vertical hotseat, on left or right based on the insets
             lp.height = LayoutParams.MATCH_PARENT;
+            if (mInsets.left > mInsets.right) {
+                lp.gravity = Gravity.LEFT;
+                lp.width = hotseatBarSizePx + mInsets.left + hotseatBarSidePaddingPx;
+                hotseat.getLayout().setPadding(
+                        mInsets.left, mInsets.top, hotseatBarSidePaddingPx, mInsets.bottom);
 
-            hotseat.getLayout().setPadding(mInsets.left + cellLayoutPaddingLeftRightPx
-                            + paddingLeft,
-                    mInsets.top,
-                    mInsets.right + cellLayoutPaddingLeftRightPx + paddingRight,
-                    workspacePadding.bottom + cellLayoutBottomPaddingPx);
+                setLayoutGravity(launcher.getDropTargetBar(), Gravity.RIGHT);
+            } else {
+                lp.gravity = Gravity.RIGHT;
+                lp.width = hotseatBarSizePx + mInsets.right + hotseatBarSidePaddingPx;
+                hotseat.getLayout().setPadding(
+                        hotseatBarSidePaddingPx, mInsets.top, mInsets.right, mInsets.bottom);
+
+                setLayoutGravity(launcher.getDropTargetBar(), Gravity.LEFT);
+            }
+
         } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
             lp.gravity = Gravity.BOTTOM;
@@ -661,11 +640,15 @@
         View pageIndicator = launcher.findViewById(R.id.page_indicator);
         if (pageIndicator != null) {
             lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
-            if (isVerticalBarLayout()) {
-                if (mInsets.left > 0) {
-                    lp.leftMargin = mInsets.left;
+            if (hasVerticalBarLayout) {
+                if (mInsets.left > mInsets.right) {
+                    lp.leftMargin = hotseatBarSidePaddingPx;
+                    lp.rightMargin = mInsets.right;
+                    lp.gravity =  Gravity.RIGHT | Gravity.BOTTOM;
                 } else {
-                    lp.leftMargin = pageIndicatorLandWorkspaceOffsetPx;
+                    lp.leftMargin = mInsets.left;
+                    lp.rightMargin = hotseatBarSidePaddingPx;
+                    lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
                 }
                 lp.bottomMargin = workspacePadding.bottom;
             } else {
@@ -689,18 +672,6 @@
         }
     }
 
-    public int getCurrentWidth() {
-        return isLandscape
-                ? Math.max(widthPx, heightPx)
-                : Math.min(widthPx, heightPx);
-    }
-
-    public int getCurrentHeight() {
-        return isLandscape
-                ? Math.min(widthPx, heightPx)
-                : Math.max(widthPx, heightPx);
-    }
-
     public int getCellHeight(@ContainerType int containerType) {
         switch (containerType) {
             case CellLayout.WORKSPACE:
@@ -715,20 +686,6 @@
         }
     }
 
-    /**
-     * @return the left/right paddings for all containers.
-     */
-    public final int[] getContainerPadding() {
-        // No paddings for portrait phone
-        if (isPhone && !isVerticalBarLayout()) {
-            return new int[] {0, 0};
-        }
-
-        // In landscape, we match the width of the workspace
-        Rect padding = getWorkspacePadding(null);
-        return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
-    }
-
     public boolean inMultiWindowMode() {
         return this != inv.landscapeProfile && this != inv.portraitProfile;
     }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 1272e0a..bd19dfa 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -32,7 +32,7 @@
 import android.util.Property;
 import android.util.SparseArray;
 
-import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.BitmapInfo;
 
 public class FastBitmapDrawable extends Drawable {
 
@@ -40,19 +40,9 @@
     private static final float DISABLED_DESATURATION = 1f;
     private static final float DISABLED_BRIGHTNESS = 0.5f;
 
-    public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
+    public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = (input) ->
+            (input < 0.05f) ? (input / 0.05f) : ((input < 0.3f) ? 1 : (1 - input) / 0.7f);
 
-        @Override
-        public float getInterpolation(float input) {
-            if (input < 0.05f) {
-                return input / 0.05f;
-            } else if (input < 0.3f){
-                return 1;
-            } else {
-                return (1 - input) / 0.7f;
-            }
-        }
-    };
     public static final int CLICK_FEEDBACK_DURATION = 2000;
 
     // Since we don't need 256^2 values for combinations of both the brightness and saturation, we
@@ -69,12 +59,11 @@
 
     protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
     private final Bitmap mBitmap;
+    protected final int mIconColor;
 
     private boolean mIsPressed;
     private boolean mIsDisabled;
 
-    private IconPalette mIconPalette;
-
     private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
             = new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
         @Override
@@ -99,7 +88,20 @@
     private ObjectAnimator mBrightnessAnimator;
 
     public FastBitmapDrawable(Bitmap b) {
+        this(b, Color.TRANSPARENT);
+    }
+
+    public FastBitmapDrawable(BitmapInfo info) {
+        this(info.icon, info.color);
+    }
+
+    public FastBitmapDrawable(ItemInfoWithIcon info) {
+        this(info.iconBitmap, info.iconColor);
+    }
+
+    protected FastBitmapDrawable(Bitmap b, int iconColor) {
         mBitmap = b;
+        mIconColor = iconColor;
         setFilterBitmap(true);
     }
 
@@ -108,14 +110,6 @@
         canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
     }
 
-    public IconPalette getIconPalette() {
-        if (mIconPalette == null) {
-            mIconPalette = IconPalette.fromDominantColor(Utilities
-                    .findDominantColorByHue(mBitmap, 20), true /* desaturateBackground */);
-        }
-        return mIconPalette;
-    }
-
     @Override
     public void setColorFilter(ColorFilter cf) {
         // No op
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 20a6be2..490feda 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
 
 import android.content.Context;
 import android.graphics.Rect;
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index ab853e5..baa60b0 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -33,18 +33,21 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
+import android.os.Build.VERSION;
 import android.os.Handler;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.support.annotation.NonNull;
+import android.support.v4.graphics.ColorUtils;
 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;
+import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.graphics.ColorExtractor;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.uioverrides.UiFactory;
@@ -81,14 +84,13 @@
 
     @Thunk static final Object ICON_UPDATE_TOKEN = new Object();
 
-    public static class CacheEntry {
-        public Bitmap icon;
+    public static class CacheEntry extends BitmapInfo {
         public CharSequence title = "";
         public CharSequence contentDescription = "";
         public boolean isLowResIcon;
     }
 
-    private final HashMap<UserHandle, Bitmap> mDefaultIcons = new HashMap<>();
+    private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
     @Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
 
     private final Context mContext;
@@ -190,9 +192,9 @@
         return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
     }
 
-    protected Bitmap makeDefaultIcon(UserHandle user) {
+    protected BitmapInfo makeDefaultIcon(UserHandle user) {
         Drawable unbadged = getFullResDefaultActivityIcon();
-        return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, Build.VERSION_CODES.O);
+        return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, VERSION.SDK_INT);
     }
 
     /**
@@ -376,16 +378,16 @@
         }
         if (entry == null) {
             entry = new CacheEntry();
-            entry.icon = LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
-                    mContext,  app.getApplicationInfo().targetSdkVersion);
+            LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
+                    mContext,  app.getApplicationInfo().targetSdkVersion).applyTo(entry);
         }
         entry.title = app.getLabel();
         entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
         mCache.put(key, entry);
 
         Bitmap lowResIcon = generateLowResIcon(entry.icon);
-        ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
-                app.getApplicationInfo().packageName);
+        ContentValues values = newContentValues(entry.icon, lowResIcon, entry.color,
+                entry.title.toString(), app.getApplicationInfo().packageName);
         addIconToDB(values, app.getComponentName(), info, userSerial);
     }
 
@@ -459,7 +461,7 @@
         // null info means not installed, but if we have a component from the intent then
         // we should still look in the cache for restored app icons.
         if (info.getTargetComponent() == null) {
-            info.iconBitmap = getDefaultIcon(info.user);
+            getDefaultIcon(info.user).applyTo(info);
             info.title = "";
             info.contentDescription = "";
             info.usingLowResIcon = false;
@@ -494,11 +496,11 @@
     private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
         info.title = Utilities.trim(entry.title);
         info.contentDescription = entry.contentDescription;
-        info.iconBitmap = entry.icon == null ? getDefaultIcon(info.user) : entry.icon;
         info.usingLowResIcon = entry.isLowResIcon;
+        ((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
     }
 
-    public synchronized Bitmap getDefaultIcon(UserHandle user) {
+    public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
         if (!mDefaultIcons.containsKey(user)) {
             mDefaultIcons.put(user, makeDefaultIcon(user));
         }
@@ -506,7 +508,7 @@
     }
 
     public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
-        return mDefaultIcons.get(user) == icon;
+        return getDefaultIcon(user).icon == icon;
     }
 
     /**
@@ -533,9 +535,9 @@
                 providerFetchedOnce = true;
 
                 if (info != null) {
-                    entry.icon = LauncherIcons.createBadgedIconBitmap(
+                    LauncherIcons.createBadgedIconBitmap(
                             getFullResIcon(info), info.getUser(), mContext,
-                            infoProvider.get().getApplicationInfo().targetSdkVersion);
+                            info.getApplicationInfo().targetSdkVersion).applyTo(entry);
                 } else {
                     if (usePackageIcon) {
                         CacheEntry packageEntry = getEntryForPackageLocked(
@@ -543,7 +545,7 @@
                         if (packageEntry != null) {
                             if (DEBUG) Log.d(TAG, "using package default icon for " +
                                     componentName.toShortString());
-                            entry.icon = packageEntry.icon;
+                            packageEntry.applyTo(entry);
                             entry.title = packageEntry.title;
                             entry.contentDescription = packageEntry.contentDescription;
                         }
@@ -551,7 +553,7 @@
                     if (entry.icon == null) {
                         if (DEBUG) Log.d(TAG, "using default icon for " +
                                 componentName.toShortString());
-                        entry.icon = getDefaultIcon(user);
+                        getDefaultIcon(user).applyTo(entry);
                     }
                 }
             }
@@ -594,7 +596,7 @@
             entry.title = title;
         }
         if (icon != null) {
-            entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
+            LauncherIcons.createIconBitmap(icon, mContext).applyTo(entry);
         }
         if (!TextUtils.isEmpty(title) && entry.icon != null) {
             mCache.put(cacheKey, entry);
@@ -633,22 +635,23 @@
 
                     // Load the full res icon for the application, but if useLowResIcon is set, then
                     // only keep the low resolution icon instead of the larger full-sized icon
-                    Bitmap icon = LauncherIcons.createBadgedIconBitmap(
+                    BitmapInfo iconInfo = LauncherIcons.createBadgedIconBitmap(
                             appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
                     if (mInstantAppResolver.isInstantApp(appInfo)) {
-                        LauncherIcons.badgeWithDrawable(icon,
+                        LauncherIcons.badgeWithDrawable(iconInfo.icon,
                                 mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
                     }
-                    Bitmap lowResIcon =  generateLowResIcon(icon);
+                    Bitmap lowResIcon =  generateLowResIcon(iconInfo.icon);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
-                    entry.icon = useLowResIcon ? lowResIcon : icon;
+                    entry.icon = useLowResIcon ? lowResIcon : iconInfo.icon;
+                    entry.color = iconInfo.color;
                     entry.isLowResIcon = useLowResIcon;
 
                     // Add the icon in the DB here, since these do not get written during
                     // package updates.
-                    ContentValues values =
-                            newContentValues(icon, lowResIcon, entry.title.toString(), packageName);
+                    ContentValues values = newContentValues(iconInfo.icon, lowResIcon, entry.color,
+                            entry.title.toString(), packageName);
                     addIconToDB(values, cacheKey.componentName, info,
                             mUserManager.getSerialNumberForUser(user));
 
@@ -671,14 +674,16 @@
         try {
             c = mIconDb.query(
                 new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
-                        IconDB.COLUMN_LABEL},
+                        IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL},
                 IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
                 new String[]{cacheKey.componentName.flattenToString(),
                         Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
             if (c.moveToNext()) {
                 entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
+                // Set the alpha to be 255, so that we never have a wrong color
+                entry.color = ColorUtils.setAlphaComponent(c.getInt(1), 255);
                 entry.isLowResIcon = lowRes;
-                entry.title = c.getString(1);
+                entry.title = c.getString(2);
                 if (entry.title == null) {
                     entry.title = "";
                     entry.contentDescription = "";
@@ -771,7 +776,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 17;
+        private final static int DB_VERSION = 18;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -784,6 +789,7 @@
         private final static String COLUMN_VERSION = "version";
         private final static String COLUMN_ICON = "icon";
         private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
+        private final static String COLUMN_ICON_COLOR = "icon_color";
         private final static String COLUMN_LABEL = "label";
         private final static String COLUMN_SYSTEM_STATE = "system_state";
 
@@ -802,6 +808,7 @@
                     COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
                     COLUMN_ICON + " BLOB, " +
                     COLUMN_ICON_LOW_RES + " BLOB, " +
+                    COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
                     COLUMN_LABEL + " TEXT, " +
                     COLUMN_SYSTEM_STATE + " TEXT, " +
                     "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
@@ -809,11 +816,12 @@
         }
     }
 
-    private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label,
-            String packageName) {
+    private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, int iconColor,
+            String label, String packageName) {
         ContentValues values = new ContentValues();
         values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
         values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
+        values.put(IconDB.COLUMN_ICON_COLOR, iconColor);
 
         values.put(IconDB.COLUMN_LABEL, label);
         values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df1eec6..c476421 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -40,6 +40,7 @@
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.BitmapInfo;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -480,7 +481,7 @@
                 final LauncherAppState app = LauncherAppState.getInstance(mContext);
                 // Set default values until proper values is loaded.
                 appInfo.title = "";
-                appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
+                app.getIconCache().getDefaultIcon(user).applyTo(appInfo);
                 final ShortcutInfo si = appInfo.makeShortcut();
                 if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
                     app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
@@ -497,7 +498,7 @@
                 return Pair.create((ItemInfo) si, (Object) activityInfo);
             } else if (shortcutInfo != null) {
                 ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
-                si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
+                LauncherIcons.createShortcutIcon(shortcutInfo, mContext).applyTo(si);
                 return Pair.create((ItemInfo) si, (Object) shortcutInfo);
             } else if (providerInfo != null) {
                 LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -641,18 +642,20 @@
         // users wouldn't get here without intent forwarding anyway.
         info.user = Process.myUserHandle();
 
+        BitmapInfo iconInfo = null;
         if (bitmap instanceof Bitmap) {
-            info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
+            iconInfo = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
         } else {
             Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
             if (extra instanceof Intent.ShortcutIconResource) {
                 info.iconResource = (Intent.ShortcutIconResource) extra;
-                info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
+                iconInfo = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
             }
         }
-        if (info.iconBitmap == null) {
-            info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
+        if (iconInfo == null) {
+            iconInfo = app.getIconCache().getDefaultIcon(info.user);
         }
+        iconInfo.applyTo(info);
 
         info.title = Utilities.trim(name);
         info.contentDescription = UserManagerCompat.getInstance(app.getContext())
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index fea4dda..bf985c3 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -29,6 +29,11 @@
     public Bitmap iconBitmap;
 
     /**
+     * Dominant color in the {@link #iconBitmap}.
+     */
+    public int iconColor;
+
+    /**
      * Indicates whether we're using a low res icon
      */
     public boolean usingLowResIcon;
@@ -96,6 +101,7 @@
     protected ItemInfoWithIcon(ItemInfoWithIcon info) {
         super(info);
         iconBitmap = info.iconBitmap;
+        iconColor = info.iconColor;
         usingLowResIcon = info.usingLowResIcon;
         runtimeStatusFlags = info.runtimeStatusFlags;
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 44660ec..adfb44c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -29,8 +29,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
 
 import android.Manifest;
 import android.animation.Animator;
@@ -72,6 +70,7 @@
 import android.text.method.TextKeyListener;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.ActionMode;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
@@ -127,15 +126,16 @@
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
-import com.android.launcher3.util.RunnableWithId;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetListRowEntry;
@@ -198,6 +198,10 @@
     // Type: SparseArray<Parcelable>
     private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
 
+    // When starting an action mode, setting this tag will cause the action mode to be cancelled
+    // automatically when user interacts with the launcher.
+    public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
     private LauncherStateManager mStateManager;
 
     private boolean mIsSafeModeEnabled;
@@ -236,10 +240,6 @@
 
     @Thunk boolean mWorkspaceLoading = true;
 
-    private boolean mPaused = true;
-    private boolean mOnResumeNeedsLoad;
-
-    private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
     private OnResumeCallback mOnResumeCallback;
 
     private ViewOnDrawExecutor mPendingExecutor;
@@ -275,6 +275,7 @@
     private boolean mAppLaunchSuccess;
 
     private RotationPrefChangeHandler mRotationPrefChangeHandler;
+    private ActionMode mCurrentActionMode;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -333,11 +334,6 @@
         mAppWidgetHost = new LauncherAppWidgetHost(this);
         mAppWidgetHost.startListening();
 
-        // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
-        // this also ensures that any synchronous binding below doesn't re-trigger another
-        // LauncherModel load.
-        mPaused = false;
-
         mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
 
         setupViews();
@@ -777,10 +773,7 @@
             mLauncherCallbacks.onStart();
         }
         mAppWidgetHost.setListenIfResumed(true);
-
-        if (!isWorkspaceLoading()) {
-            NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
-        }
+        NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
         if (mShouldFadeInScrim && mDragLayer.getBackground() != null) {
             if (mScrimAnimator != null) {
@@ -810,21 +803,6 @@
 
         mAppLaunchSuccess = false;
         getUserEventDispatcher().resetElapsedSessionMillis();
-        mPaused = false;
-        if (mOnResumeNeedsLoad) {
-            setWorkspaceLoading(true);
-            mModel.startLoader(getCurrentWorkspaceScreen());
-            mOnResumeNeedsLoad = false;
-        }
-        if (mBindOnResumeCallbacks.size() > 0) {
-            // We might have postponed some bind calls until onResume (see waitUntilResume) --
-            // execute them here
-            for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
-                mBindOnResumeCallbacks.get(i).run();
-            }
-            mBindOnResumeCallbacks.clear();
-        }
-
         setOnResumeCallback(null);
         // Process any items that were added while Launcher was away.
         InstallShortcutReceiver.disableAndFlushInstallQueue(
@@ -847,7 +825,6 @@
         InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
 
         super.onPause();
-        mPaused = true;
         mDragController.cancelDrag();
         mDragController.resetLastGestureUpTime();
 
@@ -1161,20 +1138,12 @@
     };
 
     public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                mWorkspace.updateIconBadges(updatedBadges);
-                mAppsView.updateIconBadges(updatedBadges);
+        mWorkspace.updateIconBadges(updatedBadges);
+        mAppsView.updateIconBadges(updatedBadges);
 
-                PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
-                if (popup != null) {
-                    popup.updateNotificationHeader(updatedBadges);
-                }
-            }
-        };
-        if (!waitUntilResume(r)) {
-            r.run();
+        PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
+        if (popup != null) {
+            popup.updateNotificationHeader(updatedBadges);
         }
     }
 
@@ -1626,6 +1595,9 @@
 
     @Override
     public void onBackPressed() {
+        if (finishAutoCancelActionMode()) {
+            return;
+        }
         if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
             return;
         }
@@ -2156,33 +2128,6 @@
         return result;
     }
 
-    /**
-     * If the activity is currently paused, signal that we need to run the passed Runnable
-     * in onResume.
-     *
-     * This needs to be called from incoming places where resources might have been loaded
-     * while the activity is paused. That is because the Configuration (e.g., rotation)  might be
-     * wrong when we're not running, and if the activity comes back to what the configuration was
-     * when we were paused, activity is not restarted.
-     *
-     * Implementation of the method from LauncherModel.Callbacks.
-     *
-     * @return {@code true} if we are currently paused. The caller might be able to skip some work
-     */
-    @Thunk boolean waitUntilResume(Runnable run) {
-        if (mPaused) {
-            if (LOGD) Log.d(TAG, "Deferring update until onResume");
-            if (run instanceof RunnableWithId) {
-                // Remove any runnables which have the same id
-                while (mBindOnResumeCallbacks.remove(run)) { }
-            }
-            mBindOnResumeCallbacks.add(run);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     public void setOnResumeCallback(OnResumeCallback callback) {
         if (mOnResumeCallback != null) {
             mOnResumeCallback.onLauncherResume();
@@ -2191,31 +2136,6 @@
     }
 
     /**
-     * If the activity is currently paused, signal that we need to re-run the loader
-     * in onResume.
-     *
-     * This needs to be called from incoming places where resources might have been loaded
-     * while we are paused.  That is becaues the Configuration might be wrong
-     * when we're not running, and if it comes back to what it was when we
-     * were paused, we are not restarted.
-     *
-     * Implementation of the method from LauncherModel.Callbacks.
-     *
-     * @return true if we are currently paused.  The caller might be able to
-     * skip some work in that case since we will come back again.
-     */
-    @Override
-    public boolean setLoadOnResume() {
-        if (mPaused) {
-            if (LOGD) Log.d(TAG, "setLoadOnResume");
-            mOnResumeNeedsLoad = true;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
      * Implementation of the method from LauncherModel.Callbacks.
      */
     @Override
@@ -2233,7 +2153,6 @@
      */
     @Override
     public void clearPendingBinds() {
-        mBindOnResumeCallbacks.clear();
         if (mPendingExecutor != null) {
             mPendingExecutor.markCompleted();
             mPendingExecutor = null;
@@ -2297,18 +2216,8 @@
     }
 
     @Override
-    public void bindAppsAdded(final ArrayList<Long> newScreens,
-                              final ArrayList<ItemInfo> addNotAnimated,
-                              final ArrayList<ItemInfo> addAnimated) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindAppsAdded(newScreens, addNotAnimated, addAnimated);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
+    public void bindAppsAdded(ArrayList<Long> newScreens, ArrayList<ItemInfo> addNotAnimated,
+            ArrayList<ItemInfo> addAnimated) {
         // Add the new screens
         if (newScreens != null) {
             bindAddScreens(newScreens);
@@ -2334,15 +2243,6 @@
      */
     @Override
     public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindItems(items, forceAnimateIcons);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
         // 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<>();
@@ -2585,7 +2485,7 @@
         }
 
         if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) {
-            view.reinflate();
+            view.reInflate();
         }
 
         getModelWriter().updateItemInDatabase(info);
@@ -2613,27 +2513,11 @@
 
     @Override
     public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
-        Runnable r = new Runnable() {
-            public void run() {
-                finishFirstPageBind(executor);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
-        Runnable onComplete = new Runnable() {
-            @Override
-            public void run() {
-                if (executor != null) {
-                    executor.onLoadAnimationCompleted();
-                }
-            }
-        };
         if (mDragLayer.getAlpha() < 1) {
-            mDragLayer.animate().alpha(1).withEndAction(onComplete).start();
-        } else {
-            onComplete.run();
+            mDragLayer.animate().alpha(1).withEndAction(
+                    executor == null ? null : executor::onLoadAnimationCompleted).start();
+        } else if (executor != null) {
+            executor.onLoadAnimationCompleted();
         }
     }
 
@@ -2643,10 +2527,6 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void finishBindingItems() {
-        Runnable r = this::finishBindingItems;
-        if (waitUntilResume(r)) {
-            return;
-        }
         TraceHelper.beginSection("finishBindingItems");
         mWorkspace.restoreInstanceStateForRemainingPages();
 
@@ -2661,7 +2541,6 @@
         InstallShortcutReceiver.disableAndFlushInstallQueue(
                 InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
 
-        NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
         TraceHelper.endSection("finishBindingItems");
     }
 
@@ -2687,21 +2566,12 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAllApplications(final ArrayList<AppInfo> apps) {
-        Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
-            public void run() {
-                bindAllApplications(apps);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
+    public void bindAllApplications(ArrayList<AppInfo> apps) {
         if (mAppsView != null) {
             Executor pendingExecutor = getPendingExecutor();
             if (pendingExecutor != null && !isInState(ALL_APPS)) {
                 // Wait until the fade in animation has finished before setting all apps list.
-                pendingExecutor.execute(r);
+                pendingExecutor.execute(() -> bindAllApplications(apps));
                 return;
             }
 
@@ -2734,47 +2604,22 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAppsAddedOrUpdated(final ArrayList<AppInfo> apps) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindAppsAddedOrUpdated(apps);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
+    @Override
+    public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
         if (mAppsView != null) {
             mAppsView.addOrUpdateApps(apps);
         }
     }
 
     @Override
-    public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindPromiseAppProgressUpdated(app);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
+    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
         if (mAppsView != null) {
             mAppsView.updatePromiseAppProgress(app);
         }
     }
 
     @Override
-    public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindWidgetsRestored(widgets);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
+    public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) {
         mWorkspace.widgetsRestored(widgets);
     }
 
@@ -2785,16 +2630,7 @@
      * @param updated list of shortcuts which have changed.
      */
     @Override
-    public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, final UserHandle user) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindShortcutsChanged(updated, user);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
+    public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, final UserHandle user) {
         if (!updated.isEmpty()) {
             mWorkspace.updateShortcuts(updated);
         }
@@ -2806,16 +2642,7 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     @Override
-    public void bindRestoreItemsChange(final HashSet<ItemInfo> updates) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindRestoreItemsChange(updates);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
+    public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
         mWorkspace.updateRestoreItems(updates);
     }
 
@@ -2828,29 +2655,12 @@
      */
     @Override
     public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindWorkspaceComponentsRemoved(matcher);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
         mWorkspace.removeItemsByMatcher(matcher);
         mDragController.onAppsRemoved(matcher);
     }
 
     @Override
     public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindAppInfosRemoved(appInfos);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
         // Update AllApps
         if (mAppsView != null) {
             mAppsView.removeApps(appInfos);
@@ -2860,16 +2670,6 @@
     @Override
     public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
         mPopupDataProvider.setAllWidgets(allWidgets);
-        Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
-            @Override
-            public void run() {
-                bindAllWidgets(allWidgets);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
         if (topView != null) {
             topView.onWidgetsBound();
@@ -3015,6 +2815,26 @@
         }
     }
 
+    @Override
+    public void onActionModeStarted(ActionMode mode) {
+        super.onActionModeStarted(mode);
+        mCurrentActionMode = mode;
+    }
+
+    @Override
+    public void onActionModeFinished(ActionMode mode) {
+        super.onActionModeFinished(mode);
+        mCurrentActionMode = null;
+    }
+
+    public boolean finishAutoCancelActionMode() {
+        if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
+            mCurrentActionMode.finish();
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Callback for listening for onResume
      */
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 0136c70..a46692b 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -27,6 +27,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.Preconditions;
@@ -39,6 +40,8 @@
 
 public class LauncherAppState {
 
+    public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
+
     // We do not need any synchronization for this variable as its only written on UI thread.
     private static LauncherAppState INSTANCE;
 
@@ -103,6 +106,10 @@
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
 
+        if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            filter.addAction(ACTION_FORCE_ROLOAD);
+        }
+
         mContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(mContext).enableAndResetCache();
         new ConfigMonitor(mContext).register();
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 9aa74b3..7bc7139 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -26,11 +26,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.widget.Toast;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.widget.DeferredAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import java.util.ArrayList;
 
@@ -84,6 +87,14 @@
             // have been established by this point, and we will end up populating the
             // widgets upon bind anyway. See issue 14255011 for more context.
         }
+
+        // We go in reverse order and inflate any deferred widget
+        for (int i = mViews.size() - 1; i >= 0; i--) {
+            LauncherAppWidgetHostView view = mViews.valueAt(i);
+            if (view instanceof DeferredAppWidgetHostView) {
+                view.reInflate();
+            }
+        }
     }
 
     @Override
@@ -178,6 +189,11 @@
             inflater.inflate(appWidget.initialLayout, lahv);
             lahv.setAppWidget(0, appWidget);
             return lahv;
+        } else if ((mFlags & FLAG_LISTENING) == 0) {
+            DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+            view.setAppWidget(appWidgetId, appWidget);
+            mViews.put(appWidgetId, view);
+            return view;
         } else {
             try {
                 return super.createView(context, appWidgetId, appWidget);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 469c5f1..ea4b280 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
+import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProviderOperation;
@@ -37,6 +40,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.BgDataModel;
@@ -135,7 +139,6 @@
     };
 
     public interface Callbacks {
-        public boolean setLoadOnResume();
         public int getCurrentWorkspaceScreen();
         public void clearPendingBinds();
         public void startBinding();
@@ -405,6 +408,8 @@
                     enqueueModelUpdateTask(new UserLockStateChangedTask(user));
                 }
             }
+        } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+            forceReload();
         }
     }
 
@@ -419,25 +424,11 @@
             mModelLoaded = false;
         }
 
-        // Do this here because if the launcher activity is running it will be restarted.
-        // If it's not running startLoaderFromBackground will merely tell it that it needs
-        // to reload.
-        startLoaderFromBackground();
-    }
-
-    /**
-     * When the launcher is in the background, it's possible for it to miss paired
-     * configuration changes.  So whenever we trigger the loader from the background
-     * tell the launcher that it needs to re-run the loader when it comes back instead
-     * of doing it now.
-     */
-    public void startLoaderFromBackground() {
+        // Start the loader if launcher is already running, otherwise the loader will run,
+        // the next time launcher starts
         Callbacks callbacks = getCallback();
         if (callbacks != null) {
-            // Only actually run the loader if they're not paused.
-            if (!callbacks.setLoadOnResume()) {
-                startLoader(callbacks.getCurrentWorkspaceScreen());
-            }
+            startLoader(callbacks.getCurrentWorkspaceScreen());
         }
     }
 
@@ -629,7 +620,7 @@
             @Override
             public ShortcutInfo get() {
                 si.updateFromDeepShortcutInfo(info, mApp.getContext());
-                si.iconBitmap = LauncherIcons.createShortcutIcon(info, mApp.getContext());
+                LauncherIcons.createShortcutIcon(info, mApp.getContext()).applyTo(si);
                 return si;
             }
         });
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index a1f5879..4f65d19 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -55,7 +55,7 @@
     private static final LauncherState[] sAllStates = new LauncherState[4];
 
     public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
-            0, 1f, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED);
+            0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED);
 
     public static final LauncherState ALL_APPS = new AllAppsState(1);
 
@@ -95,13 +95,6 @@
     public final int transitionDuration;
 
     /**
-     * Fraction shift in the vertical translation UI and related properties
-     *
-     * @see com.android.launcher3.allapps.AllAppsTransitionController
-     */
-    public final float verticalProgress;
-
-    /**
      * True if the state allows workspace icons to be dragged.
      */
     public final boolean workspaceIconsCanBeDragged;
@@ -112,8 +105,7 @@
      */
     public final boolean disablePageClipping;
 
-    public LauncherState(int id, int containerType, int transitionDuration, float verticalProgress,
-            int flags) {
+    public LauncherState(int id, int containerType, int transitionDuration, int flags) {
         this.containerType = containerType;
         this.transitionDuration = transitionDuration;
 
@@ -126,8 +118,6 @@
         this.workspaceIconsCanBeDragged = (flags & FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED) != 0;
         this.disablePageClipping = (flags & FLAG_DISABLE_PAGE_CLIPPING) != 0;
 
-        this.verticalProgress = verticalProgress;
-
         this.ordinal = id;
         sAllStates[id] = this;
     }
@@ -154,6 +144,15 @@
         return launcher.getWorkspace();
     }
 
+    /**
+     * Fraction shift in the vertical translation UI and related properties
+     *
+     * @see com.android.launcher3.allapps.AllAppsTransitionController
+     */
+    public float getVerticalProgress(Launcher launcher) {
+        return 1f;
+    }
+
     public String getDescription(Launcher launcher) {
         return launcher.getWorkspace().getCurrentPageDescription();
     }
@@ -162,7 +161,7 @@
         if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
             return DEFAULT_ALPHA_PROVIDER;
         }
-        final int centerPage = launcher.getWorkspace().getPageNearestToCenterOfScreen();
+        final int centerPage = launcher.getWorkspace().getNextPage();
         return new PageAlphaProvider(ACCEL_2) {
             @Override
             public float getPageAlpha(int pageIndex) {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 0137b26..d25f958 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -264,6 +264,7 @@
     private void onStateTransitionEnd(LauncherState state) {
         mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
         mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+        mLauncher.finishAutoCancelActionMode();
     }
 
     /**
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 5ed722c..4c30853 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -68,7 +68,7 @@
     private static final int MIN_LENGTH_FOR_FLING = 25;
 
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
-    protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
+    public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
 
     // OverScroll constants
     private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -155,10 +155,6 @@
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
     protected PageIndicator mPageIndicator;
-    // The viewport whether the pages are to be contained (the actual view may be larger than the
-    // viewport)
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private Rect mViewport = new Rect();
 
     // Reordering
     // We use the min scale to determine how much to expand the actually PagedView measured
@@ -283,25 +279,6 @@
         }
     }
 
-    // Convenience methods to get the actual width/height of the PagedView (since it is measured
-    // to be larger to account for the minimum possible scale)
-    int getViewportWidth() {
-        return mViewport.width();
-    }
-    public int getViewportHeight() {
-        return mViewport.height();
-    }
-
-    // Convenience methods to get the offset ASSUMING that we are centering the pages in the
-    // PagedView both horizontally and vertically
-    int getViewportOffsetX() {
-        return (getMeasuredWidth() - getViewportWidth()) / 2;
-    }
-
-    int getViewportOffsetY() {
-        return (getMeasuredHeight() - getViewportHeight()) / 2;
-    }
-
     public PageIndicator getPageIndicator() {
         return mPageIndicator;
     }
@@ -587,12 +564,12 @@
     }
 
     public int getNormalChildHeight() {
-        return  getViewportHeight() - getPaddingTop() - getPaddingBottom()
+        return  getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
                 - mInsets.top - mInsets.bottom;
     }
 
     public int getNormalChildWidth() {
-        return  getViewportWidth() - getPaddingLeft() - getPaddingRight()
+        return  getMeasuredWidth() - getPaddingLeft() - getPaddingRight()
                 - mInsets.left - mInsets.right;
     }
 
@@ -610,8 +587,6 @@
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
 
-        mViewport.set(0, 0, widthSize, heightSize);
-
         if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
             return;
@@ -628,9 +603,9 @@
         if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
 
         int myWidthSpec = MeasureSpec.makeMeasureSpec(
-                getViewportWidth() - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
+                widthSize - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
         int myHeightSpec = MeasureSpec.makeMeasureSpec(
-                getViewportHeight() - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
+                heightSize - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
 
         // measureChildren takes accounts for content padding, we only need to care about extra
         // space due to insets.
@@ -648,19 +623,15 @@
         if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
         final int childCount = getChildCount();
 
-        int offsetX = getViewportOffsetX();
-        int offsetY = getViewportOffsetY();
-
-        // Update the viewport offsets
-        mViewport.offset(offsetX, offsetY);
-
         final int startIndex = mIsRtl ? childCount - 1 : 0;
         final int endIndex = mIsRtl ? -1 : childCount;
         final int delta = mIsRtl ? -1 : 1;
 
         int verticalPadding = getPaddingTop() + getPaddingBottom();
 
-        int childLeft = offsetX + getPaddingLeft();
+        int scrollOffsetLeft = mInsets.left + getPaddingLeft();
+        int childLeft = scrollOffsetLeft;
+
         if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
             mPageScrolls = new int[childCount];
         }
@@ -668,8 +639,8 @@
         for (int i = startIndex; i != endIndex; i += delta) {
             final View child = getPageAt(i);
             if (child.getVisibility() != View.GONE) {
-                int childTop = offsetY + getPaddingTop() + mInsets.top;
-                childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding
+                int childTop = getPaddingTop() + mInsets.top;
+                childTop += (getMeasuredHeight() - mInsets.top - mInsets.bottom - verticalPadding
                         - child.getMeasuredHeight()) / 2;
 
                 final int childWidth = child.getMeasuredWidth();
@@ -679,8 +650,7 @@
                 child.layout(childLeft, childTop,
                         childLeft + child.getMeasuredWidth(), childTop + childHeight);
 
-                int scrollOffsetLeft = getPaddingLeft();
-                mPageScrolls[i] = childLeft - scrollOffsetLeft - offsetX;
+                mPageScrolls[i] = childLeft - scrollOffsetLeft;
 
                 childLeft += childWidth + mPageSpacing + getChildGap();
             }
@@ -809,10 +779,7 @@
 
     protected int getChildOffset(int index) {
         if (index < 0 || index > getChildCount() - 1) return 0;
-
-        int offset = getPageAt(index).getLeft() - getViewportOffsetX();
-
-        return offset;
+        return getPageAt(index).getLeft();
     }
 
     @Override
@@ -930,32 +897,9 @@
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-    /**
-     * Return true if a tap at (x, y) should trigger a flip to the previous page.
-     */
-    protected boolean hitsPreviousPage(float x, float y) {
-        if (mIsRtl) {
-            return (x > (getViewportOffsetX() + getViewportWidth() -
-                    getPaddingRight() - mPageSpacing));
-        }
-        return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
-    }
-
-    /**
-     * Return true if a tap at (x, y) should trigger a flip to the next page.
-     */
-    protected boolean hitsNextPage(float x, float y) {
-        if (mIsRtl) {
-            return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
-        }
-        return  (x > (getViewportOffsetX() + getViewportWidth() -
-                getPaddingRight() - mPageSpacing));
-    }
-
     /** Returns whether x and y originated within the buffered viewport */
     private boolean isTouchPointInViewportWithBuffer(int x, int y) {
-        sTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
-                mViewport.right + mViewport.width() / 2, mViewport.bottom);
+        sTmpRect.set(-getMeasuredWidth() / 2, 0, 3 * getMeasuredWidth() / 2, getMeasuredHeight());
         return sTmpRect.contains(x, y);
     }
 
@@ -1105,7 +1049,7 @@
     }
 
     protected float getScrollProgress(int screenCenter, View v, int page) {
-        final int halfScreenSize = getViewportWidth() / 2;
+        final int halfScreenSize = getMeasuredWidth() / 2;
 
         int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
         int count = getChildCount();
@@ -1145,8 +1089,8 @@
         } else {
             View child = getChildAt(index);
 
-            int scrollOffset = scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft();
-            int baselineX = mPageScrolls[index] + scrollOffset + getViewportOffsetX();
+            int scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft();
+            int baselineX = mPageScrolls[index] + scrollOffset;
             return (int) (child.getX() - baselineX);
         }
     }
@@ -1154,7 +1098,7 @@
     protected void dampedOverScroll(float amount) {
         if (Float.compare(amount, 0f) == 0) return;
 
-        int overScrollAmount = OverScroll.dampedScroll(amount, getViewportWidth());
+        int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
         if (amount < 0) {
             mOverScrollX = overScrollAmount;
             super.scrollTo(mOverScrollX, getScrollY());
@@ -1328,8 +1272,8 @@
                                     // dragViewIndex < pageUnderPointIndex, so after we remove the
                                     // drag view all subsequent views to pageUnderPointIndex will
                                     // shift down.
-                                    int oldX = getViewportOffsetX() + getChildOffset(i);
-                                    int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
+                                    int oldX = getChildOffset(i);
+                                    int newX = getChildOffset(i + shiftDelta);
 
                                     // Animate the view translation from its old position to its new
                                     // position
@@ -1600,20 +1544,20 @@
         }
     }
 
-    int getPageNearestToCenterOfScreen() {
+    public int getPageNearestToCenterOfScreen() {
         return getPageNearestToCenterOfScreen(getScrollX());
     }
 
     private int getPageNearestToCenterOfScreen(int scaledScrollX) {
-        int screenCenter = getViewportOffsetX() + scaledScrollX + (getViewportWidth() / 2);
+        int screenCenter = scaledScrollX + (getMeasuredWidth() / 2);
         int minDistanceFromScreenCenter = Integer.MAX_VALUE;
         int minDistanceFromScreenCenterIndex = -1;
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; ++i) {
-            View layout = (View) getPageAt(i);
+            View layout = getPageAt(i);
             int childWidth = layout.getMeasuredWidth();
             int halfChildWidth = (childWidth / 2);
-            int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
+            int childCenter = getChildOffset(i) + halfChildWidth;
             int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
             if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
                 minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1650,7 +1594,7 @@
 
     protected void snapToPageWithVelocity(int whichPage, int velocity) {
         whichPage = validateNewPage(whichPage);
-        int halfScreenSize = getViewportWidth() / 2;
+        int halfScreenSize = getMeasuredWidth() / 2;
 
         final int newX = getScrollForPage(whichPage);
         int delta = newX - getUnboundedScrollX();
@@ -1690,7 +1634,7 @@
         snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
     }
 
-    protected void snapToPage(int whichPage, int duration) {
+    public void snapToPage(int whichPage, int duration) {
         snapToPage(whichPage, duration, false, null);
     }
 
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 841c0cd..1a63326 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,6 +23,7 @@
 import android.view.ViewGroup;
 
 import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 public class ShortcutAndWidgetContainer extends ViewGroup {
     static final String TAG = "ShortcutAndWidgetContainer";
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ca235eb..158c540 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,7 +28,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -37,6 +36,7 @@
 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;
@@ -44,7 +44,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
-import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.View;
 
@@ -81,6 +80,8 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
+    public static final boolean ATLEAST_P = BuildCompat.isAtLeastP();
+
     public static final boolean ATLEAST_OREO_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
 
@@ -285,89 +286,6 @@
         }
     }
 
-    /**
-     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
-     * @param bitmap The bitmap to scan
-     * @param samples The approximate max number of samples to use.
-     */
-    public static int findDominantColorByHue(Bitmap bitmap, int samples) {
-        final int height = bitmap.getHeight();
-        final int width = bitmap.getWidth();
-        int sampleStride = (int) Math.sqrt((height * width) / samples);
-        if (sampleStride < 1) {
-            sampleStride = 1;
-        }
-
-        // This is an out-param, for getting the hsv values for an rgb
-        float[] hsv = new float[3];
-
-        // First get the best hue, by creating a histogram over 360 hue buckets,
-        // where each pixel contributes a score weighted by saturation, value, and alpha.
-        float[] hueScoreHistogram = new float[360];
-        float highScore = -1;
-        int bestHue = -1;
-
-        int[] pixels = new int[samples];
-        int pixelCount = 0;
-
-        for (int y = 0; y < height; y += sampleStride) {
-            for (int x = 0; x < width; x += sampleStride) {
-                int argb = bitmap.getPixel(x, y);
-                int alpha = 0xFF & (argb >> 24);
-                if (alpha < 0x80) {
-                    // Drop mostly-transparent pixels.
-                    continue;
-                }
-                // Remove the alpha channel.
-                int rgb = argb | 0xFF000000;
-                Color.colorToHSV(rgb, hsv);
-                // Bucket colors by the 360 integer hues.
-                int hue = (int) hsv[0];
-                if (hue < 0 || hue >= hueScoreHistogram.length) {
-                    // Defensively avoid array bounds violations.
-                    continue;
-                }
-                if (pixelCount < samples) {
-                    pixels[pixelCount++] = rgb;
-                }
-                float score = hsv[1] * hsv[2];
-                hueScoreHistogram[hue] += score;
-                if (hueScoreHistogram[hue] > highScore) {
-                    highScore = hueScoreHistogram[hue];
-                    bestHue = hue;
-                }
-            }
-        }
-
-        SparseArray<Float> rgbScores = new SparseArray<>();
-        int bestColor = 0xff000000;
-        highScore = -1;
-        // Go back over the RGB colors that match the winning hue,
-        // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
-        // The highest-scoring RGB color wins.
-        for (int i = 0; i < pixelCount; i++) {
-            int rgb = pixels[i];
-            Color.colorToHSV(rgb, hsv);
-            int hue = (int) hsv[0];
-            if (hue == bestHue) {
-                float s = hsv[1];
-                float v = hsv[2];
-                int bucket = (int) (s * 100) + (int) (v * 10000);
-                // Score by cumulative saturation * value.
-                float score = s * v;
-                Float oldTotal = rgbScores.get(bucket);
-                float newTotal = oldTotal == null ? score : oldTotal + score;
-                rgbScores.put(bucket, newTotal);
-                if (newTotal > highScore) {
-                    highScore = newTotal;
-                    // All the colors in the winning bucket are very similar. Last in wins.
-                    bestColor = rgb;
-                }
-            }
-        }
-        return bestColor;
-    }
-
     /*
      * Finds a system apk which had a broadcast receiver listening to a particular action.
      * @param action intent action used to find the apk
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 417366d..1b72f83 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -88,8 +88,10 @@
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -1131,7 +1133,7 @@
                 mLauncherOverlay.onScrollInteractionBegin();
             }
 
-            mLastOverlayScroll = Math.abs(amount / getViewportWidth());
+            mLastOverlayScroll = Math.abs(amount / getMeasuredWidth());
             mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
         } else {
             dampedOverScroll(amount);
@@ -1329,7 +1331,7 @@
 
     private void updatePageAlphaValues() {
         if (!workspaceInModalState() && !mIsSwitchingState) {
-            int screenCenter = getScrollX() + getViewportWidth() / 2;
+            int screenCenter = getScrollX() + getMeasuredWidth() / 2;
             for (int i = 0; i < getChildCount(); i++) {
                 CellLayout child = (CellLayout) getChildAt(i);
                 if (child != null) {
@@ -1413,8 +1415,8 @@
         if (mChildrenLayersEnabled) {
             final int screenCount = getChildCount();
 
-            float visibleLeft = getViewportOffsetX();
-            float visibleRight = visibleLeft + getViewportWidth();
+            float visibleLeft = 0;
+            float visibleRight = visibleLeft + getMeasuredWidth();
             float scaleX = getScaleX();
             if (scaleX < 1 && scaleX > 0) {
                 float mid = getMeasuredWidth() / 2;
@@ -2162,7 +2164,7 @@
         // Use the absolute left instead of the child left, as we want the visible area
         // irrespective of the visible child. Since the view can only scroll horizontally, the
         // top position is not affected.
-        mTempXY[0] = getViewportOffsetX() + getPaddingLeft() + boundingLayout.getLeft();
+        mTempXY[0] = getPaddingLeft() + boundingLayout.getLeft();
         mTempXY[1] = child.getTop() + boundingLayout.getTop();
 
         float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
@@ -3510,7 +3512,7 @@
                 return false;
             });
             for (PendingAppWidgetHostView view : views) {
-                view.reinflate();
+                view.reInflate();
             }
         }
 
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 9ed86ed..cf35e52 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -154,10 +154,13 @@
 
     private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
             PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter) {
+        float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
+        int drawableAlpha = Math.round(pageAlpha * (state.hasScrim ? 255 : 0));
+
         propertySetter.setInt(cl.getScrimBackground(),
-                DRAWABLE_ALPHA, state.hasScrim ? 255 : 0, Interpolators.ZOOM_IN);
+                DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_IN);
         propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
-                pageAlphaProvider.getPageAlpha(childIndex), pageAlphaProvider.interpolator);
+                pageAlpha, pageAlphaProvider.interpolator);
     }
 
     public static class PropertySetter {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 512db72..3b6fea9 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -21,20 +21,17 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.ButtonDropTarget;
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InfoDropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.UninstallDropTarget;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 8154845..2a7f46a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -59,7 +59,6 @@
 import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.launcher3.views.BottomUserEducationView;
 
 import java.util.HashMap;
@@ -73,8 +72,6 @@
         View.OnLongClickListener, Insettable, DeviceProfile.LauncherLayoutChangeListener,
         BubbleTextView.BubbleTextShadowHandler {
 
-    protected final Rect mBasePadding = new Rect();
-
     private final Launcher mLauncher;
     private final AdapterHolder[] mAH;
     private final ClickShadowView mTouchFeedbackView;
@@ -92,7 +89,6 @@
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
 
-    private TransformingTouchDelegate mTouchDelegate;
     private boolean mUsingTabs;
     private boolean mHasPredictions = false;
     private boolean mSearchModeWhileUsingTabs = false;
@@ -133,19 +129,13 @@
 
         DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
         grid.addLauncherLayoutChangedListener(this);
+        onLauncherLayoutChanged();
 
         applyTouchDelegate();
     }
 
     private void applyTouchDelegate() {
-        RecyclerView rv = getActiveRecyclerView();
-        mTouchDelegate = new TransformingTouchDelegate(rv);
-        mTouchDelegate.setBounds(
-                rv.getLeft() - mBasePadding.left,
-                rv.getTop() - mBasePadding.top,
-                rv.getRight() + mBasePadding.right,
-                rv.getBottom() + mBasePadding.bottom);
-        setTouchDelegate(mTouchDelegate);
+        // TODO: Reimplement once fast scroller is fixed.
     }
 
     @Override
@@ -166,11 +156,8 @@
             return;
         }
 
-        int[] padding = grid.getContainerPadding();
-        int paddingLeft = padding[0];
-        int paddingRight = padding[1];
-        mBasePadding.set(paddingLeft, 0, paddingRight, 0);
-        setPadding(paddingLeft, 0, paddingRight, 0);
+        Rect workspacePadding = grid.getWorkspacePadding(null);
+        setPadding(workspacePadding.left, 0, workspacePadding.right, 0);
     }
 
     @Override
@@ -294,12 +281,9 @@
 
         // This is a focus listener that proxies focus from a view into the list view.  This is to
         // work around the search box from getting first focus and showing the cursor.
-        setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (hasFocus && getActiveRecyclerView() != null) {
-                    getActiveRecyclerView().requestFocus();
-                }
+        setOnFocusChangeListener((v, hasFocus) -> {
+            if (hasFocus && getActiveRecyclerView() != null) {
+                getActiveRecyclerView().requestFocus();
             }
         });
 
@@ -309,8 +293,6 @@
         mSearchContainer = findViewById(R.id.search_container_all_apps);
         mSearchUiManager = (SearchUiManager) mSearchContainer;
         mSearchUiManager.initialize(this);
-
-        onLauncherLayoutChanged();
     }
 
     public SearchUiManager getSearchUiManager() {
@@ -614,14 +596,14 @@
     }
 
     public List<AppInfo> getPredictedApps() {
-        if (mUsingTabs) {
+        if (isHeaderVisible()) {
             return mHeader.getPredictionRow().getPredictedApps();
         } else {
             return mAH[AdapterHolder.MAIN].appsList.getPredictedApps();
         }
     }
 
-    private boolean isHeaderVisible() {
+    public boolean isHeaderVisible() {
         return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 246f7be..234eb81 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.os.UserHandle;
 import android.support.animation.DynamicAnimation;
 import android.support.animation.SpringAnimation;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
@@ -33,7 +32,6 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.Switch;
 import android.widget.TextView;
 
 import com.android.launcher3.AppInfo;
@@ -43,7 +41,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
 import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.discovery.AppDiscoveryAppInfo;
 import com.android.launcher3.discovery.AppDiscoveryItemView;
@@ -332,7 +329,6 @@
                         R.layout.all_apps_divider, parent, false));
             case VIEW_TYPE_WORK_TAB_FOOTER:
                 View footer = mLayoutInflater.inflate(R.layout.work_tab_footer, parent, false);
-                // TODO: Implement the work mode toggle logic here.
                 return new ViewHolder(footer);
             default:
                 throw new RuntimeException("Unexpected view type");
@@ -379,8 +375,8 @@
                 // nothing to do
                 break;
             case VIEW_TYPE_WORK_TAB_FOOTER:
-                Switch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
-                workModeToggle.setChecked(!isAnyProfileQuietModeEnabled());
+                WorkModeSwitch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
+                workModeToggle.refresh();
                 break;
         }
         if (mBindViewCallback != null) {
@@ -549,15 +545,4 @@
             return factor;
         }
     }
-
-    private boolean isAnyProfileQuietModeEnabled() {
-        UserManagerCompat userManager = UserManagerCompat.getInstance(mLauncher);
-        List<UserHandle> userProfiles = userManager.getUserProfiles();
-        for (UserHandle userProfile : userProfiles) {
-            if (userManager.isQuietModeEnabled(userProfile)) {
-                return true;
-            }
-        }
-        return false;
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 5830f74..5d1fc8e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -21,7 +21,6 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.graphics.GradientView;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.AllAppsScrim;
@@ -63,6 +62,7 @@
 
     private final Launcher mLauncher;
     private final boolean mIsDarkTheme;
+    private final boolean mIsVerticalLayout;
 
     // Animation in this class is controlled by a single variable {@link mProgress}.
     // Visually, it represents top y coordinate of the all apps container if multiplied with
@@ -83,6 +83,7 @@
         mProgress = 1f;
 
         mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
+        mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
     }
 
     public float getShiftRange() {
@@ -95,18 +96,6 @@
         mAppsView.setVisibility(View.VISIBLE);
     }
 
-    private void updateLightStatusBar(float shift) {
-        // Use a light system UI (dark icons) if all apps is behind at least half of the status bar.
-        boolean forceChange = shift <= mShiftRange / 4;
-        if (forceChange) {
-            mLauncher.getSystemUiController().updateUiState(
-                    SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
-        } else {
-            mLauncher.getSystemUiController().updateUiState(
-                    SystemUiController.UI_STATE_ALL_APPS, 0);
-        }
-    }
-
     /**
      * Note this method should not be called outside this class. This is public because it is used
      * in xml-based animations which also handle updating the appropriate UI.
@@ -124,24 +113,30 @@
         float alpha = 1 - workspaceHotseatAlpha;
         float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
 
-        mAppsView.setAlpha(alpha);
         mAppsView.setTranslationY(shiftCurrent);
-
         if (mAllAppsScrim == null) {
             mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
         }
         float hotseatTranslation = -mShiftRange + shiftCurrent;
         mAllAppsScrim.setProgress(hotseatTranslation, alpha);
 
-        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+        if (!mIsVerticalLayout) {
+            mAppsView.setAlpha(alpha);
             mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y, hotseatTranslation,
                     hotseatAlpha);
-        } else {
-            mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y,
-                    PARALLAX_COEFFICIENT * hotseatTranslation, hotseatAlpha);
-        }
 
-        updateLightStatusBar(shiftCurrent);
+            // Use a light system UI (dark icons) if all apps is behind at least half of the
+            // status bar.
+            boolean forceChange = shiftCurrent <= mShiftRange / 4;
+            if (forceChange) {
+                mLauncher.getSystemUiController().updateUiState(
+                        SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
+            } else {
+                mLauncher.getSystemUiController().updateUiState(
+                        SystemUiController.UI_STATE_ALL_APPS, 0);
+            }
+
+        }
     }
 
     public float getProgress() {
@@ -154,7 +149,7 @@
      */
     @Override
     public void setState(LauncherState state) {
-        setProgress(state.verticalProgress);
+        setProgress(state.getVerticalProgress(mLauncher));
         onProgressAnimationEnd();
     }
 
@@ -165,15 +160,15 @@
     @Override
     public void setStateWithAnimation(LauncherState toState,
             AnimatorSetBuilder builder, AnimationConfig config) {
-        if (Float.compare(mProgress, toState.verticalProgress) == 0) {
+        float targetProgress = toState.getVerticalProgress(mLauncher);
+        if (Float.compare(mProgress, targetProgress) == 0) {
             // Fail fast
             onProgressAnimationEnd();
             return;
         }
 
         Interpolator interpolator = config.userControlled ? LINEAR : FAST_OUT_SLOW_IN;
-        ObjectAnimator anim = ObjectAnimator.ofFloat(
-                this, PROGRESS, mProgress, toState.verticalProgress);
+        ObjectAnimator anim = ObjectAnimator.ofFloat(this, PROGRESS, mProgress, targetProgress);
         anim.setDuration(config.duration);
         anim.setInterpolator(interpolator);
         anim.addListener(getProgressAnimatorListener());
@@ -221,6 +216,9 @@
         } else if (Float.compare(mProgress, 0f) == 0) {
             mHotseat.setVisibility(View.INVISIBLE);
             mAppsView.setVisibility(View.VISIBLE);
+        } else {
+            mAppsView.setVisibility(View.VISIBLE);
+            mHotseat.setVisibility(View.VISIBLE);
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 02e731a..eecd009 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.allapps;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Process;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -23,11 +24,13 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.discovery.AppDiscoveryAppInfo;
 import com.android.launcher3.discovery.AppDiscoveryItem;
 import com.android.launcher3.discovery.AppDiscoveryUpdateState;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -620,11 +623,18 @@
         }
 
         // Add the work profile footer if required.
-        if (mIsWork) {
+        if (shouldShowWorkFooter()) {
             mAdapterItems.add(AdapterItem.asWorkTabFooter(position++));
         }
     }
 
+    private boolean shouldShowWorkFooter() {
+        return mIsWork && Utilities.ATLEAST_P &&
+                (DeepShortcutManager.getInstance(mLauncher).hasHostPermission()
+                        || mLauncher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+                        == PackageManager.PERMISSION_GRANTED);
+    }
+
     public boolean isAppDiscoveryRunning() {
         return mAppDiscoveryUpdateState == AppDiscoveryUpdateState.START
                 || mAppDiscoveryUpdateState == AppDiscoveryUpdateState.UPDATE;
diff --git a/src/com/android/launcher3/allapps/LandscapeFastScroller.java b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
deleted file mode 100644
index cdde657..0000000
--- a/src/com/android/launcher3/allapps/LandscapeFastScroller.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.allapps;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-import com.android.launcher3.views.RecyclerViewFastScroller;
-
-/**
- * Extension of {@link RecyclerViewFastScroller} to be used in landscape layout.
- */
-public class LandscapeFastScroller extends RecyclerViewFastScroller {
-
-    public LandscapeFastScroller(Context context) {
-        super(context);
-    }
-
-    public LandscapeFastScroller(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public LandscapeFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    public boolean handleTouchEvent(MotionEvent ev) {
-        // We handle our own touch event, no need to handle recycler view touch delegates.
-        return false;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        event.offsetLocation(0, -mRv.getPaddingTop());
-        if (super.handleTouchEvent(event)) {
-            getParent().requestDisallowInterceptTouchEvent(true);
-        }
-        event.offsetLocation(0, mRv.getPaddingTop());
-        return true;
-    }
-
-    @Override
-    public boolean shouldBlockIntercept(int x, int y) {
-        // If the user touched the scroll bar area, block swipe
-        return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
-    }
-}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
new file mode 100644
index 0000000..32c9ce3
--- /dev/null
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.widget.Switch;
+
+import com.android.launcher3.compat.UserManagerCompat;
+
+import java.util.List;
+
+public class WorkModeSwitch extends Switch {
+
+    public WorkModeSwitch(Context context) {
+        super(context);
+    }
+
+    public WorkModeSwitch(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        // No-op, do not change the checked state until broadcast is received.
+    }
+
+    @Override
+    public void toggle() {
+        trySetQuietModeEnabledToAllProfilesAsync(isChecked());
+    }
+
+    private void setCheckedInternal(boolean checked) {
+        super.setChecked(checked);
+    }
+
+    public void refresh() {
+        setCheckedInternal(!isAnyProfileQuietModeEnabled());
+        setEnabled(true);
+    }
+
+    private boolean isAnyProfileQuietModeEnabled() {
+        UserManagerCompat userManager = UserManagerCompat.getInstance(getContext());
+        List<UserHandle> userProfiles = userManager.getUserProfiles();
+        for (UserHandle userProfile : userProfiles) {
+            if (Process.myUserHandle().equals(userProfile)) {
+                continue;
+            }
+            if (userManager.isQuietModeEnabled(userProfile)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
+        new AsyncTask<Void, Void, Boolean>() {
+
+            @Override
+            protected void onPreExecute() {
+                super.onPreExecute();
+                setEnabled(false);
+            }
+
+            @Override
+            protected Boolean doInBackground(Void... voids) {
+                UserManagerCompat userManager = UserManagerCompat.getInstance(getContext());
+                List<UserHandle> userProfiles = userManager.getUserProfiles();
+                boolean showConfirm = false;
+                for (UserHandle userProfile : userProfiles) {
+                    if (Process.myUserHandle().equals(userProfile)) {
+                        continue;
+                    }
+                    showConfirm |= !userManager.trySetQuietModeEnabled(enabled, userProfile);
+                }
+                return showConfirm;
+            }
+
+            @Override
+            protected void onPostExecute(Boolean showConfirm) {
+                if (showConfirm) {
+                    setEnabled(true);
+                }
+            }
+        }.execute();
+    }
+}
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index 63e67ff..7cd9651 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -28,6 +28,7 @@
 public class AnimatorSetBuilder {
 
     protected final ArrayList<Animator> mAnims = new ArrayList<>();
+    private long mStartDelay = 0;
 
     /**
      * Associates a tag with all the animations added after this call.
@@ -38,9 +39,14 @@
         mAnims.add(anim);
     }
 
+    public void setStartDelay(long startDelay) {
+        mStartDelay = startDelay;
+    }
+
     public AnimatorSet build() {
         AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
         anim.playTogether(mAnims);
+        anim.setStartDelay(mStartDelay);
         return anim;
     }
 }
diff --git a/src/com/android/launcher3/badge/BadgeInfo.java b/src/com/android/launcher3/badge/BadgeInfo.java
index 08d8ad4..f03544f 100644
--- a/src/com/android/launcher3/badge/BadgeInfo.java
+++ b/src/com/android/launcher3/badge/BadgeInfo.java
@@ -16,14 +16,6 @@
 
 package com.android.launcher3.badge;
 
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.util.PackageUserKey;
@@ -53,15 +45,6 @@
      */
     private int mTotalCount;
 
-    /** This will only be initialized if the badge should display the notification icon. */
-    private NotificationInfo mNotificationInfo;
-
-    /**
-     * When retrieving the notification icon, we draw it into this shader, which can be clipped
-     * as necessary when drawn in a badge.
-     */
-    private Shader mNotificationIcon;
-
     public BadgeInfo(PackageUserKey packageUserKey) {
         mPackageUserKey = packageUserKey;
         mNotificationKeys = new ArrayList<>();
@@ -110,44 +93,6 @@
         return Math.min(mTotalCount, MAX_COUNT);
     }
 
-    public void setNotificationToShow(@Nullable NotificationInfo notificationInfo) {
-        mNotificationInfo = notificationInfo;
-        mNotificationIcon = null;
-    }
-
-    public boolean hasNotificationToShow() {
-        return mNotificationInfo != null;
-    }
-
-    /**
-     * Returns a shader to set on a Paint that will draw the notification icon in a badge.
-     *
-     * The shader is cached until {@link #setNotificationToShow(NotificationInfo)} is called.
-     */
-    public @Nullable Shader getNotificationIconForBadge(Context context, int badgeColor,
-            int badgeSize, int badgePadding) {
-        if (mNotificationInfo == null) {
-            return null;
-        }
-        if (mNotificationIcon == null) {
-            Drawable icon = mNotificationInfo.getIconForBackground(context, badgeColor)
-                    .getConstantState().newDrawable();
-            int iconSize = badgeSize - badgePadding * 2;
-            icon.setBounds(0, 0, iconSize, iconSize);
-            Bitmap iconBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(iconBitmap);
-            canvas.translate(badgePadding, badgePadding);
-            icon.draw(canvas);
-            mNotificationIcon = new BitmapShader(iconBitmap, Shader.TileMode.CLAMP,
-                    Shader.TileMode.CLAMP);
-        }
-        return mNotificationIcon;
-    }
-
-    public boolean isIconLarge() {
-        return mNotificationInfo != null && mNotificationInfo.isIconLarge();
-    }
-
     /**
      * Whether newBadge represents the same PackageUserKey as this badge, and icons with
      * this badge should be invalidated. So, for instance, if a badge has 3 notifications
@@ -158,7 +103,6 @@
      */
     public boolean shouldBeInvalidated(BadgeInfo newBadge) {
         return mPackageUserKey.equals(newBadge.mPackageUserKey)
-                && (getNotificationCount() != newBadge.getNotificationCount()
-                    || hasNotificationToShow());
+                && (getNotificationCount() != newBadge.getNotificationCount());
     }
 }
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 6ce334e..02ec9f0 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -16,21 +16,17 @@
 
 package com.android.launcher3.badge;
 
-import android.content.Context;
-import android.content.res.Resources;
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Shader;
-import android.support.annotation.Nullable;
 import android.util.Log;
-import android.util.SparseArray;
 
-import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.graphics.ShadowGenerator;
 
 /**
@@ -41,147 +37,58 @@
 
     private static final String TAG = "BadgeRenderer";
 
-    private static final boolean DOTS_ONLY = true;
-
     // The badge sizes are defined as percentages of the app icon size.
-    private static final float SIZE_PERCENTAGE = 0.38f;
+    private static final float SIZE_PERCENTAGE = 0.23f;
     // Used to expand the width of the badge for each additional digit.
-    private static final float CHAR_SIZE_PERCENTAGE = 0.12f;
-    private static final float TEXT_SIZE_PERCENTAGE = 0.26f;
     private static final float OFFSET_PERCENTAGE = 0.02f;
-    private static final float STACK_OFFSET_PERCENTAGE_X = 0.05f;
-    private static final float STACK_OFFSET_PERCENTAGE_Y = 0.06f;
-    private static final float DOT_SCALE = 0.6f;
 
-    private final Context mContext;
     private final int mSize;
-    private final int mCharSize;
-    private final int mTextHeight;
     private final int mOffset;
-    private final int mStackOffsetX;
-    private final int mStackOffsetY;
-    private final IconDrawer mLargeIconDrawer;
-    private final IconDrawer mSmallIconDrawer;
-    private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
-            | Paint.FILTER_BITMAP_FLAG);
-    private final SparseArray<Bitmap> mBackgroundsWithShadow;
+    private final float mCircleRadius;
+    private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
 
-    public BadgeRenderer(Context context, int iconSizePx) {
-        mContext = context;
-        Resources res = context.getResources();
+    private final Bitmap mBackgroundWithShadow;
+    private final int mBitmapOffset;
+
+    public BadgeRenderer(int iconSizePx) {
         mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
-        mCharSize = (int) (CHAR_SIZE_PERCENTAGE * iconSizePx);
         mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
-        mStackOffsetX = (int) (STACK_OFFSET_PERCENTAGE_X * iconSizePx);
-        mStackOffsetY = (int) (STACK_OFFSET_PERCENTAGE_Y * iconSizePx);
-        mTextPaint.setTextSize(iconSizePx * TEXT_SIZE_PERCENTAGE);
-        mTextPaint.setTextAlign(Paint.Align.CENTER);
-        mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
-        mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
-        // Measure the text height.
-        Rect tempTextHeight = new Rect();
-        mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
-        mTextHeight = tempTextHeight.height();
 
-        mBackgroundsWithShadow = new SparseArray<>(3);
+        ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
+        mBackgroundWithShadow = builder.setupBlurForSize(mSize).createPill(mSize, mSize);
+        mCircleRadius = builder.radius;
+
+        mBitmapOffset = -mBackgroundWithShadow.getHeight() / 2; // Same as width.
     }
 
     /**
      * Draw a circle in the top right corner of the given bounds, and draw
      * {@link BadgeInfo#getNotificationCount()} on top of the circle.
-     * @param palette The colors (based on the icon) to use for the badge.
-     * @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
+     * @param color The color (based on the icon) to use for the badge.
      * @param iconBounds The bounds of the icon being badged.
      * @param badgeScale The progress of the animation, from 0 to 1.
      * @param spaceForOffset How much space is available to offset the badge up and to the right.
      */
-    public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
-            Rect iconBounds, float badgeScale, Point spaceForOffset) {
-        if (palette == null || iconBounds == null || spaceForOffset == null) {
+    public void draw(
+            Canvas canvas, int color, Rect iconBounds, float badgeScale, Point spaceForOffset) {
+        if (iconBounds == null || spaceForOffset == null) {
             Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
             return;
         }
-        mTextPaint.setColor(palette.textColor);
-        IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
-                ? mLargeIconDrawer : mSmallIconDrawer;
-        Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
-                mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
-        String notificationCount = badgeInfo == null ? "0"
-                : String.valueOf(badgeInfo.getNotificationCount());
-        int numChars = notificationCount.length();
-        int width = DOTS_ONLY ? mSize : mSize + mCharSize * (numChars - 1);
-        // Lazily load the background with shadow.
-        Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
-        if (backgroundWithShadow == null) {
-            backgroundWithShadow = new ShadowGenerator.Builder(Color.WHITE)
-                    .setupBlurForSize(mSize).createPill(width, mSize);
-            mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
-        }
         canvas.save(Canvas.MATRIX_SAVE_FLAG);
         // We draw the badge relative to its center.
-        int badgeCenterX = iconBounds.right - width / 2;
+        int badgeCenterX = iconBounds.right - mSize / 2;
         int badgeCenterY = iconBounds.top + mSize / 2;
-        boolean isText = !DOTS_ONLY && badgeInfo != null && badgeInfo.getNotificationCount() != 0;
-        boolean isIcon = !DOTS_ONLY && icon != null;
-        boolean isDot = !(isText || isIcon);
-        if (isDot) {
-            badgeScale *= DOT_SCALE;
-        }
+
         int offsetX = Math.min(mOffset, spaceForOffset.x);
         int offsetY = Math.min(mOffset, spaceForOffset.y);
         canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
         canvas.scale(badgeScale, badgeScale);
-        // Prepare the background and shadow and possible stacking effect.
-        mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
-        int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
-        boolean shouldStack = !isDot && badgeInfo != null
-                && badgeInfo.getNotificationKeys().size() > 1;
-        if (shouldStack) {
-            int offsetDiffX = mStackOffsetX - mOffset;
-            int offsetDiffY = mStackOffsetY - mOffset;
-            canvas.translate(offsetDiffX, offsetDiffY);
-            canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
-                    -backgroundWithShadowSize / 2, mBackgroundPaint);
-            canvas.translate(-offsetDiffX, -offsetDiffY);
-        }
 
-        if (isText) {
-            canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
-                    -backgroundWithShadowSize / 2, mBackgroundPaint);
-            canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
-        } else if (isIcon) {
-            canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
-                    -backgroundWithShadowSize / 2, mBackgroundPaint);
-            iconDrawer.drawIcon(icon, canvas);
-        } else if (isDot) {
-            mBackgroundPaint.setColorFilter(palette.saturatedBackgroundColorMatrixFilter);
-            canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
-                    -backgroundWithShadowSize / 2, mBackgroundPaint);
-        }
+        mCirclePaint.setColor(Color.BLACK);
+        canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
+        mCirclePaint.setColor(color);
+        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
         canvas.restore();
     }
-
-    /** Draws the notification icon with padding of a given size. */
-    private class IconDrawer {
-
-        private final int mPadding;
-        private final Bitmap mCircleClipBitmap;
-        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
-                Paint.FILTER_BITMAP_FLAG);
-
-        public IconDrawer(int padding) {
-            mPadding = padding;
-            mCircleClipBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ALPHA_8);
-            Canvas canvas = new Canvas();
-            canvas.setBitmap(mCircleClipBitmap);
-            canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2 - padding, mPaint);
-        }
-
-        public void drawIcon(Shader icon, Canvas canvas) {
-            mPaint.setShader(icon);
-            canvas.drawBitmap(mCircleClipBitmap, -mSize / 2, -mSize / 2, mPaint);
-            mPaint.setShader(null);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index 3214b46..5cd90b1 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -137,8 +137,7 @@
             ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
             ShortcutInfo info = new ShortcutInfo(compat, context);
             // Apply the unbadged icon and fetch the actual icon asynchronously.
-            info.iconBitmap = LauncherIcons
-                    .createShortcutIcon(compat, context, false /* badged */);
+            LauncherIcons.createShortcutIcon(compat, context, false /* badged */).applyTo(info);
             LauncherAppState.getInstance(context).getModel()
                     .updateAndBindShortcutInfo(info, compat);
             return info;
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 25808d2..197f798 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -33,7 +33,9 @@
     public static UserManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.ATLEAST_NOUGAT_MR1) {
+                if (Utilities.ATLEAST_P) {
+                    sInstance = new UserManagerCompatVP(context.getApplicationContext());
+                } else if (Utilities.ATLEAST_NOUGAT_MR1) {
                     sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
                 } else if (Utilities.ATLEAST_NOUGAT) {
                     sInstance = new UserManagerCompatVN(context.getApplicationContext());
@@ -61,4 +63,5 @@
     public abstract boolean isUserUnlocked(UserHandle user);
 
     public abstract boolean isDemoUser();
+    public abstract boolean trySetQuietModeEnabled(boolean enableQuietMode, UserHandle user);
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index bb42573..e6cc319 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -83,6 +83,11 @@
     }
 
     @Override
+    public boolean trySetQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
+        return false;
+    }
+
+    @Override
     public void enableAndResetCache() {
         synchronized (this) {
             mUsers = new LongArrayMap<>();
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVP.java b/src/com/android/launcher3/compat/UserManagerCompatVP.java
new file mode 100644
index 0000000..a0bf0ab
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVP.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.compat;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class UserManagerCompatVP extends UserManagerCompatVNMr1 {
+    private static final String TAG = "UserManagerCompatVP";
+
+    private Method mTrySetQuietModeEnabledMethod;
+
+    UserManagerCompatVP(Context context) {
+        super(context);
+        // TODO: Replace it with proper API call once SDK is ready.
+        try {
+            mTrySetQuietModeEnabledMethod = UserManager.class.getDeclaredMethod(
+                    "trySetQuietModeEnabled", boolean.class, UserHandle.class);
+        } catch (NoSuchMethodException e) {
+            Log.e(TAG, "trySetQuietModeEnabled is not available", e);
+        }
+    }
+
+    @Override
+    public boolean trySetQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
+        if (mTrySetQuietModeEnabledMethod == null) {
+            return false;
+        }
+        try {
+            return (boolean)
+                    mTrySetQuietModeEnabledMethod.invoke(mUserManager, enableQuietMode, user);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            Log.e(TAG, "Failed to invoke mTrySetQuietModeEnabledMethod", e);
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 4cc70d3..299f090 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -18,6 +18,7 @@
 import static android.app.WallpaperManager.FLAG_SYSTEM;
 
 import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.graphics.ColorExtractor.findDominantColorByHue;
 
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
@@ -257,7 +258,7 @@
             String value = VERSION_PREFIX + wallpaperId;
 
             if (bitmap != null) {
-                int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+                int color = findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
                 value += "," + color;
             }
 
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
index ed5cfeb..26e5066 100644
--- a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
+++ b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
@@ -18,12 +18,14 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.graphics.Color;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.graphics.ColorExtractor;
 
 public class AppDiscoveryAppInfo extends AppInfo {
 
@@ -41,6 +43,8 @@
         this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
         this.title = item.title;
         this.iconBitmap = item.bitmap;
+        this.iconColor = iconBitmap == null ? Color.TRANSPARENT :
+                ColorExtractor.findDominantColorByHue(item.bitmap);
         this.usingLowResIcon = false;
         this.isInstantApp = item.isInstantApp;
         this.isRecent = item.isRecent;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 27d6b89..c75e616 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -157,6 +157,8 @@
                 mTouchCompleteListener.onTouchComplete();
             }
             mTouchCompleteListener = null;
+        } else if (action == MotionEvent.ACTION_DOWN) {
+            mLauncher.finishAutoCancelActionMode();
         }
         mActiveController = null;
 
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7c89df3..11ff88f 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -265,7 +265,7 @@
                             mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
 
                             if (info.isDisabled()) {
-                                FastBitmapDrawable d = new FastBitmapDrawable(null);
+                                FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
                                 d.setIsDisabled(true);
                                 mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
                             }
@@ -367,7 +367,8 @@
                 return new FixedSizeEmptyDrawable(iconSize);
             }
             ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
-            Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
+            Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache())
+                    .iconBitmap;
 
             float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
             float insetFraction = (iconSize - badgeSize) / iconSize;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b78e470..d4c396a 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -412,17 +412,7 @@
         mInfo = info;
         ArrayList<ShortcutInfo> children = info.contents;
         Collections.sort(children, ITEM_POS_COMPARATOR);
-
-        ArrayList<ShortcutInfo> overflow = mContent.bindItems(children);
-
-        // If our folder has too many items we prune them from the list. This is an issue
-        // when upgrading from the old Folders implementation which could contain an unlimited
-        // number of items.
-        // TODO: Remove this, as with multi-page folders, there will never be any overflow
-        for (ShortcutInfo item: overflow) {
-            mInfo.remove(item, false);
-            mLauncher.getModelWriter().deleteItemFromDatabase(item);
-        }
+        mContent.bindItems(children);
 
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         if (lp == null) {
@@ -710,8 +700,7 @@
         final int itemType = item.itemType;
         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
-                itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
-                    !isFull());
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
     }
 
     public void onDragEnter(DragObject d) {
@@ -926,10 +915,6 @@
         return mState != STATE_ANIMATING;
     }
 
-    public boolean isFull() {
-        return mContent.isFull();
-    }
-
     private void centerAboutIcon() {
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 5983029..2de09b8 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,7 +63,6 @@
 import com.android.launcher3.dragndrop.BaseItemDragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
@@ -206,7 +205,7 @@
         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
                 itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
-                !mFolder.isFull() && item != mInfo && !mFolder.isOpen());
+                item != mInfo && !mFolder.isOpen());
     }
 
     public boolean acceptDrop(ItemInfo dragInfo) {
@@ -499,8 +498,7 @@
             // If we are animating to the accepting state, animate the badge out.
             float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
             mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
-            IconPalette badgePalette = IconPalette.getFolderBadgePalette(getResources());
-            mBadgeRenderer.draw(canvas, badgePalette, mBadgeInfo, mTempBounds,
+            mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
                     badgeScale, mTempSpaceForBadgeOffset);
         }
     }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 9e5bc4f..f4462aa 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -55,8 +55,6 @@
 
     private static final String TAG = "FolderPagedView";
 
-    private static final boolean ALLOW_FOLDER_SCROLL = true;
-
     private static final int REORDER_ANIMATION_DURATION = 230;
     private static final int START_VIEW_REORDER_DELAY = 30;
     private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
@@ -183,21 +181,13 @@
 
     /**
      * 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) {
+    public void bindItems(ArrayList<ShortcutInfo> items) {
         ArrayList<View> icons = new ArrayList<>();
-        ArrayList<ShortcutInfo> extra = new ArrayList<>();
-
         for (ShortcutInfo item : items) {
-            if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
-                extra.add(item);
-            } else {
-                icons.add(createNewView(item));
-            }
+            icons.add(createNewView(item));
         }
         arrangeChildren(icons, icons.size(), false);
-        return extra;
     }
 
     public void allocateSpaceForRank(int rank) {
@@ -431,10 +421,6 @@
                 pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
     }
 
-    public boolean isFull() {
-        return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
-    }
-
     public View getFirstItem() {
         if (getChildCount() < 1) {
             return null;
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index eba5d98..285aef8 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -200,6 +200,10 @@
         return ColorUtils.setAlphaComponent(mBgColor, alpha);
     }
 
+    public int getBadgeColor() {
+        return mBgColor;
+    }
+
     public void drawBackground(Canvas canvas) {
         mPaint.setStyle(Paint.Style.FILL);
         mPaint.setColor(getBgColor());
diff --git a/src/com/android/launcher3/graphics/BitmapInfo.java b/src/com/android/launcher3/graphics/BitmapInfo.java
new file mode 100644
index 0000000..ab906e2
--- /dev/null
+++ b/src/com/android/launcher3/graphics/BitmapInfo.java
@@ -0,0 +1,43 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Bitmap;
+
+import com.android.launcher3.ItemInfoWithIcon;
+
+public class BitmapInfo {
+
+    public Bitmap icon;
+    public int color;
+
+    public void applyTo(ItemInfoWithIcon info) {
+        info.iconBitmap = icon;
+        info.iconColor = color;
+    }
+
+    public void applyTo(BitmapInfo info) {
+        info.icon = icon;
+        info.color = color;
+    }
+
+    public static BitmapInfo fromBitmap(Bitmap bitmap) {
+        BitmapInfo info = new BitmapInfo();
+        info.icon = bitmap;
+        info.color = ColorExtractor.findDominantColorByHue(bitmap);
+        return info;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/ColorExtractor.java b/src/com/android/launcher3/graphics/ColorExtractor.java
new file mode 100644
index 0000000..e9d72b7
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ColorExtractor.java
@@ -0,0 +1,113 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.SparseArray;
+
+/**
+ * Utility class for extracting colors from a bitmap.
+ */
+public class ColorExtractor {
+
+    public static int findDominantColorByHue(Bitmap bitmap) {
+        return findDominantColorByHue(bitmap, 20);
+    }
+
+    /**
+     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+     * @param bitmap The bitmap to scan
+     * @param samples The approximate max number of samples to use.
+     */
+    public static int findDominantColorByHue(Bitmap bitmap, int samples) {
+        final int height = bitmap.getHeight();
+        final int width = bitmap.getWidth();
+        int sampleStride = (int) Math.sqrt((height * width) / samples);
+        if (sampleStride < 1) {
+            sampleStride = 1;
+        }
+
+        // This is an out-param, for getting the hsv values for an rgb
+        float[] hsv = new float[3];
+
+        // First get the best hue, by creating a histogram over 360 hue buckets,
+        // where each pixel contributes a score weighted by saturation, value, and alpha.
+        float[] hueScoreHistogram = new float[360];
+        float highScore = -1;
+        int bestHue = -1;
+
+        int[] pixels = new int[samples];
+        int pixelCount = 0;
+
+        for (int y = 0; y < height; y += sampleStride) {
+            for (int x = 0; x < width; x += sampleStride) {
+                int argb = bitmap.getPixel(x, y);
+                int alpha = 0xFF & (argb >> 24);
+                if (alpha < 0x80) {
+                    // Drop mostly-transparent pixels.
+                    continue;
+                }
+                // Remove the alpha channel.
+                int rgb = argb | 0xFF000000;
+                Color.colorToHSV(rgb, hsv);
+                // Bucket colors by the 360 integer hues.
+                int hue = (int) hsv[0];
+                if (hue < 0 || hue >= hueScoreHistogram.length) {
+                    // Defensively avoid array bounds violations.
+                    continue;
+                }
+                if (pixelCount < samples) {
+                    pixels[pixelCount++] = rgb;
+                }
+                float score = hsv[1] * hsv[2];
+                hueScoreHistogram[hue] += score;
+                if (hueScoreHistogram[hue] > highScore) {
+                    highScore = hueScoreHistogram[hue];
+                    bestHue = hue;
+                }
+            }
+        }
+
+        SparseArray<Float> rgbScores = new SparseArray<>();
+        int bestColor = 0xff000000;
+        highScore = -1;
+        // Go back over the RGB colors that match the winning hue,
+        // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
+        // The highest-scoring RGB color wins.
+        for (int i = 0; i < pixelCount; i++) {
+            int rgb = pixels[i];
+            Color.colorToHSV(rgb, hsv);
+            int hue = (int) hsv[0];
+            if (hue == bestHue) {
+                float s = hsv[1];
+                float v = hsv[2];
+                int bucket = (int) (s * 100) + (int) (v * 10000);
+                // Score by cumulative saturation * value.
+                float score = s * v;
+                Float oldTotal = rgbScores.get(bucket);
+                float newTotal = oldTotal == null ? score : oldTotal + score;
+                rgbScores.put(bucket, newTotal);
+                if (newTotal > highScore) {
+                    highScore = newTotal;
+                    // All the colors in the winning bucket are very similar. Last in wins.
+                    bestColor = rgb;
+                }
+            }
+        }
+        return bestColor;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 902906f..6a328e9 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -31,7 +31,7 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 371479b..32d9e41 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -31,7 +31,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
@@ -64,18 +64,18 @@
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
-    public FastBitmapDrawable newIcon(Bitmap icon, ItemInfo info) {
-        return new FastBitmapDrawable(icon);
+    public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
+        return new FastBitmapDrawable(info);
     }
 
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
-    public PreloadIconDrawable newPendingIcon(Bitmap icon, Context context) {
+    public PreloadIconDrawable newPendingIcon(ItemInfoWithIcon info, Context context) {
         if (mPreloadProgressPath == null) {
             mPreloadProgressPath = getPreloadProgressPath(context);
         }
-        return new PreloadIconDrawable(icon, mPreloadProgressPath, context);
+        return new PreloadIconDrawable(info, mPreloadProgressPath, context);
     }
 
 
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 6e01ed5..9c3b77e 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -18,12 +18,7 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
 
@@ -41,37 +36,10 @@
     private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
     private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
 
-    private static IconPalette sBadgePalette;
-    private static IconPalette sFolderBadgePalette;
-
-    public final int dominantColor;
-    public final int backgroundColor;
-    public final ColorMatrixColorFilter backgroundColorMatrixFilter;
-    public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
-    public final int textColor;
-    public final int secondaryColor;
-
-    private IconPalette(int color, boolean desaturateBackground) {
-        dominantColor = color;
-        backgroundColor = desaturateBackground ? getMutedColor(dominantColor, 0.87f) : dominantColor;
-        ColorMatrix backgroundColorMatrix = new ColorMatrix();
-        Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
-        backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
-        if (!desaturateBackground) {
-            saturatedBackgroundColorMatrixFilter = backgroundColorMatrixFilter;
-        } else {
-            // Get slightly more saturated background color.
-            Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
-            saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
-        }
-        textColor = getTextColorForBackground(backgroundColor);
-        secondaryColor = getLowContrastColor(backgroundColor);
-    }
-
     /**
      * Returns a color suitable for the progress bar color of preload icon.
      */
-    public int getPreloadProgressColor(Context context) {
+    public static int getPreloadProgressColor(Context context, int dominantColor) {
         int result = dominantColor;
 
         // Make sure that the dominant color has enough saturation to be visible properly.
@@ -86,37 +54,6 @@
         return result;
     }
 
-    public static IconPalette fromDominantColor(int dominantColor, boolean desaturateBackground) {
-        return new IconPalette(dominantColor, desaturateBackground);
-    }
-
-    /**
-     * Returns an IconPalette based on the badge_color in colors.xml.
-     * If that color is Color.TRANSPARENT, then returns null instead.
-     */
-    public static @Nullable IconPalette getBadgePalette(Resources resources) {
-        int badgeColor = resources.getColor(R.color.badge_color);
-        if (badgeColor == Color.TRANSPARENT) {
-            // Colors will be extracted per app icon, so a static palette won't work.
-            return null;
-        }
-        if (sBadgePalette == null) {
-            sBadgePalette = fromDominantColor(badgeColor, false);
-        }
-        return sBadgePalette;
-    }
-
-    /**
-     * Returns an IconPalette based on the folder_badge_color in colors.xml.
-     */
-    public static @NonNull IconPalette getFolderBadgePalette(Resources resources) {
-        if (sFolderBadgePalette == null) {
-            int badgeColor = resources.getColor(R.color.folder_badge_color);
-            sFolderBadgePalette = fromDominantColor(badgeColor, false);
-        }
-        return sFolderBadgePalette;
-    }
-
     /**
      * Resolves a color such that it has enough contrast to be used as the
      * color of an icon or text on the given background color.
@@ -208,30 +145,8 @@
         return ColorUtils.LABToColor(low, a, b);
     }
 
-    private static int getMutedColor(int color, float whiteScrimAlpha) {
+    public static int getMutedColor(int color, float whiteScrimAlpha) {
         int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
         return ColorUtils.compositeColors(whiteScrim, color);
     }
-
-    private static int getTextColorForBackground(int backgroundColor) {
-        return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
-    }
-
-    private static int getLowContrastColor(int color) {
-        return getLighterOrDarkerVersionOfColor(color, 1.5f);
-    }
-
-    private static int getLighterOrDarkerVersionOfColor(int color, float contrastRatio) {
-        int whiteMinAlpha = ColorUtils.calculateMinimumAlpha(Color.WHITE, color, contrastRatio);
-        int blackMinAlpha = ColorUtils.calculateMinimumAlpha(Color.BLACK, color, contrastRatio);
-        int translucentWhiteOrBlack;
-        if (whiteMinAlpha >= 0) {
-            translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.WHITE, whiteMinAlpha);
-        } else if (blackMinAlpha >= 0) {
-            translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.BLACK, blackMinAlpha);
-        } else {
-            translucentWhiteOrBlack = Color.WHITE;
-        }
-        return ColorUtils.compositeColors(translucentWhiteOrBlack, color);
-    }
 }
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 8c4738c..fdb6313 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.Intent.ShortcutIconResource;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -42,6 +41,7 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.IconCache;
+import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -51,6 +51,7 @@
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.util.Provider;
+import com.android.launcher3.util.Themes;
 
 /**
  * Helper methods for generating various launcher icons
@@ -69,7 +70,7 @@
      * Returns a bitmap suitable for the all apps view. If the package or the resource do not
      * exist, it returns null.
      */
-    public static Bitmap createIconBitmap(ShortcutIconResource iconRes, Context context) {
+    public static BitmapInfo createIconBitmap(ShortcutIconResource iconRes, Context context) {
         PackageManager packageManager = context.getPackageManager();
         // the resource
         try {
@@ -92,12 +93,13 @@
     /**
      * Returns a bitmap which is of the appropriate size to be displayed as an icon
      */
-    public static Bitmap createIconBitmap(Bitmap icon, Context context) {
+    public static BitmapInfo createIconBitmap(Bitmap icon, Context context) {
         final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
         if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
-            return icon;
+            return BitmapInfo.fromBitmap(icon);
         }
-        return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f);
+        return BitmapInfo.fromBitmap(
+                createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f));
     }
 
     /**
@@ -105,7 +107,7 @@
      * view or workspace. The icon is badged for {@param user}.
      * The bitmap is also visually normalized with other icons.
      */
-    public static Bitmap createBadgedIconBitmap(
+    public static BitmapInfo createBadgedIconBitmap(
             Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {
 
         IconNormalizer normalizer;
@@ -141,18 +143,20 @@
             }
         }
 
+        final Bitmap result;
         if (user != null && !Process.myUserHandle().equals(user)) {
             BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
             Drawable badged = context.getPackageManager().getUserBadgedIcon(
                     drawable, user);
             if (badged instanceof BitmapDrawable) {
-                return ((BitmapDrawable) badged).getBitmap();
+                result = ((BitmapDrawable) badged).getBitmap();
             } else {
-                return createIconBitmap(badged, context, 1f);
+                result = createIconBitmap(badged, context, 1f);
             }
         } else {
-            return bitmap;
+            result = bitmap;
         }
+        return BitmapInfo.fromBitmap(result);
     }
 
     /**
@@ -302,29 +306,23 @@
         return drawable;
     }
 
-    public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
+    public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
         return createShortcutIcon(shortcutInfo, context, true /* badged */);
     }
 
-    public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+    public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
             boolean badged) {
         return createShortcutIcon(shortcutInfo, context, badged, null);
     }
 
-    public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
-            final Bitmap fallbackIcon) {
-        // If the shortcut is pinned but no longer has an icon in the system,
-        // keep the current icon instead of reverting to the default icon.
-        return createShortcutIcon(shortcutInfo, context, true, Provider.of(fallbackIcon));
-    }
-
-    public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+    public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
             boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
         LauncherAppState app = LauncherAppState.getInstance(context);
         Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
                 .getShortcutIconDrawable(shortcutInfo,
                         app.getInvariantDeviceProfile().fillResIconDpi);
         IconCache cache = app.getIconCache();
+
         Bitmap unbadgedBitmap = null;
         if (unbadgedDrawable != null) {
             unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow(
@@ -334,27 +332,32 @@
                 unbadgedBitmap = fallbackIconProvider.get();
             }
             if (unbadgedBitmap == null) {
-                unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle());
+                unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
             }
         }
 
+        BitmapInfo result = new BitmapInfo();
         if (!badged) {
-            return unbadgedBitmap;
+            result.color = Themes.getColorAccent(context);
+            result.icon = unbadgedBitmap;
+            return result;
         }
 
         int size = app.getInvariantDeviceProfile().iconBitmapSize;
 
         final Bitmap unbadgedfinal = unbadgedBitmap;
-        final Bitmap badge = getShortcutInfoBadge(shortcutInfo, cache);
+        final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
 
-        return UiFactory.createFromRenderer(size, size, false, (c) -> {
+        result.color = badge.iconColor;
+        result.icon = UiFactory.createFromRenderer(size, size, false, (c) -> {
             ShadowGenerator.getInstance(context).recreateIcon(unbadgedfinal, c);
             badgeWithDrawable(c, new FastBitmapDrawable(badge), context);
         });
+        return result;
     }
 
-    public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
-        final Bitmap badgeBitmap;
+    public static ItemInfoWithIcon getShortcutInfoBadge(
+            ShortcutInfoCompat shortcutInfo, IconCache cache) {
         ComponentName cn = shortcutInfo.getActivity();
         if (cn != null) {
             // Get the app info for the source activity.
@@ -365,13 +368,12 @@
                     .addCategory(Intent.CATEGORY_LAUNCHER)
                     .setComponent(cn);
             cache.getTitleAndIcon(appInfo, false);
-            badgeBitmap = appInfo.iconBitmap;
+            return appInfo;
         } else {
             PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
             cache.getTitleAndIconForApp(pkgInfo, false);
-            badgeBitmap = pkgInfo.iconBitmap;
+            return pkgInfo;
         }
-        return badgeBitmap;
     }
 
     /**
diff --git a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
index 6df1ecb..fc20926 100644
--- a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
+++ b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
@@ -35,16 +35,43 @@
     private final RectF mDst = new RectF();
     public final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
+    /**
+     * Draws the bitmap split into three parts horizontally, with the middle part having width
+     * as {@link #EXTENSION_PX} in the center of the bitmap.
+     */
     public void draw(Bitmap bitmap, Canvas canvas, float left, float top, float right) {
-        int width = bitmap.getWidth();
         int height = bitmap.getHeight();
 
         mSrc.top = 0;
         mSrc.bottom = height;
-
         mDst.top = top;
         mDst.bottom = top + height;
+        draw3Patch(bitmap, canvas, left, right);
+    }
 
+
+    /**
+     * Draws the bitmap split horizontally into 3 parts (same as {@link #draw}) and split
+     * vertically into two parts, bottom part of size {@link #EXTENSION_PX} / 2 which is
+     * stretched vertically.
+     */
+    public void drawVerticallyStretched(Bitmap bitmap, Canvas canvas, float left, float top,
+            float right, float bottom) {
+        draw(bitmap, canvas, left, top, right);
+
+        // Draw bottom stretched region.
+        int height = bitmap.getHeight();
+        mSrc.top = height - EXTENSION_PX / 4;
+        mSrc.bottom = height;
+        mDst.top = top + height;
+        mDst.bottom = bottom;
+        draw3Patch(bitmap, canvas, left, right);
+    }
+
+
+
+    private void draw3Patch(Bitmap bitmap, Canvas canvas, float left, float right) {
+        int width = bitmap.getWidth();
         int halfWidth = width / 2;
 
         // Draw left edge
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 6d486ee..a40b6df 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -32,6 +32,7 @@
 import android.util.SparseArray;
 
 import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.anim.Interpolators;
 
 import java.lang.ref.WeakReference;
@@ -86,7 +87,7 @@
     private final Paint mProgressPaint;
 
     private Bitmap mShadowBitmap;
-    private int mIndicatorColor = 0;
+    private final int mIndicatorColor;
 
     private int mTrackAlpha;
     private float mTrackLength;
@@ -103,8 +104,8 @@
     /**
      * @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
      */
-    public PreloadIconDrawable(Bitmap b, Path progressPath, Context context) {
-        super(b);
+    public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
+        super(info);
         mContext = context;
         mProgressPath = progressPath;
         mScaledTrackPath = new Path();
@@ -113,6 +114,7 @@
         mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
         mProgressPaint.setStyle(Paint.Style.STROKE);
         mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+        mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
 
         setInternalProgress(0);
     }
@@ -266,9 +268,6 @@
             mScaledTrackPath.reset();
             mTrackAlpha = MAX_PAINT_ALPHA;
             setIsDisabled(true);
-        } else if (mIndicatorColor == 0) {
-            // Update the indicator color
-            mIndicatorColor = getIconPalette().getPreloadProgressColor(mContext);
         }
 
         if (progress < 1 && progress > 0) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index ccef9b7..b1d07f1 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.util.ContentWriter;
@@ -151,10 +152,9 @@
         info.user = user;
         info.itemType = itemType;
         info.title = getTitle();
-        info.iconBitmap = loadIcon(info);
         // the fallback icon
-        if (info.iconBitmap == null) {
-            info.iconBitmap = mIconCache.getDefaultIcon(info.user);
+        if (!loadIcon(info)) {
+            mIconCache.getDefaultIcon(info.user).applyTo(info);
         }
 
         // TODO: If there's an explicit component and we can't install that, delete it.
@@ -165,8 +165,7 @@
     /**
      * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
      */
-    protected Bitmap loadIcon(ShortcutInfo info) {
-        Bitmap icon = null;
+    protected boolean loadIcon(ShortcutInfo info) {
         if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
             String packageName = getString(iconPackageIndex);
             String resourceName = getString(iconResourceIndex);
@@ -174,24 +173,24 @@
                 info.iconResource = new ShortcutIconResource();
                 info.iconResource.packageName = packageName;
                 info.iconResource.resourceName = resourceName;
-                icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+                BitmapInfo iconInfo = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+                if (iconInfo != null) {
+                    iconInfo.applyTo(info);
+                    return true;
+                }
             }
         }
-        if (icon == null) {
-            // Failed to load from resource, try loading from DB.
-            byte[] data = getBlob(iconIndex);
-            try {
-                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;
-            }
+
+        // Failed to load from resource, try loading from DB.
+        byte[] data = getBlob(iconIndex);
+        try {
+            LauncherIcons.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length),
+                    mContext).applyTo(info);
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to load icon for info " + info, e);
+            return false;
         }
-        if (icon == null) {
-            Log.e(TAG, "Failed to load icon for info " + info);
-        }
-        return icon;
     }
 
     /**
@@ -211,9 +210,8 @@
         info.user = user;
         info.intent = intent;
 
-        info.iconBitmap = loadIcon(info);
         // the fallback icon
-        if (info.iconBitmap == null) {
+        if (!loadIcon(info)) {
             mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
         }
 
@@ -269,8 +267,7 @@
 
         mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
         if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
-            Bitmap icon = loadIcon(info);
-            info.iconBitmap = icon != null ? icon : info.iconBitmap;
+            loadIcon(info);
         }
 
         if (lai != null) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 310416f..b13b48a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -472,12 +472,12 @@
                                         public Bitmap get() {
                                             // If the pinned deep shortcut is no longer published,
                                             // use the last saved icon instead of the default.
-                                            return c.loadIcon(finalInfo);
+                                            return c.loadIcon(finalInfo)
+                                                    ? finalInfo.iconBitmap : null;
                                         }
                                     };
-                                    info.iconBitmap = LauncherIcons
-                                            .createShortcutIcon(pinnedShortcut, context,
-                                                    true /* badged */, fallbackIconProvider);
+                                    LauncherIcons.createShortcutIcon(pinnedShortcut, context,
+                                            true /* badged */, fallbackIconProvider).applyTo(info);
                                     if (pmHelper.isAppSuspended(
                                             pinnedShortcut.getPackage(), info.user)) {
                                         info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 470dadf..18ae61b 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -40,6 +40,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -191,9 +192,10 @@
                         // Update shortcuts which use iconResource.
                         if ((si.iconResource != null)
                                 && packageSet.contains(si.iconResource.packageName)) {
-                            Bitmap icon = LauncherIcons.createIconBitmap(si.iconResource, context);
-                            if (icon != null) {
-                                si.iconBitmap = icon;
+                            BitmapInfo iconInfo =
+                                    LauncherIcons.createIconBitmap(si.iconResource, context);
+                            if (iconInfo != null) {
+                                iconInfo.applyTo(si);
                                 infoUpdated = true;
                             }
                         }
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index c1f33a6..0b75e2c 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.Provider;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -92,8 +93,10 @@
                 }
                 for (final ShortcutInfo shortcutInfo : shortcutInfos) {
                     shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
-                    shortcutInfo.iconBitmap = LauncherIcons.createShortcutIcon(fullDetails, context,
-                            shortcutInfo.iconBitmap);
+                    // If the shortcut is pinned but no longer has an icon in the system,
+                    // keep the current icon instead of reverting to the default icon.
+                    LauncherIcons.createShortcutIcon(fullDetails, context, true,
+                            Provider.of(shortcutInfo.iconBitmap)).applyTo(shortcutInfo);
                     updatedShortcutInfos.add(shortcutInfo);
                 }
             }
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 2e9ac72..b033405 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.Provider;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -91,8 +92,10 @@
                     }
                     si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
                     si.updateFromDeepShortcutInfo(shortcut, context);
-                    si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context,
-                            si.iconBitmap);
+                    // If the shortcut is pinned but no longer has an icon in the system,
+                    // keep the current icon instead of reverting to the default icon.
+                    LauncherIcons.createShortcutIcon(shortcut, context, true,
+                            Provider.of(si.iconBitmap)).applyTo(si);
                 } else {
                     si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                 }
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 120de04..6918935 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -83,7 +83,7 @@
         if (mIconDrawable == null) {
             mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
                     .getInstance(context).getIconCache()
-                    .getDefaultIcon(statusBarNotification.getUser()));
+                    .getDefaultIcon(statusBarNotification.getUser()).icon);
             mBadgeIcon = Notification.BADGE_ICON_NONE;
         }
         intent = notification.contentIntent;
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 5bbd19c..2fefa85 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -18,6 +18,7 @@
 
 import android.app.Notification;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.support.annotation.Nullable;
 import android.view.MotionEvent;
@@ -114,12 +115,12 @@
         }
     }
 
-    public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
+    public void updateHeader(int notificationCount, int iconColor) {
         mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount));
-        if (palette != null) {
+        if (Color.alpha(iconColor) > 0) {
             if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
                 mNotificationHeaderTextColor =
-                        IconPalette.resolveContrastColor(mContext, palette.dominantColor,
+                        IconPalette.resolveContrastColor(mContext, iconColor,
                                 Themes.getAttrColor(mContext, R.attr.popupColorPrimary));
             }
             mHeaderText.setTextColor(mNotificationHeaderTextColor);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 6481183..cedf291 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -54,6 +54,7 @@
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherModel;
@@ -742,11 +743,11 @@
     }
 
     private void updateNotificationHeader() {
-        ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
+        ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
         BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
         if (mNotificationItemView != null && badgeInfo != null) {
-            IconPalette palette = mOriginalIcon.getBadgePalette();
-            mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
+            mNotificationItemView.updateHeader(
+                    badgeInfo.getNotificationCount(), itemInfo.iconColor);
         }
     }
 
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index abc186b..335426c 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -25,9 +25,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.badge.BadgeInfo;
-import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -42,7 +40,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Provides data for the popup menu that appears after long-clicking on apps.
@@ -94,8 +91,9 @@
                 mPackageUserToBadgeInfos.remove(postedPackageUserKey);
             }
         }
-        updateLauncherIconBadges(Utilities.singletonHashSet(postedPackageUserKey),
-                badgeShouldBeRefreshed);
+        if (badgeShouldBeRefreshed) {
+            mLauncher.updateIconBadges(Utilities.singletonHashSet(postedPackageUserKey));
+        }
     }
 
     @Override
@@ -106,7 +104,7 @@
             if (oldBadgeInfo.getNotificationKeys().size() == 0) {
                 mPackageUserToBadgeInfos.remove(removedPackageUserKey);
             }
-            updateLauncherIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
+            mLauncher.updateIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
             trimNotifications(mPackageUserToBadgeInfos);
         }
     }
@@ -142,7 +140,7 @@
         }
 
         if (!updatedBadges.isEmpty()) {
-            updateLauncherIconBadges(updatedBadges.keySet());
+            mLauncher.updateIconBadges(updatedBadges.keySet());
         }
         trimNotifications(updatedBadges);
     }
@@ -154,66 +152,6 @@
         }
     }
 
-    private void updateLauncherIconBadges(Set<PackageUserKey> updatedBadges) {
-        updateLauncherIconBadges(updatedBadges, true);
-    }
-
-    /**
-     * Updates the icons on launcher (workspace, folders, all apps) to refresh their badges.
-     * @param updatedBadges The packages whose badges should be refreshed (either a notification was
-     *                      added or removed, or the badge should show the notification icon).
-     * @param shouldRefresh An optional parameter that will allow us to only refresh badges that
-     *                      have actually changed. If a notification updated its content but not
-     *                      its count or icon, then the badge doesn't change.
-     */
-    private void updateLauncherIconBadges(Set<PackageUserKey> updatedBadges,
-            boolean shouldRefresh) {
-        Iterator<PackageUserKey> iterator = updatedBadges.iterator();
-        while (iterator.hasNext()) {
-            BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(iterator.next());
-            if (badgeInfo != null && !updateBadgeIcon(badgeInfo) && !shouldRefresh) {
-                // The notification icon isn't used, and the badge hasn't changed
-                // so there is no update to be made.
-                iterator.remove();
-            }
-        }
-        if (!updatedBadges.isEmpty()) {
-            mLauncher.updateIconBadges(updatedBadges);
-        }
-    }
-
-    /**
-     * Determines whether the badge should show a notification icon rather than a number,
-     * and sets that icon on the BadgeInfo if so.
-     * @param badgeInfo The badge to update with an icon (null if it shouldn't show one).
-     * @return Whether the badge icon potentially changed (true unless it stayed null).
-     */
-    private boolean updateBadgeIcon(BadgeInfo badgeInfo) {
-        boolean hadNotificationToShow = badgeInfo.hasNotificationToShow();
-        NotificationInfo notificationInfo = null;
-        NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
-        if (notificationListener != null && badgeInfo.getNotificationKeys().size() >= 1) {
-            // Look for the most recent notification that has an icon that should be shown in badge.
-            for (NotificationKeyData notificationKeyData : badgeInfo.getNotificationKeys()) {
-                String notificationKey = notificationKeyData.notificationKey;
-                StatusBarNotification[] activeNotifications = notificationListener
-                        .getActiveNotifications(new String[]{notificationKey});
-                if (activeNotifications.length == 1) {
-                    notificationInfo = new NotificationInfo(mLauncher, activeNotifications[0]);
-                    if (notificationInfo.shouldShowIconInBadge()) {
-                        // Found an appropriate icon.
-                        break;
-                    } else {
-                        // Keep looking.
-                        notificationInfo = null;
-                    }
-                }
-            }
-        }
-        badgeInfo.setNotificationToShow(notificationInfo);
-        return hadNotificationToShow || badgeInfo.hasNotificationToShow();
-    }
-
     public void setDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
         mDeepShortcutMap = deepShortcutMapCopy;
         if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 6c83d12..4adfb7c 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -148,8 +148,8 @@
                 final ShortcutInfoCompat shortcut = shortcuts.get(i);
                 final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
                 // Use unbadged icon for the menu.
-                si.iconBitmap = LauncherIcons.createShortcutIcon(
-                        shortcut, launcher, false /* badged */);
+                LauncherIcons.createShortcutIcon(shortcut, launcher, false /* badged */)
+                        .applyTo(si);
                 si.rank = i;
 
                 final DeepShortcutView view = shortcutViews.get(i);
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index da656db..ddcd8ae 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -43,23 +43,28 @@
     private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
 
     public SpringLoadedState(int id) {
-        super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, 1f, STATE_FLAGS);
+        super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
     public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
         DeviceProfile grid = launcher.getDeviceProfile();
         Workspace ws = launcher.getWorkspace();
-        if (grid.isVerticalBarLayout() || ws.getChildCount() == 0) {
+        if (ws.getChildCount() == 0) {
             return super.getWorkspaceScaleAndTranslation(launcher);
         }
 
+        if (grid.isVerticalBarLayout()) {
+            float scale = grid.workspaceSpringLoadShrinkFactor;
+            return new float[] {scale, 0, 0};
+        }
+
         float scale = grid.workspaceSpringLoadShrinkFactor;
         Rect insets = launcher.getDragLayer().getInsets();
 
         float scaledHeight = scale * ws.getNormalChildHeight();
         float shrunkTop = insets.top + grid.dropTargetBarSizePx;
-        float shrunkBottom = ws.getViewportHeight() - insets.bottom
+        float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
                 - grid.getWorkspacePadding(null).bottom
                 - grid.workspaceSpringLoadedBottomSpace;
         float totalShrunkSpace = shrunkBottom - shrunkTop;
diff --git a/src/com/android/launcher3/util/RunnableWithId.java b/src/com/android/launcher3/util/RunnableWithId.java
deleted file mode 100644
index 030eb09..0000000
--- a/src/com/android/launcher3/util/RunnableWithId.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.util;
-
-/**
- * A runnable with an id associated which is used for equality check.
- */
-public abstract class RunnableWithId implements Runnable {
-
-    public static final int RUNNABLE_ID_BIND_APPS = 1;
-    public static final int RUNNABLE_ID_BIND_WIDGETS = 2;
-
-    public final int id;
-
-    public RunnableWithId(int id) {
-        this.id = id;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return obj instanceof RunnableWithId && ((RunnableWithId) obj).id == id;
-    }
-}
diff --git a/src/com/android/launcher3/views/AllAppsScrim.java b/src/com/android/launcher3/views/AllAppsScrim.java
index d1354ad..17ddae3 100644
--- a/src/com/android/launcher3/views/AllAppsScrim.java
+++ b/src/com/android/launcher3/views/AllAppsScrim.java
@@ -20,10 +20,13 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.dynamicui.WallpaperColorInfo;
@@ -33,7 +36,8 @@
 
 import static com.android.launcher3.graphics.NinePatchDrawHelper.EXTENSION_PX;
 
-public class AllAppsScrim extends View implements WallpaperColorInfo.OnChangeListener {
+public class AllAppsScrim extends View implements WallpaperColorInfo.OnChangeListener, Insettable,
+        DeviceProfile.LauncherLayoutChangeListener {
 
     private static final int MAX_ALPHA = 235;
     private static final int MIN_ALPHA_PORTRAIT = 100;
@@ -42,6 +46,9 @@
     protected final WallpaperColorInfo mWallpaperColorInfo;
     private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
+    private final Rect mPadding = new Rect();
+    private final Rect mInsets = new Rect();
+    private final DeviceProfile mGrid;
     private final float mRadius;
     private final int mMinAlpha;
     private final int mAlphaRange;
@@ -74,7 +81,8 @@
         mShadowBlur = getResources().getDimension(R.dimen.all_apps_scrim_blur);
 
         Launcher launcher = Launcher.getLauncher(context);
-        mFillAlpha = mMinAlpha = launcher.getDeviceProfile().isVerticalBarLayout()
+        mGrid = launcher.getDeviceProfile();
+        mFillAlpha = mMinAlpha = mGrid.isVerticalBarLayout()
                 ? MIN_ALPHA_LANDSCAPE : MIN_ALPHA_PORTRAIT;
         mAlphaRange = MAX_ALPHA - mMinAlpha;
         mShadowBitmap = generateShadowBitmap();
@@ -90,11 +98,12 @@
         builder.shadowBlur = mShadowBlur;
 
         // Create the bitmap such that only the top half is drawn in the bitmap.
-        int bitmapHeight = Math.round(curveBot);
-        int bitmapWidth = bitmapHeight + EXTENSION_PX;
+        int bitmapWidth = 2 * Math.round(curveBot) + EXTENSION_PX;
+        int bitmapHeight = bitmapWidth / 2;
         Bitmap result = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
 
-        builder.bounds.set(0, mShadowBlur, bitmapWidth, 2 * curveBot + EXTENSION_PX);
+        float fullSize = 2 * curveBot + EXTENSION_PX - mShadowBlur;
+        builder.bounds.set(mShadowBlur, mShadowBlur, fullSize, fullSize);
         builder.drawShadow(new Canvas(result));
         return result;
     }
@@ -103,12 +112,15 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mWallpaperColorInfo.addOnChangeListener(this);
+        mGrid.addLauncherLayoutChangedListener(this);
+        onLauncherLayoutChanged();
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mWallpaperColorInfo.removeOnChangeListener(this);
+        mGrid.removeLauncherLayoutChangedListener(this);
     }
 
     @Override
@@ -126,10 +138,20 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        float edgeTop = getHeight() + mTranslateY - mDrawHeight;
+        float edgeTop = getHeight() + mTranslateY - mDrawHeight + mPadding.top;
+        float edgeRight = getWidth() - mPadding.right;
 
-        mShadowHelper.draw(mShadowBitmap, canvas, 0, edgeTop - mShadowBlur, getWidth());
-        canvas.drawRoundRect(0, edgeTop, getWidth(),
+        if (mPadding.left > 0 || mPadding.right > 0) {
+            mShadowHelper.drawVerticallyStretched(mShadowBitmap, canvas,
+                    mPadding.left - mShadowBlur,
+                    edgeTop - mShadowBlur,
+                    edgeRight + mShadowBlur,
+                    getHeight());
+        } else {
+            mShadowHelper.draw(mShadowBitmap, canvas, mPadding.left - mShadowBlur,
+                    edgeTop - mShadowBlur, edgeRight + mShadowBlur);
+        }
+        canvas.drawRoundRect(mPadding.left, edgeTop, edgeRight,
                 getHeight() + mRadius, mRadius, mRadius, mFillPaint);
     }
 
@@ -145,4 +167,23 @@
     public void setDrawRegion(float height) {
         mDrawHeight = height;
     }
+
+    @Override
+    public void setInsets(Rect insets) {
+        mInsets.set(insets);
+        onLauncherLayoutChanged();
+    }
+
+    @Override
+    public void onLauncherLayoutChanged() {
+        if (!mGrid.isVerticalBarLayout()) {
+            return;
+        }
+        mGrid.getWorkspacePadding(mPadding);
+        mPadding.bottom = 0;
+        mPadding.left += mInsets.left;
+        mPadding.top = mInsets.top;
+        mPadding.right += mInsets.right;
+        invalidate();
+    }
 }
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
new file mode 100644
index 0000000..37e5efcb
--- /dev/null
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -0,0 +1,83 @@
+/*
+ * 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.widget;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.widget.RemoteViews;
+
+import com.android.launcher3.R;
+
+/**
+ * A widget host views created while the host has not bind to the system service.
+ */
+public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView {
+
+    private final TextPaint mPaint;
+    private Layout mSetupTextLayout;
+
+    public DeferredAppWidgetHostView(Context context) {
+        super(context);
+        setWillNotDraw(false);
+
+        mPaint = new TextPaint();
+        mPaint.setColor(Color.WHITE);
+        mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
+                mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+        setBackgroundResource(R.drawable.bg_deferred_app_widget);
+    }
+
+    @Override
+    public void updateAppWidget(RemoteViews remoteViews) {
+        // Not allowed
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        AppWidgetProviderInfo info = getAppWidgetInfo();
+        if (info == null || TextUtils.isEmpty(info.label)) {
+            return;
+        }
+
+        // Use double padding so that there is extra space between background and text
+        int availableWidth = getMeasuredWidth() - 2 * (getPaddingLeft() + getPaddingRight());
+        if (mSetupTextLayout != null && mSetupTextLayout.getText().equals(info.label)
+                && mSetupTextLayout.getWidth() == availableWidth) {
+            return;
+        }
+        mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth,
+                Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mSetupTextLayout != null) {
+            canvas.translate(getPaddingLeft() * 2,
+                    (getHeight() - mSetupTextLayout.getHeight()) / 2);
+            mSetupTextLayout.draw(canvas);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
similarity index 92%
rename from src/com/android/launcher3/LauncherAppWidgetHostView.java
rename to src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 6f953e5..0b1474a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.widget;
 
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
@@ -37,6 +37,15 @@
 import android.widget.Advanceable;
 import android.widget.RemoteViews;
 
+import com.android.launcher3.CheckLongPressHelper;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.SimpleOnStylusPressListener;
+import com.android.launcher3.StylusEventHelper;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
 
@@ -59,14 +68,10 @@
 
     private final CheckLongPressHelper mLongPressHelper;
     private final StylusEventHelper mStylusEventHelper;
-    private final Launcher mLauncher;
-
-    private static final int DONT_REINFLATE = 0;
-    private static final int REINFLATE_ON_RESUME = 1;
-    private static final int REINFLATE_ON_CONFIG_CHANGE = 2;
+    protected final Launcher mLauncher;
 
     @ViewDebug.ExportedProperty(category = "launcher")
-    private int mReinflateStatus;
+    private boolean mReinflateOnConfigChange;
 
     private float mSlop;
 
@@ -128,12 +133,7 @@
         // Consequently, the widgets will be inflated for the orientation of the foreground activity
         // (framework issue). On resuming, we ensure that any widgets are inflated for the current
         // orientation.
-        if (mReinflateStatus == DONT_REINFLATE && !isSameOrientation()) {
-            mReinflateStatus = REINFLATE_ON_RESUME;
-            if (!mLauncher.waitUntilResume(new ReInflateRunnable())) {
-                mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
-            }
-        }
+        mReinflateOnConfigChange = !isSameOrientation();
     }
 
     private boolean isSameOrientation() {
@@ -485,40 +485,20 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        if (mReinflateStatus == REINFLATE_ON_CONFIG_CHANGE) {
-            // We are finally in the same orientation
-            reinflateIfNecessary();
+        // Only reinflate when the final configuration is same as the required configuration
+        if (mReinflateOnConfigChange && isSameOrientation()) {
+            mReinflateOnConfigChange = false;
+            if (isAttachedToWindow()) {
+                reInflate();
+            }
         }
     }
 
-    private void reinflateIfNecessary() {
-        if (!isSameOrientation()) {
-            // We cannot reinflate yet, wait until next config change
-            mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
-            return;
-        }
-
-        mReinflateStatus = DONT_REINFLATE;
-        if (isAttachedToWindow()) {
-            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
-            reinflate();
-        }
-    }
-
-    public void reinflate() {
+    public void reInflate() {
         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
         // Remove and rebind the current widget (which was inflated in the wrong
         // orientation), but don't delete it from the database
         mLauncher.removeItem(this, info, false  /* deleteFromDb */);
         mLauncher.bindAppWidget(info);
     }
-
-    private class ReInflateRunnable implements Runnable {
-        @Override
-        public void run() {
-            if (mReinflateStatus == REINFLATE_ON_RESUME) {
-                reinflateIfNecessary();
-            }
-        }
-    }
 }
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
similarity index 92%
rename from src/com/android/launcher3/PendingAppWidgetHostView.java
rename to src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index b86d413..6970833 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.widget;
 
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -32,10 +32,19 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.IconCache;
 import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
         implements OnClickListener, ItemInfoUpdateReceiver {
@@ -48,9 +57,6 @@
     private final LauncherAppWidgetInfo mInfo;
     private final int mStartState;
     private final boolean mDisabledForSafeMode;
-    private Launcher mLauncher;
-
-    private Bitmap mIcon;
 
     private Drawable mCenterDrawable;
     private Drawable mSettingIconDrawable;
@@ -64,7 +70,6 @@
             IconCache cache, boolean disabledForSafeMode) {
         super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
 
-        mLauncher = Launcher.getLauncher(context);
         mInfo = info;
         mStartState = info.restoreStatus;
         mDisabledForSafeMode = disabledForSafeMode;
@@ -122,53 +127,44 @@
 
     @Override
     public void reapplyItemInfo(ItemInfoWithIcon info) {
-        Bitmap icon = info.iconBitmap;
-        if (mIcon == icon) {
-            return;
-        }
-        mIcon = icon;
         if (mCenterDrawable != null) {
             mCenterDrawable.setCallback(null);
             mCenterDrawable = null;
         }
-        if (mIcon != null) {
+        if (info.iconBitmap != null) {
             // The view displays three modes,
             //   1) App icon in the center
             //   2) Preload icon in the center
             //   3) Setup icon in the center and app icon in the top right corner.
             DrawableFactory drawableFactory = DrawableFactory.get(getContext());
             if (mDisabledForSafeMode) {
-                FastBitmapDrawable disabledIcon = drawableFactory.newIcon(mIcon, mInfo);
+                FastBitmapDrawable disabledIcon = drawableFactory.newIcon(info);
                 disabledIcon.setIsDisabled(true);
                 mCenterDrawable = disabledIcon;
                 mSettingIconDrawable = null;
             } else if (isReadyForClickSetup()) {
-                mCenterDrawable = drawableFactory.newIcon(mIcon, mInfo);
+                mCenterDrawable = drawableFactory.newIcon(info);
                 mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
-
-                updateSettingColor();
+                updateSettingColor(info.iconColor);
             } else {
                 mCenterDrawable = DrawableFactory.get(getContext())
-                        .newPendingIcon(mIcon, getContext());
-                mCenterDrawable.setCallback(this);
+                        .newPendingIcon(info, getContext());
                 mSettingIconDrawable = null;
                 applyState();
             }
+            mCenterDrawable.setCallback(this);
             mDrawableSizeChanged = true;
         }
         invalidate();
     }
 
-    private void updateSettingColor() {
-        int color = Utilities.findDominantColorByHue(mIcon, 20);
+    private void updateSettingColor(int dominantColor) {
         // Make the dominant color bright.
         float[] hsv = new float[3];
-        Color.colorToHSV(color, hsv);
+        Color.colorToHSV(dominantColor, hsv);
         hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
         hsv[2] = 1;
-        color = Color.HSVToColor(hsv);
-
-        mSettingIconDrawable.setColorFilter(color,  PorterDuff.Mode.SRC_IN);
+        mSettingIconDrawable.setColorFilter(Color.HSVToColor(hsv),  PorterDuff.Mode.SRC_IN);
     }
 
     @Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
index 485e97b..24236d8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
@@ -43,7 +43,7 @@
     };
 
     public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS);
+        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
@@ -77,4 +77,9 @@
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return PAGE_ALPHA_PROVIDER;
     }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        return 0f;
+    }
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
index 19967ae..3468827 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
@@ -40,7 +40,7 @@
             FLAG_DISABLE_PAGE_CLIPPING;
 
     public OverviewState(int id) {
-        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS);
+        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
@@ -53,9 +53,9 @@
         int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight());
         Rect workspacePadding = grid.getWorkspacePadding(null);
         int workspaceTop = insets.top + workspacePadding.top;
-        int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom;
+        int workspaceBottom = ws.getHeight() - insets.bottom - workspacePadding.bottom;
         int overviewTop = insets.top;
-        int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight;
+        int overviewBottom = ws.getHeight() - insets.bottom - overviewButtonBarHeight;
         int workspaceOffsetTopEdge =
                 workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
         int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index bbb6772..cf90afd 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -9,12 +9,12 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
+import android.graphics.Color;
 import android.os.Process;
 import android.os.UserHandle;
 import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.provider.ProviderTestRule;
-import android.support.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppFilter;
@@ -27,6 +27,7 @@
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.graphics.BitmapInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.TestLauncherProvider;
@@ -208,7 +209,7 @@
             CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
             if (entry == null) {
                 entry = new CacheEntry();
-                entry.icon = getDefaultIcon(user);
+                getDefaultIcon(user).applyTo(entry);
             }
             return entry;
         }
@@ -216,6 +217,7 @@
         public void addCache(ComponentName key, String title) {
             CacheEntry entry = new CacheEntry();
             entry.icon = newIcon();
+            entry.color = Color.RED;
             entry.title = title;
             mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
         }
@@ -225,8 +227,8 @@
         }
 
         @Override
-        protected Bitmap makeDefaultIcon(UserHandle user) {
-            return newIcon();
+        protected BitmapInfo makeDefaultIcon(UserHandle user) {
+            return BitmapInfo.fromBitmap(newIcon());
         }
     }
 }
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 173c556..dfefa31 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -17,6 +17,7 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.graphics.BitmapInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -138,7 +139,8 @@
         assertTrue(mLoaderCursor.moveToNext());
 
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
-        when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
+        when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
+                .thenReturn(BitmapInfo.fromBitmap(icon));
         ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
         assertEquals(icon, info.iconBitmap);
         assertEquals("my-shortcut", info.title);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 87103d7..32f90a6 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -30,12 +30,12 @@
 import android.support.test.uiautomator.UiSelector;
 
 import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.PendingAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;