Snap for 4818534 from bf29023212a107481b6cbf3dbe4fda52c6f50180 to pi-release

Change-Id: I06c92e93af46c5f35d997815ed1392cca2f05c46
diff --git a/proguard.flags b/proguard.flags
index 555d13e..e401116 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -112,6 +112,11 @@
     public <init>(...);
 }
 
+# UserEventDispatcherExtension
+-keep class com.android.quickstep.logging.UserEventDispatcherExtension {
+    public <init>(...);
+}
+
 -keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
   *;
 }
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index cab20a3..06e6a92 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -110,6 +110,8 @@
   QUICK_SCRUB_BUTTON = 12;
   CLEAR_ALL_BUTTON = 13;
   CANCEL_TARGET = 14;
+  TASK_PREVIEW = 15;
+  SPLIT_SCREEN_TARGET = 16;
 }
 
 enum TipType {
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index 7ecab32..84e13ad 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -24,6 +24,7 @@
         android:id="@+id/overview_panel_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:clipChildren="false"
     >
         <include layout="@layout/overview_clear_all_button"/>
 
@@ -33,6 +34,7 @@
             android:layout_height="match_parent"
             android:clipChildren="false"
             android:clipToPadding="false"
+            android:outlineProvider="none"
             android:focusableInTouchMode="true"
             android:theme="@style/HomeScreenElementTheme"
         >
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 919afdb..840b040 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:clipChildren="false"
     android:visibility="invisible"
 >
     <include layout="@layout/overview_clear_all_button"/>
@@ -28,6 +29,7 @@
         android:layout_height="match_parent"
         android:clipChildren="false"
         android:clipToPadding="false"
+        android:outlineProvider="none"
         android:focusableInTouchMode="true"
         android:accessibilityPaneTitle="@string/accessibility_recent_apps"
         android:theme="@style/HomeScreenElementTheme"
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 7673f69..c294376 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -18,5 +18,5 @@
 
     <string name="overview_callbacks_class" translatable="false"></string>
 
-    <string name="user_event_dispatcher_class" translatable="false">com.google.quickstep.logging.UserEventDispatcherExtension</string>
+    <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 6703bb5..2630edb 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -50,6 +50,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -105,6 +106,7 @@
     private static final int APP_LAUNCH_ALPHA_DURATION = 50;
 
     public static final int RECENTS_LAUNCH_DURATION = 336;
+    public static final int RECENTS_QUICKSCRUB_LAUNCH_DURATION = 300;
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
 
@@ -236,8 +238,14 @@
         return bounds;
     }
 
-    public void setRemoteAnimationProvider(RemoteAnimationProvider animationProvider) {
+    public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
+            CancellationSignal cancellationSignal) {
         mRemoteAnimationProvider = animationProvider;
+        cancellationSignal.setOnCancelListener(() -> {
+            if (animationProvider == mRemoteAnimationProvider) {
+                mRemoteAnimationProvider = null;
+            }
+        });
     }
 
     /**
@@ -253,15 +261,21 @@
         RecentsView recentsView = mLauncher.getOverviewPanel();
         boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
         boolean skipLauncherChanges = !launcherClosing;
+        boolean isLaunchingFromQuickscrub =
+                recentsView.getQuickScrubController().isWaitingForTaskLaunch();
 
         TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
         if (taskView == null) {
             return false;
         }
 
+        int duration = isLaunchingFromQuickscrub
+                ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
+                : RECENTS_LAUNCH_DURATION;
+
         ClipAnimationHelper helper = new ClipAnimationHelper();
         target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
-                .setDuration(RECENTS_LAUNCH_DURATION));
+                .setDuration(duration));
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
@@ -270,7 +284,7 @@
         if (launcherClosing) {
             launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
+            launcherAnim.setDuration(duration);
 
             // Make sure recents gets fixed up by resetting task alphas and scales, etc.
             windowAnimEndListener = new AnimatorListenerAdapter() {
@@ -282,11 +296,10 @@
             };
         } else {
             AnimatorPlaybackController controller =
-                    mLauncher.getStateManager()
-                            .createAnimationToNewWorkspace(NORMAL, RECENTS_LAUNCH_DURATION);
+                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, duration);
             controller.dispatchOnStart();
             childStateAnimation = controller.getTarget();
-            launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+            launcherAnim = controller.getAnimationPlayer().setDuration(duration);
             windowAnimEndListener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -581,11 +594,8 @@
 
                 float offsetX = (scaledWindowWidth - iconWidth) / 2;
                 float offsetY = (scaledWindowHeight - iconHeight) / 2;
-                if (mLauncher.isInMultiWindowModeCompat()) {
-                    mFloatingView.getLocationOnScreen(floatingViewBounds);
-                } else {
-                    mFloatingView.getLocationInWindow(floatingViewBounds);
-                }
+                mFloatingView.getLocationOnScreen(floatingViewBounds);
+
                 float transX0 = floatingViewBounds[0] - offsetX;
                 float transY0 = floatingViewBounds[1] - offsetY;
                 matrix.postTranslate(transX0, transY0);
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index e5e377f..08b6bfc 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Handler;
 
 import com.android.launcher3.states.InternalStateHandler;
@@ -48,10 +49,11 @@
 
             // Set a one-time animation provider. After the first call, this will get cleared.
             // TODO: Probably also check the intended target id.
+            CancellationSignal cancellationSignal = new CancellationSignal();
             appTransitionManager.setRemoteAnimationProvider((targets) -> {
 
                 // On the first call clear the reference.
-                appTransitionManager.setRemoteAnimationProvider(null);
+                cancellationSignal.cancel();
                 RemoteAnimationProvider provider = mRemoteAnimationProvider;
                 mRemoteAnimationProvider = null;
 
@@ -59,7 +61,7 @@
                     return provider.createWindowAnimation(targets);
                 }
                 return null;
-            });
+            }, cancellationSignal);
         }
         OverviewCallbacks.get(launcher).onInitOverviewTransition();
         return mOnInitListener.test(launcher, alreadyOnHome);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index 43d9822..2645302 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -34,7 +34,7 @@
     /**
      * Vertical transition of the task previews relative to the full container.
      */
-    public static final float OVERVIEW_TRANSLATION_FACTOR = 0.5f;
+    public static final float OVERVIEW_TRANSLATION_FACTOR = 0.4f;
 
     private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_DISABLE_INTERACTION
             | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
@@ -76,4 +76,10 @@
         return Math.min(Math.min(dp.availableHeightPx / usedHeight,
                 dp.availableWidthPx / usedWidth), MAX_PREVIEW_SCALE_UP);
     }
