Merge "Updating the system shared lib" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index e4a8f36..dffe641 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -106,6 +106,8 @@
     private DeviceProfile mDeviceProfile;
     private View mFloatingView;
 
+    private RemoteAnimationRunnerCompat mRemoteAnimationOverride;
+
     private final AnimatorListenerAdapter mReapplyStateListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -175,6 +177,10 @@
         return getDefaultActivityLaunchOptions(launcher, v);
     }
 
+    public void setRemoteAnimationOverride(RemoteAnimationRunnerCompat remoteAnimationOverride) {
+        mRemoteAnimationOverride = remoteAnimationOverride;
+    }
+
     /**
      * Try to find a TaskView that corresponds with the component of the launched view.
      *
@@ -635,6 +641,7 @@
      * Registers remote animations used when closing apps to home screen.
      */
     private void registerRemoteAnimations() {
+        // Unregister this
         if (hasControlRemoteAppTransitionPermission()) {
             try {
                 RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
@@ -669,20 +676,53 @@
     private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
         return new LauncherAnimationRunner(mHandler) {
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
-                if (mLauncher.getStateManager().getState().overviewUi) {
-                    // We use a separate transition for Overview mode.
-                    return null;
+            public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
+                    Runnable runnable) {
+                if (mLauncher.getStateManager().getState().overviewUi
+                        && mRemoteAnimationOverride != null) {
+                    // This transition is only used for the fallback activity and should not be
+                    // managed here (but necessary to implement here since the defined remote
+                    // animation currently takes precendence over the one defined in the activity
+                    // options).
+                    mRemoteAnimationOverride.onAnimationStart(targetCompats, runnable);
+                    return;
                 }
+                super.onAnimationStart(targetCompats, runnable);
+            }
 
+            @Override
+            public void onAnimationCancelled() {
+                if (mLauncher.getStateManager().getState().overviewUi
+                        && mRemoteAnimationOverride != null) {
+                    // This transition is only used for the fallback activity and should not be
+                    // managed here (but necessary to implement here since the defined remote
+                    // animation currently takes precendence over the one defined in the activity
+                    // options).
+                    mRemoteAnimationOverride.onAnimationCancelled();
+                    return;
+                }
+                super.onAnimationCancelled();
+            }
+
+            @Override
+            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
                 AnimatorSet anim = new AnimatorSet();
                 anim.play(getClosingWindowAnimators(targetCompats));
 
-                if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)) {
+                // Normally, we run the launcher content animation when we are transitioning home,
+                // but if home is already visible, then we don't want to animate the contents of
+                // launcher unless we know that we are animating home as a result of the home button
+                // press with quickstep, which will result in launcher being started on touch down,
+                // prior to the animation home (and won't be in the targets list because it is
+                // already visible). In that case, we force invisibility on touch down, and only
+                // reset it after the animation to home is initialized.
+                if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
+                        || mLauncher.isForceInvisible()) {
                     // Only register the content animation for cancellation when state changes
                     mLauncher.getStateManager().setCurrentAnimation(anim);
                     createLauncherResumeAnimation(anim);
                 }
+                mLauncher.setForceInvisible(false);
                 return anim;
             }
         };
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 2626e7c..0e3d2a4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -88,10 +88,10 @@
     }
 
     @Override
-    public float[] getOverviewTranslationFactor(Launcher launcher) {
-        // Keep the same translation as in overview, so that we don't slide around when
+    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+        // Keep the same transition properties as overview, so that we don't move around when
         // transitioning to All Apps.
-        return LauncherState.OVERVIEW.getOverviewTranslationFactor(launcher);
+        return LauncherState.OVERVIEW.getOverviewScaleAndTranslationYFactor(launcher);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index 99bf264..f98f7a5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public float[] getOverviewTranslationFactor(Launcher launcher) {
-        return new float[] {0f, 0.5f};
+    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+        return new float[] {1f, 0.5f};
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 4b2763b..d97b7b2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -58,8 +58,8 @@
     }
 
     @Override
-    public float[] getOverviewTranslationFactor(Launcher launcher) {
-        return new float[] {0f, 0f};
+    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+        return new float[] {1f, 0f};
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 0c34b45..1b65ca0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -61,9 +61,6 @@
 
     private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
 
-    // If > 0, the animation progress is clamped at that value as long as user is dragging.
-    private float mClampProgressUpdate = -1;
-
     // If true, we will finish the current animation instantly on second touch.
     private boolean mFinishFastOnSecondTouch;
 
@@ -166,15 +163,6 @@
     }
 
     @Override
-    protected void updateProgress(float fraction) {
-        if (mClampProgressUpdate > 0) {
-            mCurrentAnimation.setPlayFraction(Math.min(fraction, mClampProgressUpdate));
-        } else {
-            super.updateProgress(fraction);
-        }
-    }
-
-    @Override
     protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
@@ -189,10 +177,8 @@
         if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) {
             builder = getNormalToOverviewAnimation();
             totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER;
-            mClampProgressUpdate = MAXIMUM_DISTANCE_FACTOR;
         } else {
             builder = new AnimatorSetBuilder();
-            mClampProgressUpdate = -1;
         }
 
         if (mPendingAnimation != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 9f7fef3..124ec20 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,11 +15,11 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_X_FACTOR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
+import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
@@ -30,7 +30,6 @@
 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.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.quickstep.views.LauncherRecentsView;
@@ -49,9 +48,9 @@
     @Override
     public void setState(LauncherState state) {
         mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
-        float[] translationFactor = state.getOverviewTranslationFactor(mLauncher);
-        mRecentsView.setTranslationXFactor(translationFactor[0]);
-        mRecentsView.setTranslationYFactor(translationFactor[1]);
+        float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
+        mRecentsView.setAdjacentScale(scaleTranslationYFactor[0]);
+        mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
         if (state.overviewUi) {
             mRecentsView.updateEmptyMessage();
             mRecentsView.resetTaskVisuals();
@@ -61,28 +60,18 @@
     @Override
     public void setStateWithAnimation(final LauncherState toState,
             AnimatorSetBuilder builder, AnimationConfig config) {
-
-        // Scroll to the workspace card before changing to the NORMAL state.
-        LauncherState fromState = mLauncher.getStateManager().getState();
-        int currPage = mRecentsView.getCurrentPage();
-        if (fromState.overviewUi && 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);
-            // Let the snapping animation play for a bit before we translate off screen.
-            builder.setStartDelay(snapDuration / 4);
-        }
-
         PropertySetter setter = config.getProperSetter(builder);
-        float[] translationFactor = toState.getOverviewTranslationFactor(mLauncher);
-        setter.setFloat(mRecentsView, TRANSLATION_X_FACTOR,
-                translationFactor[0],
+        float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
+        setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0],
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
-        setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR,
-                translationFactor[1],
+        setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
-        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0, LINEAR);
+        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
+                AGGRESSIVE_EASE_IN_OUT);
+
+        if (!toState.overviewUi) {
+            builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
+        }
 
         if (toState.overviewUi) {
             ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 4e3528c..3e96c44 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -36,8 +36,10 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppTransitionManagerImpl;
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -80,7 +82,10 @@
 
     ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
 
-    void startRecents(Context context, Intent intent, AssistDataReceiver assistDataReceiver,
+    void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
+            final RecentsAnimationListener remoteAnimationListener);
+
+    void startRecentsFromButton(Context context, Intent intent,
             RecentsAnimationListener remoteAnimationListener);
 
     @UiThread
@@ -203,21 +208,44 @@
         }
 
         @Override
-        public void startRecents(Context context, Intent intent,
-                AssistDataReceiver assistDataReceiver,
-                RecentsAnimationListener remoteAnimationListener) {
+        public void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
+                final RecentsAnimationListener remoteAnimationListener) {
             ActivityManagerWrapper.getInstance().startRecentsActivity(
                     intent, assistDataReceiver, remoteAnimationListener, null, null);
         }
 
+        @Override
+        public void startRecentsFromButton(Context context, Intent intent,
+                RecentsAnimationListener remoteAnimationListener) {
+            // We should use the remove animation for the fallback activity recents button case,
+            // it works better with PiP.  In Launcher, we have already registered the remote
+            // animation definition, which takes priority over explicitly defined remote
+            // animations in the provided activity options when starting the activity, so we
+            // just register a remote animation factory to get a callback to handle this.
+            LauncherAppTransitionManagerImpl appTransitionManager =
+                    (LauncherAppTransitionManagerImpl) getLauncher().getAppTransitionManager();
+            appTransitionManager.setRemoteAnimationOverride(new RecentsAnimationActivityOptions(
+                    remoteAnimationListener, () -> {
+                        // Once the controller is finished, also reset the remote animation override
+                        appTransitionManager.setRemoteAnimationOverride(null);
+                    }));
+            context.startActivity(intent);
+        }
+
+        @Nullable
+        @UiThread
+        private Launcher getLauncher() {
+            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+            if (app == null) {
+                return null;
+            }
+            return (Launcher) app.getModel().getCallback();
+        }
+
         @Nullable
         @UiThread
         private Launcher getVisibleLaucher() {
-            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-            if (app == null) {
-                return null;
-            }
-            Launcher launcher = (Launcher) app.getModel().getCallback();
+            Launcher launcher = getLauncher();
             return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
                     launcher : null;
         }
@@ -325,12 +353,23 @@
         }
 
         @Override
-        public void startRecents(Context context, Intent intent,
-                AssistDataReceiver assistDataReceiver,
+        public void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
                 final RecentsAnimationListener remoteAnimationListener) {
-            ActivityOptions options =
-                    ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                            new FallbackActivityOptions(remoteAnimationListener), 10000, 10000));
+            // We can use the normal recents animation for swipe up
+            ActivityManagerWrapper.getInstance().startRecentsActivity(
+                    intent, assistDataReceiver, remoteAnimationListener, null, null);
+        }
+
+        @Override
+        public void startRecentsFromButton(Context context, Intent intent,
+                RecentsAnimationListener remoteAnimationListener) {
+            // We should use the remove animation for the fallback activity recents button case,
+            // it works better with PiP. For the fallback activity, we should not have registered
+            // the launcher app transition manager, so we should just start the remote animation here.
+            ActivityOptions options = ActivityOptionsCompat.makeRemoteAnimation(
+                    new RemoteAnimationAdapterCompat(
+                            new RecentsAnimationActivityOptions(remoteAnimationListener, null),
+                            10000, 10000));
             context.startActivity(intent, options.toBundle());
         }
 
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 0ab2df7..4d695de 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -218,7 +218,7 @@
         handler.initWhenReady();
 
         TraceHelper.beginSection("RecentsController");