+
+    @Override
+    public void onStateDisabled(Launcher launcher) {
+        super.onStateDisabled(launcher);
+        launcher.<RecentsView>getOverviewPanel().getQuickScrubController().cancelActiveQuickscrub();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index e3aabd6..ea27eb2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -76,15 +76,16 @@
         }
         PropertySetter setter = config.getPropertySetter(builder);
         float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
-        Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
-        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0], scaleInterpolator);
-        Interpolator transYInterpolator = scaleInterpolator;
+        Interpolator scaleAndTransYInterpolator = builder.getInterpolator(
+                ANIM_OVERVIEW_SCALE, LINEAR);
         if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) {
-            transYInterpolator = Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
-                    QUICK_SCRUB_TRANSLATION_Y_FACTOR);
+            scaleAndTransYInterpolator = Interpolators.clampToProgress(
+                    QUICK_SCRUB_START_INTERPOLATOR, 0, QUICK_SCRUB_TRANSLATION_Y_FACTOR);
         }
+        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0],
+                scaleAndTransYInterpolator);
         setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
-                transYInterpolator);
+                scaleAndTransYInterpolator);
         setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
                 builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index d0c7b21..dd5dcbe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -25,14 +25,19 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.content.Context;
+import android.os.CancellationSignal;
 import android.util.Base64;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppTransitionManagerImpl;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.LauncherStateManager.StateHandler;
@@ -41,6 +46,8 @@
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.RecentsModel;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -179,6 +186,23 @@
         }
     }
 
+    public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
+            CancellationSignal cancellationSignal) {
+        LauncherAppTransitionManagerImpl appTransitionManager =
+                (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+        appTransitionManager.setRemoteAnimationProvider((targets) -> {
+
+            // On the first call clear the reference.
+            cancellationSignal.cancel();
+
+            ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+            fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets));
+            AnimatorSet anim = new AnimatorSet();
+            anim.play(fadeAnimation);
+            return anim;
+        }, cancellationSignal);
+    }
+
     public static boolean dumpActivity(Activity activity, PrintWriter writer) {
         if (!Utilities.IS_DEBUG_DEVICE) {
             return false;
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index e202c57..52a6dd5 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -82,7 +83,8 @@
     void onQuickInteractionStart(T activity, @Nullable RunningTaskInfo taskInfo,
             boolean activityVisible);
 
-    float getTranslationYForQuickScrub(T activity);
+    float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
+            Context context);
 
     void executeOnWindowAvailable(T activity, Runnable action);
 
@@ -151,10 +153,15 @@
         }
 
         @Override
-        public float getTranslationYForQuickScrub(Launcher activity) {
-            LauncherRecentsView recentsView = activity.getOverviewPanel();
-            return recentsView.computeTranslationYForFactor(
-                    FastOverviewState.OVERVIEW_TRANSLATION_FACTOR);
+        public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
+                Context context) {
+            // The padding calculations are exactly same as that of RecentsView.setInsets
+            int topMargin = context.getResources()
+                    .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+            int paddingTop = targetRect.rect.top - topMargin - dp.getInsets().top;
+            int paddingBottom = dp.availableHeightPx + dp.getInsets().top - targetRect.rect.bottom;
+
+            return FastOverviewState.OVERVIEW_TRANSLATION_FACTOR * (paddingBottom - paddingTop);
         }
 
         @Override
@@ -380,7 +387,8 @@
         }
 
         @Override
-        public float getTranslationYForQuickScrub(RecentsActivity activity) {
+        public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
+                Context context) {
             return 0;
         }
 
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 84dfa45..84dfdbd 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -46,9 +46,9 @@
         mUpdateCallback = updateCallback;
     }
 
-    public ObjectAnimator animateToValue(float v) {
+    public ObjectAnimator animateToValue(float start, float end) {
         cancelAnimation();
-        mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, v);
+        mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);
         mValueAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animator) {
diff --git a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
index b92678a..8e83bd0 100644
--- a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
@@ -64,6 +64,16 @@
     }
 
     @Override
+    public void onQuickStep(MotionEvent ev) {
+        mTarget.onQuickStep(ev);
+    }
+
+    @Override
+    public void onCommand(int command) {
+        mTarget.onCommand(command);
+    }
+
+    @Override
     public void preProcessMotionEvent(MotionEvent ev) {
         mVelocityTracker.addMovement(ev);
     }
@@ -92,6 +102,11 @@
         return target == null ? true : target.deferNextEventToMainThread();
     }
 
+    @Override
+    public void onShowOverviewFromAltTab() {
+        mTarget.onShowOverviewFromAltTab();
+    }
+
     public interface DeferredTouchProvider {
 
         TouchConsumer createTouchConsumer(VelocityTracker tracker);
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
index 15f5aa5..f73be6c 100644
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -55,6 +55,8 @@
             ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
     private static final int ACTION_QUICK_STEP =
             ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
+    private static final int ACTION_COMMAND =
+            ACTION_VIRTUAL | (8 << ACTION_POINTER_INDEX_SHIFT);
 
     private final EventArray mEmptyArray = new EventArray();
     private final Object mExecutionLock = new Object();
@@ -165,6 +167,9 @@
                         case ACTION_QUICK_STEP:
                             mConsumer.onQuickStep(event);
                             break;
+                        case ACTION_COMMAND:
+                            mConsumer.onCommand(event.getSource());
+                            break;
                         default:
                             Log.e(TAG, "Invalid virtual event: " + event.getAction());
                     }
@@ -222,6 +227,12 @@
         queueVirtualAction(ACTION_DEFER_INIT, 0);
     }
 
+    public void onCommand(int command) {
+        MotionEvent ev = MotionEvent.obtain(0, 0, ACTION_COMMAND, 0, 0, 0);
+        ev.setSource(command);
+        queueNoPreProcess(ev);
+    }
+
     public TouchConsumer getConsumer() {
         return mConsumer;
     }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 9ba3328..c856282 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -37,6 +37,7 @@
 import android.os.Bundle;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.MotionEvent;
@@ -69,6 +70,7 @@
 
     private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
 
+    private final SparseArray<RecentsAnimationState> mAnimationStates = new SparseArray<>();
     private final RunningTaskInfo mRunningTask;
     private final RecentsModel mRecentsModel;
     private final Intent mHomeIntent;
@@ -183,7 +185,7 @@
             case ACTION_UP: {
                 TraceHelper.endSection("TouchInt");
 
-                finishTouchTracking();
+                finishTouchTracking(ev);
                 break;
             }
         }
@@ -212,8 +214,9 @@
 
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
         // Create the shared handler
+        RecentsAnimationState animationState = new RecentsAnimationState();
         final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
-                mRunningTask, this, touchTimeMs, mActivityControlHelper);
+                animationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper);
 
         // Preload the plan
         mRecentsModel.loadTasks(mRunningTask.id, null);
@@ -237,31 +240,7 @@
                     public void onHandleAssistData(Bundle bundle) {
                         mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
                     }
-                },
-                new RecentsAnimationListener() {
-                    public void onAnimationStart(
-                            RecentsAnimationControllerCompat controller,
-                            RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
-                            Rect minimizedHomeBounds) {
-                        if (mInteractionHandler == handler) {
-                            TraceHelper.partitionSection("RecentsController", "Received");
-                            handler.onRecentsAnimationStart(controller,
-                                    new RemoteAnimationTargetSet(apps, MODE_CLOSING),
-                                    homeContentInsets, minimizedHomeBounds);
-                        } else {
-                            TraceHelper.endSection("RecentsController", "Finishing no handler");
-                            controller.finish(false /* toHome */);
-                        }
-                    }
-
-                    public void onAnimationCanceled() {
-                        TraceHelper.endSection("RecentsController",
-                                "Cancelled: " + mInteractionHandler);
-                        if (mInteractionHandler == handler) {
-                            handler.onRecentsAnimationCanceled();
-                        }
-                    }
-                }, null, null);
+                }, animationState, null, null);
 
         if (Looper.myLooper() != Looper.getMainLooper()) {
             startActivity.run();
@@ -277,12 +256,22 @@
         }
     }
 
+    @Override
+    public void onCommand(int command) {
+        RecentsAnimationState state = mAnimationStates.get(command);
+        if (state != null) {
+            state.execute();
+        }
+    }
+
     /**
      * Called when the gesture has ended. Does not correlate to the completion of the interaction as
      * the animation can still be running.
      */