-        Runnable startActivity = () -> mActivityControlHelper.startRecents(this, mHomeIntent,
+        Runnable startActivity = () -> mActivityControlHelper.startRecentsFromSwipe(mHomeIntent,
                 new AssistDataReceiver() {
                     @Override
                     public void onHandleAssistData(Bundle bundle) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 985a364..8e59578 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -53,6 +53,7 @@
 import com.android.quickstep.util.SysuiEventLogger;
 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.recents.view.AppTransitionAnimationSpecCompat;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
@@ -77,7 +78,7 @@
     private static final int RID_CANCEL_CONTROLLER = 1;
     private static final int RID_CANCEL_ZOOM_OUT_ANIMATION = 2;
 
-    private static final long RECENTS_LAUNCH_DURATION = 150;
+    private static final long RECENTS_LAUNCH_DURATION = 200;
 
     private static final String TAG = "OverviewCommandHelper";
     private static final boolean DEBUG_START_FALLBACK_ACTIVITY = false;
@@ -151,12 +152,13 @@
     private void initSwipeHandler(ActivityControlHelper helper, long time,
             Consumer<WindowTransformSwipeHandler> onAnimationInitCallback) {
         final int commandId = mCurrentCommandId;
-        RunningTaskInfo taskInfo = ActivityManagerWrapper.getInstance().getRunningTask();
+        final RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask();
+        final int runningTaskId = runningTask.id;
         final WindowTransformSwipeHandler handler =
-                new WindowTransformSwipeHandler(taskInfo, mContext, time, helper);
+                new WindowTransformSwipeHandler(runningTask, mContext, time, helper);
 
         // Preload the plan
-        mRecentsModel.loadTasks(taskInfo.id, null);
+        mRecentsModel.loadTasks(runningTaskId, null);
         mWindowTransformSwipeHandler = handler;
 
         mTempTaskTargetRect.setEmpty();
@@ -172,13 +174,8 @@
         addFinishCommand(commandId, RID_RESET_SWIPE_HANDLER, handler::reset);
 
         TraceHelper.beginSection(TAG);
-        Runnable startActivity = () -> helper.startRecents(mContext, homeIntent,
-                new AssistDataReceiver() {
-                    @Override
-                    public void onHandleAssistData(Bundle bundle) {
-                        mRecentsModel.preloadAssistData(taskInfo.id, bundle);
-                    }
-                },
+        Runnable startActivity = () -> helper.startRecentsFromButton(mContext,
+                addToIntent(homeIntent),
                 new RecentsAnimationListener() {
                     public void onAnimationStart(
                             RecentsAnimationControllerCompat controller,
@@ -190,11 +187,17 @@
                                     minimizedHomeBounds);
                             mTempTaskTargetRect.set(handler.getTargetRect(mWindowSize));
 
+                            ThumbnailData thumbnail = mAM.getTaskThumbnail(runningTaskId,
+                                    true /* reducedResolution */);
                             mMainThreadExecutor.execute(() -> {
                                 addFinishCommand(commandId,
                                         RID_CANCEL_CONTROLLER, () -> controller.finish(true));
                                 if (commandId == mCurrentCommandId) {
                                     onAnimationInitCallback.accept(handler);
+
+                                    // The animation has started, which means the other activity
+                                    // should be paused, lets update the thumbnail
+                                    handler.switchToScreenshotImmediate(thumbnail);
                                 }
                             });
                         } else {
@@ -230,7 +233,7 @@
         });
         handler.onGestureStarted();
         anim.setDuration(RECENTS_LAUNCH_DURATION);
-        anim.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+        anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         anim.start();
         addFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION, anim::cancel);
     }
@@ -241,6 +244,7 @@
             return;
         }
 
+        ActivityManagerWrapper.getInstance().closeSystemWindows("recentapps");
         long time = SystemClock.elapsedRealtime();
         mMainThreadExecutor.execute(() -> {
             long elapsedTime = time - mLastToggleTime;
@@ -248,40 +252,37 @@
 
             mCurrentCommandId++;
             mTempTaskTargetRect.round(mTaskTargetRect);
-            boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
             int runnableCount = mCurrentCommandFinishRunnables.size();
             if (runnableCount > 0) {
                 for (int i = 0; i < runnableCount; i++) {
                     mCurrentCommandFinishRunnables.valueAt(i).run();
                 }
                 mCurrentCommandFinishRunnables.clear();
-                isQuickTap = true;
             }
 
+            // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
+            //       the menu activity which takes window focus, prevening the right condition from
+            //       being run below
             ActivityControlHelper helper = getActivityControlHelper();
             RecentsView recents = helper.getVisibleRecentsView();
             if (recents != null) {
-                int childCount = recents.getChildCount();
-                if (childCount != 0) {
-                    ((TaskView) recents.getChildAt(childCount >= 2 ? 1 : 0)).launchTask(true);
+                // Launch the next task
+                recents.showNextTask();
+            } else {
+                if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+                    // The user tried to launch back into overview too quickly, either after
+                    // launching an app, or before overview has actually shown, just ignore for now
+                    return;
                 }
 
-                // There are not enough tasks. Skip
-                return;
+                // Start overview
+                if (helper.switchToRecentsIfVisible()) {
+                    SysuiEventLogger.writeDummyRecentsTransition(0);
+                    // Do nothing
+                } else {
+                    initSwipeHandler(helper, time, this::startZoomOutAnim);
+                }
             }
-
-            if (isQuickTap) {
-                // Focus last task. Start is on background thread so that all ActivityManager calls
-                // are serialized
-                BackgroundExecutor.get().submit(this::startLastTask);
-                return;
-            }
-            if (helper.switchToRecentsIfVisible()) {
-                SysuiEventLogger.writeDummyRecentsTransition(0);
-                return;
-            }
-
-            initSwipeHandler(helper, time, this::startZoomOutAnim);
         });
     }
 
@@ -362,10 +363,6 @@
         return false;
     }
 
-    public boolean isUsingFallbackActivity() {
-        return DEBUG_START_FALLBACK_ACTIVITY;
-    }
-
     public ActivityControlHelper getActivityControlHelper() {
         if (DEBUG_START_FALLBACK_ACTIVITY) {
             return new FallbackActivityControllerHelper();
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 275fd29..fd089b5 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -138,9 +138,8 @@
     }
 
     public void snapToNextTaskIfAvailable() {
-        if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
-            int toPage = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
-            mRecentsView.snapToPage(toPage, QUICK_SCRUB_START_DURATION);
+        if (!mStartedFromHome && mInQuickScrub && mRecentsView.getChildCount() > 0) {
+            mRecentsView.snapToPage(mRecentsView.getNextPage() + 1, QUICK_SCRUB_START_DURATION);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityOptions.java b/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
similarity index 71%
rename from quickstep/src/com/android/quickstep/FallbackActivityOptions.java
rename to quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
index 04352c3..a25e192 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityOptions.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
@@ -24,24 +24,33 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.TransactionCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.util.function.Consumer;
 
 /**
- * Temporary class to create activity options to emulate recents transition for fallback activtiy.
+ * Class to create activity options to emulate recents transition.
  */
-public class FallbackActivityOptions implements RemoteAnimationRunnerCompat {
+public class RecentsAnimationActivityOptions implements RemoteAnimationRunnerCompat {
 
     private final RecentsAnimationListener mListener;
+    private final Runnable mFinishCallback;
 
-    public FallbackActivityOptions(RecentsAnimationListener listener) {
+    public RecentsAnimationActivityOptions(RecentsAnimationListener listener,
+            Runnable finishCallback) {
         mListener = listener;
+        mFinishCallback = finishCallback;
     }
 
     @Override
     public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
             Runnable runnable) {
         showOpeningTarget(targetCompats);
-        DummyRecentsAnimationControllerCompat dummyRecentsAnim =
-                new DummyRecentsAnimationControllerCompat(runnable);
+        RemoteRecentsAnimationControllerCompat dummyRecentsAnim =
+                new RemoteRecentsAnimationControllerCompat(() -> {
+                    runnable.run();
+                    if (mFinishCallback != null) {
+                        mFinishCallback.run();
+                    }
+                });
 
         Rect insets = new Rect();
         WindowManagerWrapper.getInstance().getStableInsets(insets);
@@ -54,23 +63,22 @@
     }
 
     private void showOpeningTarget(RemoteAnimationTargetCompat[] targetCompats) {
+        TransactionCompat t = new TransactionCompat();
         for (RemoteAnimationTargetCompat target : targetCompats) {
-            TransactionCompat t = new TransactionCompat();
             int layer = target.mode == RemoteAnimationTargetCompat.MODE_CLOSING
                     ? Integer.MAX_VALUE
                     : target.prefixOrderIndex;
             t.setLayer(target.leash, layer);
             t.show(target.leash);
-            t.apply();
         }
+        t.apply();
     }
 
-    private static class DummyRecentsAnimationControllerCompat
-            extends RecentsAnimationControllerCompat {
+    private class RemoteRecentsAnimationControllerCompat extends RecentsAnimationControllerCompat {
 
         final Runnable mFinishCallback;
 
-        public DummyRecentsAnimationControllerCompat(Runnable finishCallback) {
+        public RemoteRecentsAnimationControllerCompat(Runnable finishCallback) {
             mFinishCallback = finishCallback;
         }
 
@@ -87,7 +95,8 @@
 
         @Override
         public void finish(boolean toHome) {
-            if (toHome) {
+            // This should never be called with toHome == false
+            if (mFinishCallback != null) {
                 mFinishCallback.run();
             }
         }
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1e43202..4652f2d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -159,6 +159,16 @@
     }
 
     @Override
+    public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+        mTaskChangeId++;
+    }
+
+    @Override
+    public void onActivityUnpinned() {
+        mTaskChangeId++;
+    }
+
+    @Override
     public void onTaskStackChanged() {
         mTaskChangeId++;
 
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 36a9d56..f6cf85a 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -196,6 +196,10 @@
     private final long mTouchTimeMs;
     private long mLauncherFrameDrawnTime;
 
+    // Only used with the recents activity, when the screenshot should be fetched at the beginning
+    // of the animation and not at the end when the activity is already paused
+    private boolean mSkipScreenshotAtEndOfTransition;
+
     WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs,
             ActivityControlHelper<T> controller) {
         mContext = context;
@@ -336,6 +340,9 @@
         }
         mWasLauncherAlreadyVisible = alreadyOnHome;
         mActivity = activity;
+        // Override the visibility of the activity until the gesture actually starts and we swipe
+        // up, or until we transition home and the home animation is composed
+        mActivity.setForceInvisible(true);
 
         mRecentsView = activity.getOverviewPanel();
         mQuickScrubController = mRecentsView.getQuickScrubController();
@@ -582,7 +589,6 @@
                     mSourceStackBounds.set(target.sourceContainerBounds);
 
                     initTransitionEndpoints(dp);
-                    break;
                 }
             }
         }
@@ -610,6 +616,9 @@
     private void notifyGestureStarted() {
         final T curActivity = mActivity;
         if (curActivity != null) {
+            // Once the gesture starts, we can no longer transition home through the button, so
+            // reset the force override of the activity visibility
+            mActivity.setForceInvisible(false);
             mActivityControlHelper.onQuickstepGestureStarted(
                     curActivity, mWasLauncherAlreadyVisible);
         }
@@ -725,8 +734,9 @@
                         () -> setStateOnUiThread(STATE_SWITCH_TO_SCREENSHOT_COMPLETE));
             }
         };
+
         synchronized (mRecentsAnimationWrapper) {
-            if (mRecentsAnimationWrapper.controller != null) {
+            if (mRecentsAnimationWrapper.controller != null && !mSkipScreenshotAtEndOfTransition) {
                 TransactionCompat transaction = new TransactionCompat();
                 for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
                     if (app.mode == MODE_CLOSING) {
@@ -753,6 +763,12 @@
         doLogGesture(true /* toLauncher */);
     }
 
+    @UiThread
+    public void switchToScreenshotImmediate(ThumbnailData thumbnail) {
+        mRecentsView.updateThumbnail(mRunningTaskId, thumbnail);
+        mSkipScreenshotAtEndOfTransition = true;
+    }
+
     private void setupLauncherUiAfterSwipeUpAnimation() {
         if (mLauncherTransitionController != null) {
             mLauncherTransitionController.getAnimationPlayer().end();
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index d428f23..6788827 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 
-import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
@@ -45,20 +44,6 @@
 @TargetApi(Build.VERSION_CODES.O)
 public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable {
 
-    public static final FloatProperty<LauncherRecentsView> TRANSLATION_X_FACTOR =
-            new FloatProperty<LauncherRecentsView>("translationXFactor") {
-
-                @Override
-                public void setValue(LauncherRecentsView view, float v) {
-                    view.setTranslationXFactor(v);
-                }
-
-                @Override
-                public Float get(LauncherRecentsView view) {
-                    return view.mTranslationXFactor;
-                }
-            };
-
     public static final FloatProperty<LauncherRecentsView> TRANSLATION_Y_FACTOR =
             new FloatProperty<LauncherRecentsView>("translationYFactor") {
 
@@ -74,8 +59,6 @@
             };
 
     @ViewDebug.ExportedProperty(category = "launcher")
-    private float mTranslationXFactor;
-    @ViewDebug.ExportedProperty(category = "launcher")
     private float mTranslationYFactor;
 
     private Rect mPagePadding = new Rect();
@@ -114,11 +97,6 @@
         setTranslationYFactor(mTranslationYFactor);
     }
 
-    public void setTranslationXFactor(float translationFactor) {
-        mTranslationXFactor = translationFactor;
-        invalidate();
-    }
-
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
         setTranslationY(mTranslationYFactor * (mPagePadding.bottom - mPagePadding.top));
@@ -127,10 +105,7 @@
     @Override
     public void draw(Canvas canvas) {
         maybeDrawEmptyMessage(canvas);
-        int count = canvas.save();
-        canvas.translate(mTranslationXFactor * (mIsRtl ? -getWidth() : getWidth()), 0);
         super.draw(canvas);
-        canvas.restoreToCount(count);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index e7f69b7..d95619c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -59,8 +59,8 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.Themes;
 import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.RecentsAnimationInterpolator;
 import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
@@ -85,8 +85,6 @@
 
     public static final FloatProperty<RecentsView> CONTENT_ALPHA =
             new FloatProperty<RecentsView>("contentAlpha") {
-
-
         @Override
         public void setValue(RecentsView recentsView, float v) {
             recentsView.setContentAlpha(v);
@@ -98,6 +96,20 @@
         }
     };
 
+
+
+    public static final FloatProperty<RecentsView> ADJACENT_SCALE =
+            new FloatProperty<RecentsView>("adjacentScale") {
+        @Override
+        public void setValue(RecentsView recentsView, float v) {
+            recentsView.setAdjacentScale(v);
+        }
+
+        @Override
+        public Float get(RecentsView recentsView) {
+            return recentsView.mAdjacentScale;
+        }
+    };
     private static final String PREF_FLIP_RECENTS = "pref_flip_recents";
     private static final int DISMISS_TASK_DURATION = 300;
 
@@ -126,6 +138,26 @@
                 }
             }
         }
+
+        @Override
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+            // Check this is for the right user
+            if (!checkCurrentUserId(userId, false /* debug */)) {
+                return;
+            }
+
+            // Remove the task immediately from the task list
+            TaskView taskView = getTaskView(taskId);
+            if (taskView != null) {
+                removeView(taskView);
+            }
+        }
+
+        @Override
+        public void onActivityUnpinned() {
+            // TODO: Re-enable layout transitions for addition of the unpinned task
+            reloadIfNeeded();
+        }
     };
 
     private int mLoadPlanId = -1;
@@ -145,6 +177,8 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private float mContentAlpha = 1;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private float mAdjacentScale = 1;
 
     // Keeps track of task views whose visual state should not be reset
     private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
@@ -581,6 +615,23 @@
         }
     }
 
+    public void showNextTask() {
+        TaskView runningTaskView = getTaskView(mRunningTaskId);
+        if (runningTaskView == null) {
+            // Launch the first task
+            if (getChildCount() > 0) {
+                ((TaskView) getChildAt(0)).launchTask(true /* animate */);
+            }
+        } else {
+            // Get the next launch task
+            int runningTaskIndex = indexOfChild(runningTaskView);
+            int nextTaskIndex = Math.max(0, Math.min(getChildCount() - 1, runningTaskIndex + 1));
+            if (nextTaskIndex < getChildCount()) {
+                ((TaskView) getChildAt(nextTaskIndex)).launchTask(true /* animate */);
+            }
+        }
+    }
+
     public QuickScrubController getQuickScrubController() {
         return mQuickScrubController;
     }
@@ -803,10 +854,58 @@
         setVisibility(alpha > 0 ? VISIBLE : GONE);
     }
 