-    private void finishTouchTracking() {
+    private void finishTouchTracking(MotionEvent ev) {
         if (mPassedInitialSlop && mInteractionHandler != null) {
+            mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
+
             mVelocityTracker.computeCurrentVelocity(1000,
                     ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
 
@@ -398,4 +387,55 @@
         // TODO: Consider also check if the eventQueue is using mainThread of not.
         return mInteractionHandler != null;
     }
+
+    private class RecentsAnimationState implements RecentsAnimationListener {
+
+        private final int id;
+
+        private RecentsAnimationControllerCompat mController;
+        private RemoteAnimationTargetSet mTargets;
+        private Rect mHomeContentInsets;
+        private Rect mMinimizedHomeBounds;
+        private boolean mCancelled;
+
+        public RecentsAnimationState() {
+            id = mAnimationStates.size();
+            mAnimationStates.put(id, this);
+        }
+
+        @Override
+        public void onAnimationStart(
+                RecentsAnimationControllerCompat controller,
+                RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
+                Rect minimizedHomeBounds) {
+            mController = controller;
+            mTargets = new RemoteAnimationTargetSet(apps, MODE_CLOSING);
+            mHomeContentInsets = homeContentInsets;
+            mMinimizedHomeBounds = minimizedHomeBounds;
+            mEventQueue.onCommand(id);
+        }
+
+        @Override
+        public void onAnimationCanceled() {
+            mCancelled = true;
+            mEventQueue.onCommand(id);
+        }
+
+        public void execute() {
+            if (mInteractionHandler == null || mInteractionHandler.id != id) {
+                if (!mCancelled && mController != null) {
+                    TraceHelper.endSection("RecentsController", "Finishing no handler");
+                    mController.finish(false /* toHome */);
+                }
+            } else if (mCancelled) {
+                TraceHelper.endSection("RecentsController",
+                        "Cancelled: " + mInteractionHandler);
+                mInteractionHandler.onRecentsAnimationCanceled();
+            } else {
+                TraceHelper.partitionSection("RecentsController", "Received");
+                mInteractionHandler.onRecentsAnimationStart(mController, mTargets,
+                        mHomeContentInsets, mMinimizedHomeBounds);
+            }
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 8e1a3d5..7a79c6f 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -40,7 +40,7 @@
 public class QuickScrubController implements OnAlarmListener {
 
     public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
-    public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 150;
+    public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 200;
     // We want the translation y to finish faster than the rest of the animation.
     public static final float QUICK_SCRUB_TRANSLATION_Y_FACTOR = 5f / 6;
     public static final Interpolator QUICK_SCRUB_START_INTERPOLATOR = FAST_OUT_SLOW_IN;
@@ -132,6 +132,17 @@
         }
     }
 
+    public void cancelActiveQuickscrub() {
+        if (!mInQuickScrub) {
+            return;
+        }
+        Log.d(TAG, "Quickscrub was active, cancelling");
+        mInQuickScrub = false;
+        mActivityControlHelper = null;
+        mOnFinishedTransitionToQuickScrubRunnable = null;
+        mRecentsView.setNextPageSwitchRunnable(null);
+    }
+
     /**
      * Initializes the UI for quick scrub, returns true if success.
      */
@@ -145,6 +156,10 @@
         return true;
     }
 
+    public boolean isWaitingForTaskLaunch() {
+        return mWaitingForTaskLaunch;
+    }
+
     /**
      * Attempts to go to normal overview or back to home, so UI doesn't prevent user interaction.
      */
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index aed9959..2c3f77f 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -20,6 +20,7 @@
 import com.android.launcher3.MainProcessInitializer;
 import com.android.systemui.shared.system.ThreadedRendererCompat;
 
+@SuppressWarnings("unused")
 public class QuickstepProcessInitializer extends MainProcessInitializer {
 
     public QuickstepProcessInitializer(Context context) { }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 30b10b0..34d42ac 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -20,6 +20,8 @@
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+
+import java.util.ArrayList;
 import java.util.concurrent.ExecutorService;
 
 /**
@@ -27,6 +29,10 @@
  */
 public class RecentsAnimationWrapper {
 
+    // A list of callbacks to run when we receive the recents animation target. There are different
+    // than the state callbacks as these run on the current worker thread.
+    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
+
     public RemoteAnimationTargetSet targetSet;
 
     private RecentsAnimationControllerCompat mController;
@@ -46,6 +52,21 @@
         if (mInputConsumerEnabled) {
             enableInputConsumer();
         }
+
+        if (!mCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mCallbacks)) {
+                action.run();
+            }
+            mCallbacks.clear();
+        }
+    }
+
+    public synchronized void runOnInit(Runnable action) {
+        if (targetSet == null) {
+            mCallbacks.add(action);
+        } else {
+            action.run();
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 228af8e..f82ff8c 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -35,6 +37,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
@@ -166,7 +169,8 @@
                         Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
                         return;
                     }
-
+                    activity.getUserEventDispatcher().logActionOnControl(TAP,
+                            LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
                     // Add a device profile change listener to kick off animating the side tasks
                     // once we enter multiwindow mode and relayout
                     activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
index aa844d8..4cecffa 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -48,6 +48,8 @@
 
     default void onQuickStep(MotionEvent ev) { }
 
+    default void onCommand(int command) { }
+
     /**
      * Called on the binder thread to allow the consumer to process the motion event before it is
      * posted on a handler thread.
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 191c237..b1663b1 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -16,6 +16,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -29,6 +30,7 @@
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -36,6 +38,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.support.annotation.AnyThread;
 import android.support.annotation.UiThread;
 import android.support.annotation.WorkerThread;
@@ -51,6 +54,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.logging.UserEventDispatcher;
@@ -65,11 +69,12 @@
 import com.android.quickstep.ActivityControlHelper.LayoutListener;
 import com.android.quickstep.TouchConsumer.InteractionType;
 import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.TransformedRect;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.TransformedRect;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -167,6 +172,12 @@
 
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
+    // An increasing identifier per single instance of OtherActivityTouchConsumer. Generally one
+    // instance of OtherActivityTouchConsumer will only have one swipe handle, but sometimes we can
+    // end up with multiple handlers if we get recents command in the middle of a swipe gesture.
+    // This is used to match the corresponding activity manager callbacks in
+    // OtherActivityTouchConsumer
+    public final int id;
     private final Context mContext;
     private final ActivityControlHelper<T> mActivityControlHelper;
     private final ActivityInitListener mActivityInitListener;
@@ -199,6 +210,7 @@
             InputConsumerController.getRecentsAnimationInputConsumer();
 
     private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
+
     private final long mTouchTimeMs;
     private long mLauncherFrameDrawnTime;
 
@@ -207,8 +219,9 @@
     private float mLongSwipeDisplacement = 0;
     private LongSwipeHelper mLongSwipeController;
 
-    WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs,
-            ActivityControlHelper<T> controller) {
+    WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context,
+            long touchTimeMs, ActivityControlHelper<T> controller) {
+        this.id = id;
         mContext = context;
         mRunningTaskInfo = runningTaskInfo;
         mRunningTaskId = runningTaskInfo.id;
@@ -453,11 +466,40 @@
                     "Can't change interaction type to " + interactionType);
         }
         mInteractionType = interactionType;
+        mRecentsAnimationWrapper.runOnInit(this::shiftAnimationDestinationForQuickscrub);
 
         setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
 
         // Start the window animation without waiting for launcher.
-        animateToProgress(1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR);
+        animateToProgress(mCurrentShift.value, 1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR);
+    }
+
+    private void shiftAnimationDestinationForQuickscrub() {
+        TransformedRect tempRect = new TransformedRect();
+        mActivityControlHelper
+                .getSwipeUpDestinationAndLength(mDp, mContext, mInteractionType, tempRect);
+        mClipAnimationHelper.updateTargetRect(tempRect);
+
+        float offsetY =
+                mActivityControlHelper.getTranslationYForQuickScrub(tempRect, mDp, mContext);
+        float scale, offsetX;
+        Resources res = mContext.getResources();
+
+        if (ActivityManagerWrapper.getInstance().getRecentTasks(2, UserHandle.myUserId()).size()
+                < 2) {
+            // There are not enough tasks, we don't need to shift
+            offsetX = 0;
+            scale = 1;
+        } else {
+            offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing)
+                    + tempRect.rect.width();
+            float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 +
+                    res.getDimensionPixelSize(R.dimen.recents_page_spacing);
+            float interpolation = Math.min(1, offsetX / distanceToReachEdge);
+            scale = TaskView.getCurveScaleForInterpolation(interpolation);
+        }
+        mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY,
+                QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
     }
 
     @WorkerThread
@@ -606,11 +648,13 @@
     private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
         long duration = MAX_SWIPE_DURATION;
         final float endShift;
+        final float startShift;
         if (!isFling) {
             endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted ? 1 : 0;
             long expectedDuration = Math.abs(Math.round((endShift - mCurrentShift.value)
                     * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
             duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
+            startShift = mCurrentShift.value;
         } else {
             endShift = endVelocity < 0 ? 1 : 0;
             float minFlingVelocity = mContext.getResources()
@@ -624,9 +668,11 @@
                 long baseDuration = Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
                 duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
             }
+            startShift = Utilities.boundToRange(mCurrentShift.value - endVelocity * SINGLE_FRAME_MS
+                            / (mTransitionDragLength * 1000), 0, 1);
         }
 
-        animateToProgress(endShift, duration, DEACCEL);
+        animateToProgress(startShift, endShift, duration, DEACCEL);
     }
 
     private void doLogGesture(boolean toLauncher) {
@@ -646,9 +692,10 @@
     }
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
-    private void animateToProgress(float progress, long duration, Interpolator interpolator) {
-        mIsGoingToHome = Float.compare(progress, 1) == 0;
-        ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
+    private void animateToProgress(float start, float end, long duration,
+            Interpolator interpolator) {
+        mIsGoingToHome = Float.compare(end, 1) == 0;
+        ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
         anim.setInterpolator(interpolator);
         anim.addListener(new AnimationSuccessListener() {
             @Override
@@ -658,7 +705,7 @@
                         : STATE_SCALED_CONTROLLER_APP);
             }
         });
-        anim.start();
+        mRecentsAnimationWrapper.runOnInit(anim::start);
     }
 
     @UiThread
@@ -700,6 +747,7 @@
 
         mRecentsView.setRunningTaskHidden(false);
         mRecentsView.setRunningTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
+        mQuickScrubController.cancelActiveQuickscrub();
     }
 
     private void notifyTransitionCancelled() {
@@ -785,30 +833,6 @@
 
         // Inform the last progress in case we skipped before.
         mQuickScrubController.onQuickScrubProgress(mCurrentQuickScrubProgress);
-
-        // Make sure the window follows the first task if it moves, e.g. during quick scrub.
-        TaskView firstTask = mRecentsView.getPageAt(0);
-        // The first task may be null if we are swiping up from a task that does not
-        // appear in the list (i.e. the assistant)
-        if (firstTask != null) {
-            int scrollForFirstTask = mRecentsView.getScrollForPage(0);
-            int scrollForSecondTask = mRecentsView.getChildCount() > 1
-                    ? mRecentsView.getScrollForPage(1) : scrollForFirstTask;
-            float offsetFromFirstTask = scrollForFirstTask - scrollForSecondTask;
-
-            TransformedRect tempRect = new TransformedRect();
-            mActivityControlHelper
-                    .getSwipeUpDestinationAndLength(mDp, mContext, mInteractionType, tempRect);
-            float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 +
-                    mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
-            float interpolation = Math.min(1,
-                    Math.abs(offsetFromFirstTask) / distanceToReachEdge);
-
-            mClipAnimationHelper.offsetTarget(
-                    firstTask.getCurveScaleForInterpolation(interpolation), offsetFromFirstTask,
-                    mActivityControlHelper.getTranslationYForQuickScrub(mActivity),
-                    QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
-        }
     }
 
     private void onFinishedTransitionToQuickScrub() {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index c5d74c7..9e2de33 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -20,10 +20,8 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
@@ -73,21 +71,4 @@
         // Just use the activity task size for multi-window as well.
         return false;
     }
-
-    @Override
-    public void addTaskAccessibilityActionsExtra(AccessibilityNodeInfo info) {
-        info.addAction(
-                new AccessibilityNodeInfo.AccessibilityAction(
-                        R.string.recents_clear_all,
-                        getContext().getText(R.string.recents_clear_all)));
-    }
-
-    @Override
-    public boolean performTaskAccessibilityActionExtra(int action) {
-        if (action == R.string.recents_clear_all) {
-            dismissAllTasks();
-            return true;
-        }
-        return false;
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index 04153cc..6dff187 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -15,10 +15,9 @@
  */
 package com.android.quickstep.logging;
 
+import android.content.Context;
 import android.util.Log;
 
-import static com.android.launcher3.logging.LoggerUtils.newAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
 import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
@@ -27,20 +26,20 @@
 import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
 
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.nano.LauncherDumpProto;
-import com.android.launcher3.userevent.nano.LauncherLogExtensions;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.systemui.shared.system.LauncherEventUtil;
 import com.android.systemui.shared.system.MetricsLoggerCompat;
 
 /**
  * This class handles AOSP MetricsLogger function calls and logging around
  * quickstep interactions.
  */
+@SuppressWarnings("unused")
 public class UserEventDispatcherExtension extends UserEventDispatcher {
 
     private static final String TAG = "UserEventDispatcher";
 
+    public UserEventDispatcherExtension(Context context) { }
+
     public void logStateChangeAction(int action, int dir, int srcChildTargetType,
                                      int srcParentContainerType, int dstContainerType,
                                      int pageIndex) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
new file mode 100644
index 0000000..40dd74b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util;
+
+import static com.android.quickstep.util.RemoteAnimationProvider.prepareTargetsForFirstFrame;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+/**
+ * Animation listener which fades out the closing targets
+ */
+public class RemoteFadeOutAnimationListener implements AnimatorUpdateListener {
+
+    private final RemoteAnimationTargetSet mTarget;
+    private boolean mFirstFrame = true;
+
+    public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] targets) {
+        mTarget = new RemoteAnimationTargetSet(targets, MODE_CLOSING);
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator valueAnimator) {
+        TransactionCompat t = new TransactionCompat();
+        if (mFirstFrame) {
+            prepareTargetsForFirstFrame(mTarget.unfilteredApps, t, MODE_CLOSING);
+            mFirstFrame = false;
+        }
+
+        float alpha = 1 - valueAnimator.getAnimatedFraction();
+        for (RemoteAnimationTargetCompat app : mTarget.apps) {
+            t.setAlpha(app.leash, alpha);
+        }
+        t.apply();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 25e3dc6..0025df1 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -16,13 +16,11 @@
 
 package com.android.quickstep.views;
 
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
-
 import android.content.Context;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
 
 public class ClearAllButton extends Button {
@@ -37,12 +35,9 @@
     }
 
     @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        final boolean res = super.performAccessibilityAction(action, arguments);
-        if (action == ACTION_ACCESSIBILITY_FOCUS) {
-            mRecentsView.revealClearAllButton();
-        }
-        return res;
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setParent(mRecentsView); // Pretend we are a part of the task carousel.
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index dee15d0..d550edc 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -39,6 +39,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.support.annotation.Nullable;
@@ -386,7 +387,13 @@
     private void updateClearAllButtonAlpha() {
         if (mClearAllButton != null) {
             final float alpha = calculateClearAllButtonAlpha();
-            mIsClearAllButtonFullyRevealed = alpha == 1;
+            final boolean revealed = alpha == 1;
+            if (mIsClearAllButtonFullyRevealed != revealed) {
+                mIsClearAllButtonFullyRevealed = revealed;
+                mClearAllButton.setImportantForAccessibility(revealed ?
+                        IMPORTANT_FOR_ACCESSIBILITY_YES :
+                        IMPORTANT_FOR_ACCESSIBILITY_NO);
+            }
             mClearAllButton.setAlpha(alpha * mContentAlpha);
         }
     }
@@ -507,6 +514,7 @@
         DeviceProfile dp = mActivity.getDeviceProfile();
         getTaskSize(dp, mTempRect);
 
+        // Keep this logic in sync with ActivityControlHelper.getTranslationYForQuickScrub.
         mTempRect.top -= mTaskTopMargin;
         setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
                 dp.availableWidthPx + mInsets.left - mTempRect.right,
@@ -1285,7 +1293,30 @@
     }
 
     @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (getChildCount() > 0) {
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                    if (!mIsClearAllButtonFullyRevealed && getCurrentPage() == getPageCount() - 1) {
+                        revealClearAllButton();
+                        return true;
+                    }
+                }
+                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                    if (mIsClearAllButtonFullyRevealed) {
+                        setCurrentPage(getChildCount() - 1);
+                        return true;
+                    }
+                }
+                break;
+            }
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
     public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+        outChildren.add(mClearAllButton);
         for (int i = getChildCount() - 1; i >= 0; --i) {
             outChildren.add(getChildAt(i));
         }
@@ -1295,6 +1326,13 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
 
+        if (getChildCount() > 0) {
+            info.addAction(mIsClearAllButtonFullyRevealed ?
+                    AccessibilityNodeInfo.ACTION_SCROLL_FORWARD :
+                    AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            info.setScrollable(true);
+        }
+
         final AccessibilityNodeInfo.CollectionInfo
                 collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(
                 1, getChildCount(), false,
@@ -1306,11 +1344,15 @@
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
 
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
-            final int visiblePageNumber = getChildCount() - getCurrentPage() - 1;
-            event.setFromIndex(visiblePageNumber);
-            event.setToIndex(visiblePageNumber);
-            event.setItemCount(getChildCount());
+        event.setScrollable(getPageCount() > 0);
+
+        if (!mIsClearAllButtonFullyRevealed
+                && event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            final int childCount = getChildCount();
+            final int[] visibleTasks = getVisibleChildrenRange();
+            event.setFromIndex(childCount - visibleTasks[1] - 1);
+            event.setToIndex(childCount - visibleTasks[0] - 1);
+            event.setItemCount(childCount);
         }
     }
 
@@ -1325,9 +1367,6 @@
         return true;
     }
 
-    public void addTaskAccessibilityActionsExtra(AccessibilityNodeInfo info) {
-    }
-
     public boolean performTaskAccessibilityActionExtra(int action) {
         return false;
     }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index 31c8b64..c6cd527 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -124,4 +124,9 @@
         return mRecentsView.requestFocus(direction, previouslyFocusedRect) ||
                 super.requestFocus(direction, previouslyFocusedRect);
     }
+
+    @Override
+    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+        outChildren.add(mRecentsView);
+    }
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index aca8351..c780b62 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -28,9 +28,6 @@
 import android.graphics.Path;
 import android.graphics.Path.Direction;
 import android.graphics.Path.Op;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.util.AttributeSet;
 
 import com.android.launcher3.DeviceProfile;