+    public void setAdjacentScale(float adjacentScale) {
+        if (mAdjacentScale == adjacentScale) {
+            return;
+        }
+        mAdjacentScale = adjacentScale;
+        TaskView currTask = getPageAt(mCurrentPage);
+        if (currTask == null) {
+            return;
+        }
+        currTask.setScaleX(mAdjacentScale);
+        currTask.setScaleY(mAdjacentScale);
+
+        if (mCurrentPage - 1 >= 0) {
+            TaskView adjacentTask = getPageAt(mCurrentPage - 1);
+            float[] scaleAndTranslation = getAdjacentScaleAndTranslation(currTask, adjacentTask,
+                    mAdjacentScale, 0);
+            adjacentTask.setScaleX(scaleAndTranslation[0]);
+            adjacentTask.setScaleY(scaleAndTranslation[0]);
+            adjacentTask.setTranslationX(-scaleAndTranslation[1]);
+            adjacentTask.setTranslationY(scaleAndTranslation[2]);
+        }
+        if (mCurrentPage + 1 < getChildCount()) {
+            TaskView adjacentTask = getPageAt(mCurrentPage + 1);
+            float[] scaleAndTranslation = getAdjacentScaleAndTranslation(currTask, adjacentTask,
+                    mAdjacentScale, 0);
+            adjacentTask.setScaleX(scaleAndTranslation[0]);
+            adjacentTask.setScaleY(scaleAndTranslation[0]);
+            adjacentTask.setTranslationX(scaleAndTranslation[1]);
+            adjacentTask.setTranslationY(scaleAndTranslation[2]);
+        }
+    }
+
+    private float[] getAdjacentScaleAndTranslation(TaskView currTask, TaskView adjacentTask,
+            float currTaskToScale, float currTaskToTranslationY) {
+        float displacement = currTask.getWidth() * (currTaskToScale - currTask.getCurveScale());
+        return new float[] {
+                currTaskToScale * adjacentTask.getCurveScale(),
+                mIsRtl ? -displacement : displacement,
+                currTaskToTranslationY
+        };
+    }
+
     @Override
     public void onViewAdded(View child) {
         super.onViewAdded(child);
         child.setAlpha(mContentAlpha);
+        setAdjacentScale(mAdjacentScale);
+    }
+
+    @Override
+    public TaskView getPageAt(int index) {
+        return (TaskView) getChildAt(index);
     }
 
     public void updateEmptyMessage() {
@@ -884,18 +983,24 @@
         float toScale = endInterpolation.taskScale;
         float toTranslationY = endInterpolation.taskY;
 
-        float displacementX = tv.getWidth() * (toScale - tv.getScaleX());
         if (launchingCenterTask) {
+            TaskView centerTask = getPageAt(centerTaskIndex);
             if (taskIndex - 1 >= 0) {
-                anim.play(createAnimForChild(
-                        taskIndex - 1, toScale, displacementX, toTranslationY));
+                TaskView adjacentTask = getPageAt(taskIndex - 1);
+                float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
+                        adjacentTask, toScale, toTranslationY);
+                scaleAndTranslation[1] = -scaleAndTranslation[1];
+                anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
             }
             if (taskIndex + 1 < getPageCount()) {
-                anim.play(createAnimForChild(
-                        taskIndex + 1, toScale, -displacementX, toTranslationY));
+                TaskView adjacentTask = getPageAt(taskIndex + 1);
+                float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
+                        adjacentTask, toScale, toTranslationY);
+                anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
             }
         } else {
             // We are launching an adjacent task, so parallax the center and other adjacent task.
+            float displacementX = tv.getWidth() * (toScale - tv.getCurveScale());
             anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex), TRANSLATION_X,
                     mIsRtl ? -displacementX : displacementX));
 
@@ -911,13 +1016,12 @@
         return anim;
     }
 
-    private ObjectAnimator createAnimForChild(int childIndex, float toScale, float tx, float ty) {
-        View child = getChildAt(childIndex);
+    private ObjectAnimator createAnimForChild(View child, float[] toScaleAndTranslation) {
         return ObjectAnimator.ofPropertyValuesHolder(child,
                         new PropertyListBuilder()
-                                .scale(child.getScaleX() * toScale)
-                                .translationY(ty)
-                                .translationX(mIsRtl ? tx : -tx)
+                                .scale(child.getScaleX() * toScaleAndTranslation[0])
+                                .translationX(toScaleAndTranslation[1])
+                                .translationY(toScaleAndTranslation[2])
                                 .build());
     }
 
@@ -962,4 +1066,9 @@
         super.notifyPageSwitchListener(prevPage);
         getChildAt(mCurrentPage).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
     }
+
+    @Override
+    protected String getCurrentPageDescription() {
+        return "";
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 57516b0..42da472 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -70,6 +70,7 @@
     private Task mTask;
     private TaskThumbnailView mSnapshotView;
     private ImageView mIconView;
+    private float mCurveScale;
 
     public TaskView(Context context) {
         this(context, null);
@@ -178,9 +179,13 @@
 
         mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
 
-        float scale = 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
-        setScaleX(scale);
-        setScaleY(scale);
+        mCurveScale = 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
+        setScaleX(mCurveScale);
+        setScaleY(mCurveScale);
+    }
+
+    public float getCurveScale() {
+        return mCurveScale;
     }
 
     @Override
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index cf2d79f..ae631a4 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -39,6 +39,9 @@
     protected SystemUiController mSystemUiController;
 
     private boolean mStarted;
+    // When the recents animation is running, the visibility of the Launcher is managed by the
+    // animation
+    private boolean mForceInvisible;
     private boolean mUserActive;
 
     public DeviceProfile getDeviceProfile() {
@@ -100,6 +103,7 @@
     @Override
     protected void onStop() {
         mStarted = false;
+        mForceInvisible = false;
         super.onStop();
     }
 
@@ -126,6 +130,22 @@
     }
 
     /**
+     * Used to set the override visibility state, used only to handle the transition home with the
+     * recents animation.
+     * @see LauncherAppTransitionManagerImpl.getWallpaperOpenRunner()
+     */
+    public void setForceInvisible(boolean invisible) {
+        mForceInvisible = invisible;
+    }
+
+    /**
+     * @return Wether this activity should be considered invisible regardless of actual visibility.
+     */
+    public boolean isForceInvisible() {
+        return mForceInvisible;
+    }
+
+    /**
      * Sets the device profile, adjusting it accordingly in case of multi-window
      */
     protected void setDeviceProfile(DeviceProfile dp) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ccc774a..b410f4f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -357,11 +357,6 @@
         if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
             mUserEventDispatcher = null;
             initDeviceProfile(mDeviceProfile.inv);
-            FileLog.d(TAG, "Config changed, my orientation=" +
-                    getResources().getConfiguration().orientation +
-                    ", new orientation=" + newConfig.orientation +
-                    ", old orientation=" + mOldConfig.orientation +
-                    ", isTransposed=" + mDeviceProfile.isVerticalBarLayout());
             dispatchDeviceProfileChanged();
 
             getRootView().dispatchInsets();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8b7ba20..7d208d4 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -34,7 +34,6 @@
 import android.database.Cursor;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.database.sqlite.SQLiteStatement;
 import android.net.Uri;
@@ -56,7 +55,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.DbDowngradeHelper;
-import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.provider.RestoreDbTask;
@@ -320,11 +318,6 @@
 
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
-        if (ModelWriter.DEBUG_DELETE) {
-            String args = selectionArgs == null ? null : TextUtils.join(",", selectionArgs);
-            FileLog.d(TAG, "Delete uri=" + uri + ", selection=" + selection
-                    + ", selectionArgs=" + args, new Exception());
-        }
         createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
 
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index fdb6f48..4697b82 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -181,12 +181,12 @@
     }
 
     /**
-     * Returns 2 floats designating how much to translate overview:
-     *   X factor is based on width, e.g. 0 is fully onscreen and 1 is fully offscreen
-     *   Y factor is based on padding, e.g. 0 is top aligned and 0.5 is centered vertically
+     * Returns 2 floats designating how to transition overview:
+     *   scale for the current and adjacent pages
+     *   translationY factor where 0 is top aligned and 0.5 is centered vertically
      */