@@ -48,11 +45,6 @@
  */
 public class ShelfScrimView extends ScrimView {
 
-    private static final int THRESHOLD_ALPHA_DARK = 102;
-    private static final int THRESHOLD_ALPHA_LIGHT = 46;
-    private static final int THRESHOLD_ALPHA_SUPER_LIGHT = 128;
-    private static final int CLEAR_ALL_TASKS = R.string.recents_clear_all;
-
     // In transposed layout, we simply draw a flat color.
     private boolean mDrawingFlatColor;
 
@@ -81,13 +73,7 @@
         mMaxScrimAlpha = OVERVIEW.getWorkspaceScrimAlpha(mLauncher);
 
         mEndAlpha = Color.alpha(mEndScrim);
-        if (Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)) {
-            mThresholdAlpha = THRESHOLD_ALPHA_DARK;
-        } else if (Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
-            mThresholdAlpha = THRESHOLD_ALPHA_SUPER_LIGHT;
-        } else {
-            mThresholdAlpha = THRESHOLD_ALPHA_LIGHT;
-        }
+        mThresholdAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
         mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
@@ -208,43 +194,4 @@
                 mRadius, mRadius, mPaint);
         return minTop - mDragHandleSize - top;
     }
-
-    @NonNull
-    @Override
-    protected AccessibilityHelper createAccessibilityHelper() {
-        return new ShelfScrimAccessibilityHelper();
-    }
-
-    protected class ShelfScrimAccessibilityHelper extends AccessibilityHelper {
-        @Override
-        protected void onPopulateNodeForVirtualView(int virtualViewId,
-                AccessibilityNodeInfoCompat node) {
-            super.onPopulateNodeForVirtualView(virtualViewId, node);
-
-            if (mLauncher.isInState(OVERVIEW)) {
-                final RecentsView overviewPanel = mLauncher.getOverviewPanel();
-                if (overviewPanel.getChildCount() != 0) {
-                    node.addAction(
-                            new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                                    CLEAR_ALL_TASKS,
-                                    getContext().getText(CLEAR_ALL_TASKS)));
-                }
-            }
-        }
-
-        @Override
-        protected boolean onPerformActionForVirtualView(
-                int virtualViewId, int action, Bundle arguments) {
-            if (super.onPerformActionForVirtualView(virtualViewId, action, arguments)) return true;
-
-            if (action == CLEAR_ALL_TASKS) {
-                if (mLauncher.isInState(OVERVIEW)) {
-                    mLauncher.<RecentsView>getOverviewPanel().dismissAllTasks();
-                }
-                return true;
-            }
-
-            return false;
-        }
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 128a19e..d9dfd18 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -29,6 +29,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Shader;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Property;
@@ -37,8 +38,10 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.systemui.shared.recents.model.Task;
@@ -50,6 +53,7 @@
 public class TaskThumbnailView extends View {
 
     private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
+    private static final LightingColorFilter[] sHighlightFilterCache = new LightingColorFilter[256];
 
     public static final Property<TaskThumbnailView, Float> DIM_ALPHA_MULTIPLIER =
             new FloatProperty<TaskThumbnailView>("dimAlphaMultiplier") {
@@ -68,6 +72,7 @@
 
     private final BaseActivity mActivity;
     private final TaskOverlay mOverlay;
+    private final boolean mIsDarkTextTheme;
     private final Paint mPaint = new Paint();
     private final Paint mBackgroundPaint = new Paint();
 
@@ -97,6 +102,7 @@
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
         mActivity = BaseActivity.fromContext(context);
+        mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
     }
 
     public void bind() {
@@ -198,7 +204,7 @@
     private void updateThumbnailPaintFilter() {
         int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
         if (mBitmapShader != null) {
-            LightingColorFilter filter = getLightingColorFilter(mul);
+            LightingColorFilter filter = getDimmingColorFilter(mul, mIsDarkTextTheme);
             mPaint.setColorFilter(filter);
             mBackgroundPaint.setColorFilter(filter);
         } else {
@@ -287,16 +293,25 @@
         updateThumbnailMatrix();
     }
 
-    private static LightingColorFilter getLightingColorFilter(int dimColor) {
-        if (dimColor < 0) {
-            dimColor = 0;
-        } else if (dimColor > 255) {
-            dimColor = 255;
+    private static LightingColorFilter getDimmingColorFilter(int intensity, boolean shouldLighten) {
+        intensity = Utilities.boundToRange(intensity, 0, 255);
+        if (intensity == 255) {
+            return null;
         }
-        if (sDimFilterCache[dimColor] == null) {
-            sDimFilterCache[dimColor] =
-                    new LightingColorFilter(Color.argb(255, dimColor, dimColor, dimColor), 0);
+        if (shouldLighten) {
+            if (sHighlightFilterCache[intensity] == null) {
+                int colorAdd = 255 - intensity;
+                sHighlightFilterCache[intensity] = new LightingColorFilter(
+                        Color.argb(255, intensity, intensity, intensity),
+                        Color.argb(255, colorAdd, colorAdd, colorAdd));
+            }
+            return sHighlightFilterCache[intensity];
+        } else {
+            if (sDimFilterCache[intensity] == null) {
+                sDimFilterCache[intensity] = new LightingColorFilter(
+                        Color.argb(255, intensity, intensity, intensity), 0);
+            }
+            return sDimFilterCache[intensity];
         }
-        return sDimFilterCache[dimColor];
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5413a13..b5f31b8 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -250,12 +250,12 @@
         setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
     }
 
-    public float getCurveScaleForInterpolation(float linearInterpolation) {
+    public static float getCurveScaleForInterpolation(float linearInterpolation) {
         float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
         return getCurveScaleForCurveInterpolation(curveInterpolation);
     }
 
-    private float getCurveScaleForCurveInterpolation(float curveInterpolation) {
+    private static float getCurveScaleForCurveInterpolation(float curveInterpolation) {
         return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
     }
 
@@ -321,8 +321,6 @@
         }
 
         final RecentsView recentsView = getRecentsView();
-        recentsView.addTaskAccessibilityActionsExtra(info);
-
         final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
                 AccessibilityNodeInfo.CollectionItemInfo.obtain(
                         0, 1, recentsView.getChildCount() - recentsView.indexOfChild(this) - 1, 1,
diff --git a/res/layout/longpress_options_menu.xml b/res/layout/longpress_options_menu.xml
index 168dbc3..20bb5b8 100644
--- a/res/layout/longpress_options_menu.xml
+++ b/res/layout/longpress_options_menu.xml
@@ -22,4 +22,5 @@
     android:clipToPadding="false"
     android:clipChildren="false"
     android:elevation="@dimen/deep_shortcuts_elevation"
+    android:importantForAccessibility="yes"
     android:orientation="vertical" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 64ca05e..30091a5 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -20,6 +20,7 @@
 
     <!-- Attributes used for launcher theme -->
     <attr name="allAppsScrimColor" format="color" />
+    <attr name="allAppsInterimScrimAlpha" format="integer" />
     <attr name="allAppsNavBarScrimColor" format="color" />
     <attr name="popupColorPrimary" format="color" />
     <attr name="popupColorSecondary" format="color" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2b760f3..31cbaa1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -29,6 +29,7 @@
 
     <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
         <item name="allAppsScrimColor">#EAFFFFFF</item>
+        <item name="allAppsInterimScrimAlpha">46</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
         <item name="popupColorPrimary">#FFF</item>
         <item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
@@ -47,6 +48,7 @@
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
         <item name="workspaceTextColor">#FF212121</item>
+        <item name="allAppsInterimScrimAlpha">128</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
@@ -62,6 +64,7 @@
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
         <item name="android:colorPrimary">#FF212121</item>
         <item name="allAppsScrimColor">#EA212121</item>
+        <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="popupColorPrimary">?android:attr/colorPrimary</item>
         <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
@@ -71,6 +74,7 @@
     </style>
 
     <style name="LauncherThemeDark.DarKText" parent="@style/LauncherThemeDark">
+        <item name="allAppsInterimScrimAlpha">25</item>
         <item name="workspaceTextColor">#FF212121</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index b5eef8b..05c515b 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import static android.view.View.VISIBLE;
-
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
@@ -222,7 +221,8 @@
             }
         }
 
-        // Cancel the current animation
+        // Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
+        LauncherState fromState = mState;
         mConfig.reset();
 
         if (!animated) {
@@ -245,10 +245,10 @@
 
         // Since state NORMAL can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
-        mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration;
+        mConfig.duration = state == NORMAL ? fromState.transitionDuration : state.transitionDuration;
 
         AnimatorSetBuilder builder = new AnimatorSetBuilder();
-        prepareForAtomicAnimation(mState, state, builder);
+        prepareForAtomicAnimation(fromState, state, builder);
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(
                 state, builder, onCompleteRunnable);
         Runnable runnable = new StartAnimRunnable(animation);
@@ -348,6 +348,12 @@
             }
 
             @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                mState = mCurrentStableState;
+            }
+
+            @Override
             public void onAnimationSuccess(Animator animator) {
                 // Run any queued runnables
                 if (onCompleteRunnable != null) {
@@ -432,7 +438,6 @@
     }
 
     public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
-        clearCurrentAnimation();
         setCurrentAnimation(controller.getTarget());
         mConfig.userControlled = true;
         mConfig.playbackController = controller;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 5cc2e8f..de9cd98 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -152,6 +152,8 @@
     // Similar to the platform implementation of isLayoutValid();
     protected boolean mIsLayoutValid;
 
+    private int[] mTmpIntPair = new int[2];
+
     public PagedView(Context context) {
         this(context, null);
     }
@@ -1448,6 +1450,10 @@
 
     protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
             TimeInterpolator interpolator) {
+        if (mFirstLayout) {
+            setCurrentPage(whichPage);
+            return false;
+        }
 
         if (FeatureFlags.IS_DOGFOOD_BUILD) {
             duration *= Settings.System.getFloat(getContext().getContentResolver(),
@@ -1600,4 +1606,33 @@
 
         boolean shouldIncludeView(View view);
     }
+
+    public int[] getVisibleChildrenRange() {
+        float visibleLeft = 0;
+        float visibleRight = visibleLeft + getMeasuredWidth();
+        float scaleX = getScaleX();
+        if (scaleX < 1 && scaleX > 0) {
+            float mid = getMeasuredWidth() / 2;
+            visibleLeft = mid - ((mid - visibleLeft) / scaleX);
+            visibleRight = mid + ((visibleRight - mid) / scaleX);
+        }
+
+        int leftChild = -1;
+        int rightChild = -1;
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getPageAt(i);
+
+            float left = child.getLeft() + child.getTranslationX() - getScrollX();
+            if (left <= visibleRight && (left + child.getMeasuredWidth()) >= visibleLeft) {
+                if (leftChild == -1) {
+                    leftChild = i;
+                }
+                rightChild = i;
+            }
+        }
+        mTmpIntPair[0] = leftChild;
+        mTmpIntPair[1] = rightChild;
+        return mTmpIntPair;
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 06eb82e..66fb3c6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1381,28 +1381,9 @@
         if (mChildrenLayersEnabled) {
             final int screenCount = getChildCount();
 
-            float visibleLeft = 0;
-            float visibleRight = visibleLeft + getMeasuredWidth();
-            float scaleX = getScaleX();
-            if (scaleX < 1 && scaleX > 0) {
-                float mid = getMeasuredWidth() / 2;
-                visibleLeft = mid - ((mid - visibleLeft) / scaleX);
-                visibleRight = mid + ((visibleRight - mid) / scaleX);
-            }
-
-            int leftScreen = -1;
-            int rightScreen = -1;
-            for (int i = 0; i < screenCount; i++) {
-                final View child = getPageAt(i);
-
-                float left = child.getLeft() + child.getTranslationX() - getScrollX();
-                if (left <= visibleRight && (left + child.getMeasuredWidth()) >= visibleLeft) {
-                    if (leftScreen == -1) {
-                        leftScreen = i;
-                    }
-                    rightScreen = i;
-                }
-            }
+            final int[] visibleScreens = getVisibleChildrenRange();
+            int leftScreen = visibleScreens[0];
+            int rightScreen = visibleScreens[1];
             if (mForceDrawAdjacentPages) {
                 // In overview mode, make sure that the two side pages are visible.
                 leftScreen = Utilities.boundToRange(getCurrentPage() - 1, 0, rightScreen);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 68ad6e3..72ba418 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -466,9 +466,12 @@
      */
     public void addSpringFromFlingUpdateListener(ValueAnimator animator, float velocity) {
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            boolean shouldSpring = true;
+
             @Override
             public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                if (valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
+                if (shouldSpring
+                        && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
                     int searchViewId = getSearchView().getId();
                     addSpringView(searchViewId);
 
@@ -481,7 +484,7 @@
                                 }
                             });
 
-                    animator.removeUpdateListener(this);
+                    shouldSpring = false;
                 }
             }
         });
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index df4a7c1..1e84b41 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -159,7 +159,7 @@
         postCleanup();
     }
 
-    private void postCleanup() {
+    protected void postCleanup() {
         clearReference();
         if (mLauncher != null) {
             // Remove any drag params from the launcher intent since the drag operation is complete.
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 924bb4c..07eb0d6 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -22,14 +22,17 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.view.DragEvent;
 import android.view.View;
 import android.widget.RemoteViews;
 
 import com.android.launcher3.DragSource;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -44,11 +47,13 @@
 public class PinItemDragListener extends BaseItemDragListener {
 
     private final PinItemRequest mRequest;
+    private final CancellationSignal mCancelSignal;
 
     public PinItemDragListener(PinItemRequest request, Rect previewRect,
             int previewBitmapWidth, int previewViewWidth) {
         super(previewRect, previewBitmapWidth, previewViewWidth);
         mRequest = request;
+        mCancelSignal = new CancellationSignal();
     }
 
     @Override
@@ -60,6 +65,15 @@
     }
 
     @Override
+    public boolean init(Launcher launcher, boolean alreadyOnHome) {
+        super.init(launcher, alreadyOnHome);
+        if (!alreadyOnHome) {
+            UiFactory.useFadeOutAnimationForLauncherStart(launcher, mCancelSignal);
+        }
+        return false;
+    }
+
+    @Override
     protected PendingItemDragHelper createDragHelper() {
         final PendingAddItemInfo item;
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
@@ -95,6 +109,12 @@
         targetParent.containerType = LauncherLogProto.ContainerType.PINITEM;
     }
 
+    @Override
+    protected void postCleanup() {
+        super.postCleanup();
+        mCancelSignal.cancel();
+    }
+
     public static RemoteViews getPreview(PinItemRequest request) {
         Bundle extras = request.getExtras();
         if (extras != null &&
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index f020d2d..89ba72a 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -330,9 +330,9 @@
 
         mOldBounds.set(icon.getBounds());
         if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
-            int offset = Math.max((int)(BLUR_FACTOR * textureWidth), Math.min(left, top));
+            int offset = Math.max((int) Math.ceil(BLUR_FACTOR * textureWidth), Math.max(left, top));
             int size = Math.max(width, height);
-            icon.setBounds(offset, offset, size, size);
+            icon.setBounds(offset, offset, offset + size, offset + size);
         } else {
             icon.setBounds(left, top, left+width, top+height);
         }
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
index bd5c06e..fde220c 100644
--- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.view.View;
-import android.view.View.OnFocusChangeListener;
 
 import com.android.launcher3.PagedView;
 
@@ -52,8 +51,8 @@
 
     private void computeLocationRelativeToContainer(View child, Rect outRect) {
         View parent = (View) child.getParent();
-        outRect.left += child.getLeft();
-        outRect.top += child.getTop();
+        outRect.left += child.getX();
+        outRect.top += child.getY();
 
         if (parent != mContainer) {
             if (parent instanceof PagedView) {
@@ -64,22 +63,4 @@
             computeLocationRelativeToContainer(parent, outRect);
         }
     }
-
-    /**
-     * Sets the alpha of this FocusIndicatorHelper to 0 when a view with this listener
-     * receives focus.
-     */
-    public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
-        return new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (hasFocus) {
-                    endCurrentAnimation();
-                    setCurrentView(null);
-                    setAlpha(0);
-                    invalidateDirty();
-                }
-            }
-        };
-    }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index f276fbf..172cf41 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -527,6 +527,7 @@
     protected void closeComplete() {
         super.closeComplete();
         mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
+        mOriginalIcon.forceHideBadge(false);
     }
 
     @Override
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b83d3c0..b1dd003 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -142,6 +142,8 @@
                 // First row of first screen is not empty
                 createEmptyRowOnFirstScreen = c.moveToNext();
             }
+        } else {
+            createEmptyRowOnFirstScreen = false;
         }
 
         ArrayList<ContentProviderOperation> insertOperations = new ArrayList<>(BATCH_INSERT_SIZE);
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 65acaa9..b26d39f 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -36,7 +36,6 @@
 
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -68,6 +67,10 @@
         super.setPadding(0, 0, 0, 0);
     }
 
+    protected void setPaddingUnchecked(int left, int top, int right, int bottom) {
+        super.setPadding(left, top, right, bottom);
+    }
+
     /**
      * A fragment to display the QSB.
      */
@@ -100,7 +103,7 @@
             mWrapper = new FrameLayout(getActivity());
 
             // Only add the view when enabled
-            if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            if (isQsbEnabled()) {
                 mWrapper.addView(createQsb(mWrapper));
             }
             return mWrapper;
@@ -212,7 +215,7 @@
 
         private void rebindFragment() {
             // Exit if the embedded qsb is disabled
-            if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            if (!isQsbEnabled()) {
                 return;
             }
 
@@ -221,6 +224,10 @@
                 mWrapper.addView(createQsb(mWrapper));
             }
         }
+
+        public boolean isQsbEnabled() {
+            return FeatureFlags.QSB_ON_FIRST_SCREEN;
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index d225093..5f965a3 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -52,6 +52,13 @@
         return value;
     }
 
+    public static int getAttrInteger(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        int value = ta.getInteger(0, 0);
+        ta.recycle();
+        return value;
+    }
+
     /**
      * Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
      */
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index db98f9a..5a7e50f 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.os.CancellationSignal;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherStateManager.StateHandler;
@@ -51,6 +52,9 @@
 
     public static void onTrimMemory(Launcher launcher, int level) { }
 
+    public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
+            CancellationSignal cancellationSignal) { }
+
     public static boolean dumpActivity(Activity activity, PrintWriter writer) {
         return false;
     }