-    public float[] getOverviewTranslationFactor(Launcher launcher) {
-        return new float[] {1f, 0f};
+    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+        return new float[] {1.2f, 0.2f};
     }
 
     public void onStateEnabled(Launcher launcher) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index fa86906..420a7c4 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -31,7 +31,6 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.graphics.ViewScrim;
-import com.android.launcher3.uioverrides.UiFactory;
 
 /**
  * Manages the animations between each of the workspace states.
@@ -74,11 +73,11 @@
                     propertySetter);
         }
 
-        propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN);
+        propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_OUT);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
-                scaleAndTranslation[1], Interpolators.ZOOM_IN);
+                scaleAndTranslation[1], Interpolators.ZOOM_OUT);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
-                scaleAndTranslation[2], Interpolators.ZOOM_IN);
+                scaleAndTranslation[2], Interpolators.ZOOM_OUT);
 
         int elements = state.getVisibleElements(mLauncher);
         float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
@@ -113,7 +112,7 @@
         int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
 
         propertySetter.setInt(cl.getScrimBackground(),
-                DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_IN);
+                DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_OUT);
         propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
                 pageAlpha, pageAlphaProvider.interpolator);
     }
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index 9191048..b209a2d 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.anim;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.util.SparseArray;
 import android.view.animation.Interpolator;
@@ -23,6 +24,7 @@
 import com.android.launcher3.LauncherAnimUtils;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Utility class for building animator set
@@ -35,7 +37,7 @@
     protected final ArrayList<Animator> mAnims = new ArrayList<>();
 
     private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
-    private long mStartDelay = 0;
+    private List<Runnable> mOnFinishRunnables = new ArrayList<>();
 
     /**
      * Associates a tag with all the animations added after this call.
@@ -46,14 +48,24 @@
         mAnims.add(anim);
     }
 
-    public void setStartDelay(long startDelay) {
-        mStartDelay = startDelay;
+    public void addOnFinishRunnable(Runnable onFinishRunnable) {
+        mOnFinishRunnables.add(onFinishRunnable);
     }
 
     public AnimatorSet build() {
         AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
         anim.playTogether(mAnims);
-        anim.setStartDelay(mStartDelay);
+        if (!mOnFinishRunnables.isEmpty()) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    for (Runnable onFinishRunnable : mOnFinishRunnables) {
+                        onFinishRunnable.run();
+                    }
+                    mOnFinishRunnables.clear();
+                }
+            });
+        }
         return anim;
     }
 
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 6078776..06ddf22 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -65,15 +65,22 @@
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 
     /**
-     * Inversion of zInterpolate, compounded with an ease-out.
+     * Inversion of ZOOM_OUT, compounded with an ease-out.
      */
     public static final Interpolator ZOOM_IN = new Interpolator() {
+        @Override
+        public float getInterpolation(float v) {
+            return DEACCEL_3.getInterpolation(1 - ZOOM_OUT.getInterpolation(1 - v));
+        }
+    };
+
+    public static final Interpolator ZOOM_OUT = new Interpolator() {
 
         private static final float FOCAL_LENGTH = 0.35f;
 
         @Override
         public float getInterpolation(float v) {
-            return DEACCEL_3.getInterpolation(1 - zInterpolate(1 - v));
+            return zInterpolate(v);
         }
 
         /**
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 72c703b..eba7515 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -50,7 +50,6 @@
 public class ModelWriter {
 
     private static final String TAG = "ModelWriter";
-    public static final boolean DEBUG_DELETE = true;
 
     private final Context mContext;
     private final LauncherModel mModel;
@@ -257,14 +256,6 @@
      * Removes the specified items from the database
      */
     public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
-        if (DEBUG_DELETE) {
-            // Log it on the colling thread to get the proper stack trace
-            FileLog.d(TAG, "Starting item deletion", new Exception());
-            for (ItemInfo item : items) {
-                FileLog.d(TAG, "deleting item " + item);
-            }
-            FileLog.d(TAG, "Finished deleting items");
-        }
         ModelVerifier verifier = new ModelVerifier();
 
         mWorkerExecutor.execute(() -> {
@@ -282,11 +273,6 @@
      * Remove the specified folder and all its contents from the database.
      */
     public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
-        if (DEBUG_DELETE) {
-            // Log it on the colling thread to get the proper stack trace
-            FileLog.d(TAG, "Deleting folder " + info, new Exception());
-        }
-
         ModelVerifier verifier = new ModelVerifier();
 
         mWorkerExecutor.execute(() -> {
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index ac381cc..4aa2f37 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -24,7 +24,6 @@
 import android.util.Log;
 import android.util.MutableLong;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 
 /**
@@ -35,8 +34,7 @@
  */
 public class TraceHelper {
 
-    private static final boolean FORCE_LOG = Utilities.IS_DEBUG_DEVICE;
-    private static final boolean ENABLED = FORCE_LOG || FeatureFlags.IS_DOGFOOD_BUILD;
+    private static final boolean ENABLED = FeatureFlags.IS_DOGFOOD_BUILD;
 
     private static final boolean SYSTEM_TRACE = false;
     private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
@@ -45,7 +43,7 @@
         if (ENABLED) {
             MutableLong time = sUpTimes.get(sectionName);
             if (time == null) {
-                time = new MutableLong((FORCE_LOG || isLoggable(sectionName, VERBOSE)) ? 0 : -1);
+                time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
                 sUpTimes.put(sectionName, time);
             }
             if (time.value >= 0) {