Merge "Changing RecentsView layout based on the split screen task size" into ub-launcher3-rvc-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 357e9ec..fa0d3f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -54,11 +55,7 @@
 
     @Override
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        return new float[] {getOverviewScale(launcher), NO_OFFSET};
-    }
-
-    private float getOverviewScale(Launcher launcher) {
-        return ((RecentsView) launcher.getOverviewPanel()).getMaxScaleForFullScreen();
+        return getOverviewScaleAndOffsetForBackgroundState(launcher);
     }
 
     @Override
@@ -88,4 +85,11 @@
     protected float getDepthUnchecked(Context context) {
         return 1f;
     }
+
+    public static float[] getOverviewScaleAndOffsetForBackgroundState(
+            BaseDraggingActivity activity) {
+        return new float[] {
+                ((RecentsView) activity.getOverviewPanel()).getMaxScaleForFullScreen(),
+                NO_OFFSET};
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index a7e7d3a..0be2486 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -19,8 +19,8 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.views.RecentsView;
 
@@ -49,22 +49,25 @@
 
     @Override
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        Resources res = launcher.getBaseContext().getResources();
-
-        Rect out = new Rect();
-        launcher.<RecentsView>getOverviewPanel().getTaskSize(out);
-        int taskHeight = out.height();
-
-        float topMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float bottomMargin = res.getDimension(R.dimen.overview_actions_top_margin);
-        float newHeight = taskHeight + topMargin + bottomMargin;
-        float scale = newHeight / taskHeight;
-
-        return new float[] {scale, 0};
+        return getOverviewScaleAndOffsetForModalState(launcher);
     }
 
     @Override
     public float getOverviewModalness() {
         return 1.0f;
     }
+
+    public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
+        Resources res = activity.getResources();
+
+        Rect out = new Rect();
+        activity.<RecentsView>getOverviewPanel().getTaskSize(out);
+        int taskHeight = out.height();
+        activity.<RecentsView>getOverviewPanel().getModalTaskSize(out);
+        int newHeight = out.height();
+
+        float scale = (float) newHeight / taskHeight;
+
+        return new float[] {scale, NO_OFFSET};
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 324c390..cd546e2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -15,14 +15,15 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.fallback.FallbackRecentsView.ZOOM_PROGRESS;
+import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
+import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
 
@@ -30,6 +31,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.ActivityInitListener;
@@ -89,15 +91,13 @@
     public AnimationFactory prepareRecentsUI(
             boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
         RecentsActivity activity = getCreatedActivity();
-        if (activityVisible) {
+        if (activity == null) {
             return (transitionLength) -> { };
         }
 
+        activity.getStateManager().goToState(BACKGROUND_APP);
         FallbackRecentsView rv = activity.getOverviewPanel();
         rv.setContentAlpha(0);
-        rv.getClearAllButton().setVisibilityAlpha(0);
-        rv.setDisallowScrollToClearAll(true);
-        rv.setInOverviewState(false);
 
         return new AnimationFactory() {
 
@@ -115,23 +115,19 @@
 
             @Override
             public void createActivityInterface(long transitionLength) {
-                AnimatorSet animatorSet = new AnimatorSet();
+                PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+
                 if (isAnimatingToRecents) {
-                    ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
-                    anim.setDuration(transitionLength).setInterpolator(LINEAR);
-                    animatorSet.play(anim);
+                    pa.addFloat(rv, CONTENT_ALPHA, 0, 1, LINEAR);
                 }
 
-                ObjectAnimator anim = ObjectAnimator.ofFloat(rv, ZOOM_PROGRESS, 1, 0);
-                anim.setDuration(transitionLength).setInterpolator(LINEAR);
-                animatorSet.play(anim);
-
-                AnimatorPlaybackController controller =
-                        AnimatorPlaybackController.wrap(animatorSet, transitionLength);
+                pa.addFloat(rv, SCALE_PROPERTY, rv.getMaxScaleForFullScreen(), 1, LINEAR);
+                pa.addFloat(rv, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
+                AnimatorPlaybackController controller = pa.createPlaybackController();
 
                 // Since we are changing the start position of the UI, reapply the state, at the end
-                controller.setEndAction(() ->
-                        rv.setInOverviewState(controller.getInterpolatedProgress() > 0.5));
+                controller.setEndAction(() -> activity.getStateManager().goToState(
+                        controller.getInterpolatedProgress() > 0.5 ? DEFAULT : BACKGROUND_APP));
                 callback.accept(controller);
             }
         };
@@ -147,7 +143,7 @@
     @Nullable
     @Override
     public RecentsActivity getCreatedActivity() {
-        return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
+        return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
     }
 
     @Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 8ce6bbc..77e50ca 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -37,10 +37,12 @@
 import android.util.ArrayMap;
 import android.view.MotionEvent;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.ObjectWrapper;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.RectFSpringAnim;
@@ -103,6 +105,12 @@
     private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
     private RunningWindowAnim mFinishAnimation;
 
+    // Used to control Recents components throughout the swipe gesture.
+    private AnimatorPlaybackController mLauncherTransitionController;
+    private boolean mHasLauncherTransitionControllerStarted;
+
+    private AnimationFactory mAnimationFactory = (t) -> { };
+
     public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
             GestureState gestureState, InputConsumerController inputConsumer,
             boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
@@ -165,10 +173,6 @@
         mRecentsView = mActivity.getOverviewPanel();
         mRecentsView.setOnPageTransitionEndCallback(null);
         linkRecentsViewScroll();
-        mRecentsView.setDisallowScrollToClearAll(true);
-        mRecentsView.getClearAllButton().setVisibilityAlpha(0);
-        mRecentsView.setZoomProgress(1);
-
         if (!mContinuingLastGesture) {
             if (mRunningOverHome) {
                 mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
@@ -178,10 +182,49 @@
         }
         mStateCallback.setStateOnUiThread(STATE_RECENTS_PRESENT);
         mDeviceState.enableMultipleRegions(false);
+
+        mAnimationFactory = mActivityInterface.prepareRecentsUI(alreadyOnHome,
+                this::onAnimatorPlaybackControllerCreated);
+        mAnimationFactory.createActivityInterface(mTransitionDragLength);
         return true;
     }
 
     @Override
+    protected void initTransitionEndpoints(DeviceProfile dp) {
+        super.initTransitionEndpoints(dp);
+        if (canCreateNewOrUpdateExistingLauncherTransitionController()) {
+            mAnimationFactory.createActivityInterface(mTransitionDragLength);
+        }
+    }
+
+    private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
+        mLauncherTransitionController = anim;
+        mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
+        mLauncherTransitionController.dispatchOnStart();
+        updateLauncherTransitionProgress();
+    }
+
+    private void updateLauncherTransitionProgress() {
+        if (mLauncherTransitionController == null
+                || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
+            return;
+        }
+        // Normalize the progress to 0 to 1, as the animation controller will clamp it to that
+        // anyway. The controller mimics the drag length factor by applying it to its interpolators.
+        float progress = mCurrentShift.value / mDragLengthFactor;
+        mLauncherTransitionController.setPlayFraction(progress);
+    }
+
+    /**
+     * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
+     * (it has its own animation) or if we're already animating the current controller.
+     * @return Whether we can create the launcher controller or update its progress.
+     */
+    private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
+        return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
+    }
+
+    @Override
     protected boolean moveWindowWithRecentsScroll() {
         return mInQuickSwitchMode;
     }
@@ -260,6 +303,7 @@
         }
 
         applyWindowTransform();
+        updateLauncherTransitionProgress();
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index e7fe142..0bf81ef 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -724,7 +724,7 @@
         if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return false;
         }
-        if (mGestureState.getEndTarget() == NEW_TASK
+        if (mStateCallback.hasStates(STATE_START_NEW_TASK)
                 && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
             reset();
             return true;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 86cfbdf..03d522e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -15,6 +15,9 @@
  */
 package com.android.quickstep;
 
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
@@ -29,21 +32,32 @@
 import android.app.ActivityOptions;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.view.View;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAnimationRunner;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.FallbackRecentsStateController;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -51,26 +65,40 @@
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * A recents activity that shows the recently launched tasks as swipable task cards.
  * See {@link com.android.quickstep.views.RecentsView}.
  */
-public final class RecentsActivity extends BaseRecentsActivity {
+public final class RecentsActivity extends StatefulActivity<RecentsState> {
 
     public static final String EXTRA_THUMBNAIL = "thumbnailData";
     public static final String EXTRA_TASK_ID = "taskID";
+    public static final ActivityTracker<RecentsActivity> ACTIVITY_TRACKER =
+            new ActivityTracker<>();
 
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
     private RecentsRootView mRecentsRootView;
     private FallbackRecentsView mFallbackRecentsView;
+    private OverviewActionsView mActionsView;
 
-    @Override
+    private Configuration mOldConfig;
+
+    private StateManager<RecentsState> mStateManager;
+
+    /**
+     * Init drag layer and overview panel views.
+     */
     protected void initViews() {
         setContentView(R.layout.fallback_recents_activity);
         mRecentsRootView = findViewById(R.id.drag_layer);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
+        mActionsView = findViewById(R.id.overview_actions_view);
+
         mRecentsRootView.recreateControllers();
-        mFallbackRecentsView.init(findViewById(R.id.overview_actions_view));
+        mFallbackRecentsView.init(mActionsView);
     }
 
     @Override
@@ -103,25 +131,38 @@
         intent.removeExtra(EXTRA_TASK_ID);
         intent.removeExtra(EXTRA_THUMBNAIL);
         super.onNewIntent(intent);
+        ACTIVITY_TRACKER.handleNewIntent(this, intent);
     }
 
-    @Override
+    /**
+         * Logic for when device configuration changes (rotation, screen size change, multi-window,
+         * etc.)
+         */
     protected void onHandleConfigChanged() {
-        super.onHandleConfigChanged();
+        mUserEventDispatcher = null;
+        initDeviceProfile();
+
+        AbstractFloatingView.closeOpenViews(this, true,
+                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+        dispatchDeviceProfileChanged();
+
+        reapplyUi();
         mRecentsRootView.recreateControllers();
     }
 
-    @Override
-    protected void reapplyUi() {
-        mRecentsRootView.dispatchInsets();
-    }
-
-    @Override
+    /**
+     * Generate the device profile to use in this activity.
+     * @return device profile
+     */
     protected DeviceProfile createDeviceProfile() {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+        DeviceProfile dp1 = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+
+        // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
+        // activity.
         return (mRecentsRootView != null) && isInMultiWindowMode()
                 ? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
-                : super.createDeviceProfile();
+                : dp1.copy(this);
     }
 
     @Override
@@ -139,6 +180,10 @@
         return (T) mFallbackRecentsView;
     }
 
+    public OverviewActionsView getActionsView() {
+        return mActionsView;
+    }
+
     @Override
     public void returnToHomescreen() {
         super.returnToHomescreen();
@@ -160,12 +205,7 @@
                     RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets);
-                anim.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mFallbackRecentsView.resetViewUI();
-                    }
-                });
+                anim.addListener(resetStateListener());
                 result.setAnimation(anim, RecentsActivity.this);
             }
         };
@@ -193,12 +233,7 @@
                     .createAdjacentPageAnimForTaskLaunch(taskView);
             adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
             adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
-            adjacentAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mFallbackRecentsView.resetTaskVisuals();
-                }
-            });
+            adjacentAnimation.addListener(resetStateListener());
             target.play(adjacentAnimation);
         }
         return target;
@@ -210,13 +245,14 @@
         // onActivityStart callback.
         mFallbackRecentsView.setContentAlpha(1);
         super.onStart();
-        mFallbackRecentsView.resetTaskVisuals();
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-        mFallbackRecentsView.reset();
+
+        // Workaround for b/78520668, explicitly trim memory once UI is hidden
+        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
     }
 
     @Override
@@ -228,4 +264,98 @@
     public void onTaskLaunched() {
         mFallbackRecentsView.resetTaskVisuals();
     }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mStateManager = new StateManager<>(this, RecentsState.DEFAULT);
+
+        mOldConfig = new Configuration(getResources().getConfiguration());
+        initDeviceProfile();
+        initViews();
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+        ACTIVITY_TRACKER.handleCreate(this);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        int diff = newConfig.diff(mOldConfig);
+        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
+            onHandleConfigChanged();
+        }
+        mOldConfig.setTo(newConfig);
+        super.onConfigurationChanged(newConfig);
+    }
+
+    /**
+     * Initialize/update the device profile.
+     */
+    private void initDeviceProfile() {
+        mDeviceProfile = createDeviceProfile();
+        onDeviceProfileInitiated();
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+        // overview
+        RecentsModel.INSTANCE.get(this).getThumbnailCache()
+                .getHighResLoadingState().setVisible(true);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        super.onTrimMemory(level);
+        RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        ACTIVITY_TRACKER.onActivityDestroyed(this);
+    }
+
+    @Override
+    public void onBackPressed() {
+        // TODO: Launch the task we came from
+        startHome();
+    }
+
+    public void startHome() {
+        startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    @Override
+    protected StateHandler<RecentsState>[] createStateHandlers() {
+        return new StateHandler[] { new FallbackRecentsStateController(this) };
+    }
+
+    @Override
+    public StateManager<RecentsState> getStateManager() {
+        return mStateManager;
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        writer.println(prefix + "Misc:");
+        dumpMisc(prefix + "\t", writer);
+    }
+
+    private AnimatorListenerAdapter resetStateListener() {
+        return new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mFallbackRecentsView.resetTaskVisuals();
+                mStateManager.reapplyState();
+            }
+        };
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
new file mode 100644
index 0000000..3f1e7ba
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 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.fallback;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.views.ClearAllButton;
+
+/**
+ * State controller for fallback recents activity
+ */
+public class FallbackRecentsStateController implements StateHandler<RecentsState> {
+
+    private final StateAnimationConfig mNoConfig = new StateAnimationConfig();
+    private final RecentsActivity mActivity;
+    private final FallbackRecentsView mRecentsView;
+
+    public FallbackRecentsStateController(RecentsActivity activity) {
+        mActivity = activity;
+        mRecentsView = activity.getOverviewPanel();
+    }
+
+    @Override
+    public void setState(RecentsState state) {
+        mRecentsView.updateEmptyMessage();
+        mRecentsView.resetTaskVisuals();
+        setProperties(state, mNoConfig, PropertySetter.NO_ANIM_PROPERTY_SETTER);
+    }
+
+    @Override
+    public void setStateWithAnimation(RecentsState toState, StateAnimationConfig config,
+            PendingAnimation setter) {
+        if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
+            // The entire recents animation is played atomically.
+            return;
+        }
+        if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
+            return;
+        }
+        // While animating into recents, update the visible task data as needed
+        setter.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
+        mRecentsView.updateEmptyMessage();
+
+        setProperties(toState, config, setter);
+    }
+
+    private void setProperties(RecentsState state, StateAnimationConfig config,
+            PropertySetter setter) {
+        float buttonAlpha = state.hasButtons() ? 1 : 0;
+        setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
+                buttonAlpha, LINEAR);
+        setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
+                MultiValueAlpha.VALUE, buttonAlpha, LINEAR);
+
+        float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
+        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0],
+                config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+                config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
+
+        setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
+                config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
+        setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 559004c..1ab317b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,17 +15,17 @@
  */
 package com.android.quickstep.fallback;
 
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.quickstep.fallback.RecentsState.DEFAULT;
+import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
 import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
 
+import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
-import android.graphics.Canvas;
+import android.os.Build;
 import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.view.View;
 
-import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
@@ -34,26 +34,9 @@
 
 import java.util.ArrayList;
 
-public class FallbackRecentsView extends RecentsView<RecentsActivity> {
-
-    public static final FloatProperty<FallbackRecentsView> ZOOM_PROGRESS =
-            new FloatProperty<FallbackRecentsView> ("zoomInProgress") {
-
-                @Override
-                public void setValue(FallbackRecentsView view, float value) {
-                    view.setZoomProgress(value);
-                }
-
-                @Override
-                public Float get(FallbackRecentsView view) {
-                    return view.mZoomInProgress;
-                }
-            };
-
-    private float mZoomInProgress = 0;
-    private boolean mInOverviewState = true;
-
-    private float mZoomScale = 1f;
+@TargetApi(Build.VERSION_CODES.R)
+public class FallbackRecentsView extends RecentsView<RecentsActivity>
+        implements StateListener<RecentsState> {
 
     private RunningTaskInfo mRunningTaskInfo;
 
@@ -63,6 +46,7 @@
 
     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr, FALLBACK_RECENTS_SIZE_STRATEGY);
+        mActivity.getStateManager().addStateListener(this);
     }
 
     @Override
@@ -78,70 +62,11 @@
     }
 
     @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        updateEmptyMessage();
-    }
-
-    @Override
-    public void onViewRemoved(View child) {
-        super.onViewRemoved(child);
-        updateEmptyMessage();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        maybeDrawEmptyMessage(canvas);
-        super.draw(canvas);
-    }
-
-    @Override
-    public void reset() {
-        super.reset();
-        resetViewUI();
-    }
-
-    @Override
     public boolean shouldUseMultiWindowTaskSizeStrategy() {
         // Just use the activity task size for multi-window as well.
         return false;
     }
 
-    public void resetViewUI() {
-        setZoomProgress(0);
-        resetTaskVisuals();
-    }
-
-    public void setInOverviewState(boolean inOverviewState) {
-        if (mInOverviewState != inOverviewState) {
-            mInOverviewState = inOverviewState;
-            if (mInOverviewState) {
-                resetTaskVisuals();
-            } else {
-                setZoomProgress(1);
-            }
-        }
-    }
-
-    @Override
-    public void resetTaskVisuals() {
-        super.resetTaskVisuals();
-        setFullscreenProgress(mFullscreenProgress);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mZoomScale = getMaxScaleForFullScreen();
-        setZoomProgress(mZoomInProgress);
-    }
-
-    public void setZoomProgress(float progress) {
-        mZoomInProgress = progress;
-        SCALE_PROPERTY.set(this, Utilities.mapRange(mZoomInProgress, 1, mZoomScale));
-        FULLSCREEN_PROGRESS.set(this, mZoomInProgress);
-    }
-
     public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
         mRunningTaskInfo = runningTaskInfo;
         onGestureAnimationStart(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
@@ -178,4 +103,37 @@
         }
         super.applyLoadPlan(tasks);
     }
+
+    @Override
+    public void setModalStateEnabled(boolean isModalState) {
+        super.setModalStateEnabled(isModalState);
+        if (isModalState) {
+            mActivity.getStateManager().goToState(RecentsState.MODAL_TASK);
+        } else {
+            if (mActivity.isInState(RecentsState.MODAL_TASK)) {
+                mActivity.getStateManager().goToState(DEFAULT);
+            }
+        }
+    }
+
+    @Override
+    public void onStateTransitionStart(RecentsState toState) {
+        setOverviewStateEnabled(true);
+        setFreezeViewVisibility(true);
+    }
+
+    @Override
+    public void onStateTransitionComplete(RecentsState finalState) {
+        setOverlayEnabled(finalState == DEFAULT || finalState == MODAL_TASK);
+        setFreezeViewVisibility(false);
+    }
+
+    @Override
+    public void setOverviewStateEnabled(boolean enabled) {
+        super.setOverviewStateEnabled(enabled);
+        if (enabled) {
+            RecentsState state = mActivity.getStateManager().getState();
+            setDisallowScrollToClearAll(!state.hasButtons());
+        }
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
new file mode 100644
index 0000000..211a30c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.fallback;
+
+import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
+import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
+
+import android.content.Context;
+
+import com.android.launcher3.statemanager.BaseState;
+import com.android.quickstep.RecentsActivity;
+
+/**
+ * State definition for Fallback recents
+ */
+public class RecentsState implements BaseState<RecentsState> {
+
+    private static final int FLAG_MODAL = BaseState.getFlag(0);
+    private static final int FLAG_HAS_BUTTONS = BaseState.getFlag(1);
+    private static final int FLAG_FULL_SCREEN = BaseState.getFlag(2);
+
+    public static final RecentsState DEFAULT = new RecentsState(0, FLAG_HAS_BUTTONS);
+    public static final RecentsState MODAL_TASK = new ModalState(1,
+            FLAG_DISABLE_RESTORE | FLAG_HAS_BUTTONS | FLAG_MODAL);
+    public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
+            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
+
+    public final int ordinal;
+    private final int mFlags;
+
+    private static final float NO_OFFSET = 0;
+    private static final float NO_SCALE = 1;
+
+    public RecentsState(int id, int flags) {
+        this.ordinal = id;
+        this.mFlags = flags;
+    }
+
+
+    @Override
+    public String toString() {
+        return "Ordinal-" + ordinal;
+    }
+
+    @Override
+    public final boolean hasFlag(int mask) {
+        return (mFlags & mask) != 0;
+    }
+
+    @Override
+    public int getTransitionDuration(Context context) {
+        return 250;
+    }
+
+    @Override
+    public RecentsState getHistoryForState(RecentsState previousState) {
+        return DEFAULT;
+    }
+
+    /**
+     * For this state, how modal should over view been shown. 0 modalness means all tasks drawn,
+     * 1 modalness means the current task is show on its own.
+     */
+    public float getOverviewModalness() {
+        return hasFlag(FLAG_MODAL) ? 1 : 0;
+    }
+
+    public boolean isFullScreen() {
+        return hasFlag(FLAG_FULL_SCREEN);
+    }
+
+    public boolean hasButtons() {
+        return hasFlag(FLAG_HAS_BUTTONS);
+    }
+
+    public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+        return new float[] { NO_SCALE, NO_OFFSET };
+    }
+
+
+    private static class ModalState extends RecentsState {
+
+        public ModalState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return getOverviewScaleAndOffsetForModalState(activity);
+        }
+    }
+
+    private static class BackgroundAppState extends RecentsState {
+        public BackgroundAppState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return getOverviewScaleAndOffsetForBackgroundState(activity);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 9d7efc4..250c78b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -31,11 +31,9 @@
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Canvas;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
-import android.view.View;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.BaseQuickstepLauncher;
@@ -123,24 +121,6 @@
         }
     }
 
-    @Override
-    public void draw(Canvas canvas) {
-        maybeDrawEmptyMessage(canvas);
-        super.draw(canvas);
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        updateEmptyMessage();
-    }
-
-    @Override
-    protected void onTaskStackUpdated() {
-        // Lazily update the empty message only when the task stack is reapplied
-        updateEmptyMessage();
-    }
-
     /**
      * Animates adjacent tasks and translate hotseat off screen as well.
      */
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index 7201b02..f06a6a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -145,8 +145,6 @@
 
     /** Updates vertical margins for different navigation mode. */
     public void updateVerticalMarginForNavModeChange(Mode mode) {
-        int topMargin = getResources()
-                .getDimensionPixelSize(R.dimen.overview_actions_top_margin);
         int bottomMargin = 0;
         if (mode == Mode.THREE_BUTTONS) {
             bottomMargin = getResources()
@@ -157,6 +155,6 @@
         }
         LayoutParams params = (LayoutParams) getLayoutParams();
         params.setMargins(
-                params.leftMargin, topMargin, params.rightMargin, bottomMargin);
+                params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index bc702c6..bf9119e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -554,6 +554,13 @@
         child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
         updateTaskStartIndex(child);
         mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, false);
+        updateEmptyMessage();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        maybeDrawEmptyMessage(canvas);
+        super.draw(canvas);
     }
 
     private void updateTaskStartIndex(View affectingView) {
@@ -766,7 +773,10 @@
         return taskViewCount;
     }
 
-    protected void onTaskStackUpdated() { }
+    protected void onTaskStackUpdated() {
+        // Lazily update the empty message only when the task stack is reapplied
+        updateEmptyMessage();
+    }
 
     public void resetTaskVisuals() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
@@ -833,6 +843,11 @@
         mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
     }
 
+    /** Gets the task size for modal state. */
+    public void getModalTaskSize(Rect outRect) {
+        mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
+    }
+
     @Override
     protected boolean computeScrollHelper() {
         boolean scrolling = super.computeScrollHelper();
@@ -2149,18 +2164,6 @@
         updatePageOffsets();
         if (getCurrentPageTaskView() != null) {
             getCurrentPageTaskView().setModalness(modalness);
-            TaskView tv = getCurrentPageTaskView();
-
-            // Move the task view up as it scales...
-            // ...the icon on taskview is hidden in modal state, so consider the top of the task
-            mTempFloatPoint[0] = 0;
-            mTempFloatPoint[1] = tv.getTop() + mTaskTopMargin;
-            // ...find the top after the transformation
-            getMatrix().mapPoints(mTempFloatPoint);
-
-            // ...make it match the top inset
-            float calcOffset = (mInsets.top - mTempFloatPoint[1]) * mTaskModalness;
-            tv.setTranslationY(calcOffset);
         }
     }
 
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 5c30651..72275c8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,7 +24,6 @@
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
     <dimen name="overview_actions_height">66dp</dimen>
-    <dimen name="overview_actions_top_margin">44dp</dimen>
     <dimen name="overview_actions_bottom_margin_gesture">16dp</dimen>
     <dimen name="overview_actions_bottom_margin_three_button">8dp</dimen>
     <dimen name="overview_actions_horizontal_margin">16dp</dimen>
@@ -63,7 +62,8 @@
     <dimen name="task_card_menu_shadow_height">3dp</dimen>
     <dimen name="task_card_menu_horizontal_padding">0dp</dimen>
     <dimen name="portrait_task_card_horz_space">136dp</dimen>
-    <dimen name="portrait_task_card_horz_space_big_overview">24dp</dimen>
+    <dimen name="portrait_task_card_horz_space_big_overview">96dp</dimen>
+    <dimen name="portrait_modal_task_card_horz_space">60dp</dimen>
     <dimen name="landscape_task_card_horz_space">200dp</dimen>
     <dimen name="multi_window_task_card_horz_space">100dp</dimen>
     <!-- Copied from framework resource:
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 8368817..c841170 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -129,12 +129,12 @@
     <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
     <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string>
 
-  <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
-  <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
-    <!-- Button text shown on a button on the confirm screen. [CHAR LIMIT=14] -->
-    <string name="gesture_tutorial_action_button_label" translatable="false">Done</string>
-    <!-- Button text shown on a text button on the confirm screen. [CHAR LIMIT=14] -->
-    <string name="gesture_tutorial_action_text_button_label" translatable="false">Settings</string>
+    <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
+    <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
+    <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] -->
+    <string name="gesture_tutorial_action_button_label_done" translatable="false">Done</string>
+    <!-- Button text shown on a button to go to Settings. [CHAR LIMIT=14] -->
+    <string name="gesture_tutorial_action_button_label_settings" translatable="false">Settings</string>
 
     <!-- ******* Overview ******* -->
     <!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
deleted file mode 100644
index 1b9158b..0000000
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.util.ActivityTracker;
-import com.android.launcher3.util.SystemUiController;
-import com.android.launcher3.util.Themes;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * A base fallback recents activity that provides support for device profile changes, activity
- * lifecycle tracking, and basic input handling from recents.
- *
- * This class is only used as a fallback in case the default launcher does not have a recents
- * implementation.
- */
-public abstract class BaseRecentsActivity extends BaseDraggingActivity {
-
-    public static final ActivityTracker<BaseRecentsActivity> ACTIVITY_TRACKER =
-            new ActivityTracker<>();
-    private Configuration mOldConfig;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mOldConfig = new Configuration(getResources().getConfiguration());
-        initDeviceProfile();
-        initViews();
-
-        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
-                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
-        ACTIVITY_TRACKER.handleCreate(this);
-    }
-
-    /**
-     * Init drag layer and overview panel views.
-     */
-    abstract protected void initViews();
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        int diff = newConfig.diff(mOldConfig);
-        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
-            onHandleConfigChanged();
-        }
-        mOldConfig.setTo(newConfig);
-        super.onConfigurationChanged(newConfig);
-    }
-
-    /**
-     * Logic for when device configuration changes (rotation, screen size change, multi-window,
-     * etc.)
-     */
-    protected void onHandleConfigChanged() {
-        mUserEventDispatcher = null;
-        initDeviceProfile();
-
-        AbstractFloatingView.closeOpenViews(this, true,
-                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
-        dispatchDeviceProfileChanged();
-
-        reapplyUi();
-    }
-
-    /**
-     * Initialize/update the device profile.
-     */
-    private void initDeviceProfile() {
-        mDeviceProfile = createDeviceProfile();
-        onDeviceProfileInitiated();
-    }
-
-    /**
-     * Generate the device profile to use in this activity.
-     * @return device profile
-     */
-    protected DeviceProfile createDeviceProfile() {
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
-
-        // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
-        // activity.
-        return dp.copy(this);
-    }
-
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // Workaround for b/78520668, explicitly trim memory once UI is hidden
-        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
-    }
-
-    @Override
-    public void onEnterAnimationComplete() {
-        super.onEnterAnimationComplete();
-        // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
-        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
-        // overview
-        RecentsModel.INSTANCE.get(this).getThumbnailCache()
-                .getHighResLoadingState().setVisible(true);
-    }
-
-    @Override
-    public void onTrimMemory(int level) {
-        super.onTrimMemory(level);
-        RecentsModel.INSTANCE.get(this).onTrimMemory(level);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        ACTIVITY_TRACKER.handleNewIntent(this, intent);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        ACTIVITY_TRACKER.onActivityDestroyed(this);
-    }
-
-    @Override
-    public void onBackPressed() {
-        // TODO: Launch the task we came from
-        startHome();
-    }
-
-    public void startHome() {
-        startActivity(new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        writer.println(prefix + "Misc:");
-        dumpMisc(prefix + "\t", writer);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index b0ce8e6..2d9c56f 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -154,8 +154,7 @@
      * Loads and creates a list of all the recent tasks.
      */
     @VisibleForTesting
-    ArrayList<Task> loadTasksInBackground(int numTasks,
-            boolean loadKeysOnly) {
+    ArrayList<Task> loadTasksInBackground(int numTasks, boolean loadKeysOnly) {
         int currentUserId = Process.myUserHandle().getIdentifier();
         ArrayList<Task> allTasks = new ArrayList<>();
         List<ActivityManager.RecentTaskInfo> rawTasks =
@@ -174,9 +173,7 @@
             }
         };
 
-        int taskCount = rawTasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
+        for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
             Task.TaskKey taskKey = new Task.TaskKey(rawTask);
             Task task;
             if (!loadKeysOnly) {
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index f3cefb9..58870ed 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -68,13 +68,16 @@
     @Override
     Integer getActionButtonStringId() {
         if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label;
+            return R.string.gesture_tutorial_action_button_label_done;
         }
         return null;
     }
 
     @Override
     Integer getActionTextButtonStringId() {
+        if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
+            return R.string.gesture_tutorial_action_button_label_settings;
+        }
         return null;
     }
 
@@ -86,7 +89,6 @@
     @Override
     void onActionTextButtonClicked(View button) {
         mTutorialFragment.startSystemNavigationSetting();
-        mTutorialFragment.closeTutorial();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0e45376..524cbaf 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -60,7 +60,7 @@
     @Override
     Integer getActionButtonStringId() {
         if (mTutorialType == HOME_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label;
+            return R.string.gesture_tutorial_action_button_label_done;
         }
         return null;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 3a56b0e..44c1a5d 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep.interaction;
 
-import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.graphics.Insets;
 import android.os.Bundle;
@@ -35,8 +34,6 @@
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
-import java.net.URISyntaxException;
-
 abstract class TutorialFragment extends Fragment implements OnTouchListener {
 
     private static final String LOG_TAG = "TutorialFragment";
@@ -182,14 +179,6 @@
     }
 
     void startSystemNavigationSetting() {
-        try {
-            startActivityForResult(
-                    Intent.parseUri(SYSTEM_NAVIGATION_SETTING_INTENT, /* flags= */ 0),
-                    /* requestCode= */ 0);
-        } catch (URISyntaxException e) {
-            Log.e(LOG_TAG, "The launch Intent Uri is wrong syntax: " + e);
-        } catch (ActivityNotFoundException e) {
-            Log.e(LOG_TAG, "The launch Activity not found: " + e);
-        }
+        startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
index 84d84f2..dce6f67 100644
--- a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
+++ b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
@@ -106,6 +106,68 @@
                 Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
     }
 
+    /**
+     * Calculates the modal taskView size for the provided device configuration
+     */
+    public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+        float taskWidth, taskHeight, paddingHorz;
+        Resources res = context.getResources();
+        Rect insets = dp.getInsets();
+
+        if (dp.isMultiWindowMode) {
+            getMultiWindowSize(context, dp, mTempPoint);
+            taskWidth = mTempPoint.x;
+            taskHeight = mTempPoint.y;
+            paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
+        } else {
+            taskWidth = dp.availableWidthPx;
+            taskHeight = dp.availableHeightPx;
+
+            final int paddingResId;
+            if (dp.isVerticalBarLayout()) {
+                paddingResId = R.dimen.landscape_task_card_horz_space;
+            } else {
+                paddingResId = R.dimen.portrait_modal_task_card_horz_space;
+            }
+            paddingHorz = res.getDimension(paddingResId);
+        }
+
+        // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
+        // we override the insets ourselves.
+        int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
+        int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
+
+        // Calculate for the overview height.
+        float overviewActionsHeight = getOverviewActionsHeight(context);
+        float availableHeight = launcherVisibleHeight - overviewActionsHeight;
+        float availableWidth = launcherVisibleWidth - paddingHorz;
+
+        float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+        float outWidth = scale * taskWidth;
+        float outHeight = scale * taskHeight;
+
+        // Center in the visible space
+        float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
+        float y = insets.top + (launcherVisibleHeight - overviewActionsHeight - outHeight) / 2;
+        outRect.set(Math.round(x), Math.round(y),
+                Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+    }
+
+    /** Gets the space that the overview actions will take, including margins. */
+    public float getOverviewActionsHeight(Context context) {
+        Resources res = context.getResources();
+        float actionsBottomMargin = 0;
+        if (getMode(context) == Mode.THREE_BUTTONS) {
+            actionsBottomMargin = res.getDimensionPixelSize(
+                R.dimen.overview_actions_bottom_margin_three_button);
+        } else {
+            actionsBottomMargin = res.getDimensionPixelSize(
+                R.dimen.overview_actions_bottom_margin_gesture);
+        }
+        float overviewActionsHeight = actionsBottomMargin
+                + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+        return overviewActionsHeight;
+    }
 
     public static final WindowSizeStrategy LAUNCHER_ACTIVITY_SIZE_STRATEGY =
             new WindowSizeStrategy(true) {
@@ -119,19 +181,7 @@
                 if (showOverviewActions(context)) {
                     //TODO: this needs to account for the swipe gesture height and accessibility
                     // UI when shown.
-                    float actionsBottomMargin = 0;
-                    if (getMode(context) == Mode.THREE_BUTTONS) {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                                R.dimen.overview_actions_bottom_margin_three_button);
-                    } else {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                                R.dimen.overview_actions_bottom_margin_gesture);
-                    }
-                    float actionsTopMargin = res.getDimensionPixelSize(
-                            R.dimen.overview_actions_top_margin);
-                    float actionsHeight = actionsTopMargin + actionsBottomMargin
-                            + res.getDimensionPixelSize(R.dimen.overview_actions_height);
-                    return actionsHeight;
+                    return getOverviewActionsHeight(context);
                 } else {
                     return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
                             + res.getDimensionPixelSize(
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index b4c5f96..d75d712 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
 
@@ -27,6 +29,7 @@
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -38,6 +41,8 @@
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
+    private final StatsLogManager mStatsLogManager;
+
     private int mControlType = ControlType.DEFAULT_CONTROLTYPE;
 
     public DeleteDropTarget(Context context, AttributeSet attrs) {
@@ -46,6 +51,7 @@
 
     public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        this.mStatsLogManager = StatsLogManager.newInstance(context);
     }
 
     @Override
@@ -120,6 +126,11 @@
             d.dragInfo.container = NO_ID;
         }
         super.onDrop(d, options);
+        mStatsLogManager.log(
+                mControlType == ControlType.REMOVE_TARGET
+                        ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+                        : LAUNCHER_ITEM_DROPPED_ON_CANCEL,
+                d.logInstanceId);
     }
 
     @Override
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 9dbb5fc..fbac0bd 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -7,6 +7,10 @@
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_UNINSTALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_CANCELLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_COMPLETED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 
@@ -34,6 +38,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -58,7 +63,7 @@
 
     private static final long CACHE_EXPIRE_TIMEOUT = 5000;
     private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
-
+    private final StatsLogManager mStatsLogManager;
     private final Alarm mCacheExpireAlarm;
     private boolean mHadPendingAlarm;
 
@@ -69,8 +74,8 @@
 
     public SecondaryDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-
         mCacheExpireAlarm = new Alarm();
+        mStatsLogManager = StatsLogManager.newInstance(context);
     }
 
     @Override
@@ -214,6 +219,11 @@
         // Defer onComplete
         d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
         super.onDrop(d, options);
+        if (mCurrentAccessibilityAction == UNINSTALL) {
+            mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL, d.logInstanceId);
+        } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+            mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST, d.logInstanceId);
+        }
     }
 
     @Override
@@ -338,8 +348,10 @@
                     mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
                 mDragObject.dragSource = mOriginal;
                 mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
+                mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED, mDragObject.logInstanceId);
             } else {
                 sendFailure();
+                mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED, mDragObject.logInstanceId);
             }
         }
 
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 20eec9a..c62f308 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -29,6 +29,7 @@
 import android.view.Display;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
 
 import com.android.launcher3.InvariantDeviceProfile;
 
@@ -37,6 +38,8 @@
 /** Render preview using surface view. */
 public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
 
+    private static final int FADE_IN_ANIMATION_DURATION = 200;
+
     private static final String KEY_HOST_TOKEN = "host_token";
     private static final String KEY_VIEW_WIDTH = "width";
     private static final String KEY_VIEW_HEIGHT = "height";
@@ -99,6 +102,11 @@
             view.setPivotY(0);
             view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
             view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
+            view.setAlpha(0);
+            view.animate().alpha(1)
+                    .setInterpolator(new AccelerateDecelerateInterpolator())
+                    .setDuration(FADE_IN_ANIMATION_DURATION)
+                    .start();
             mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(),
                     view.getMeasuredHeight());
         });
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index fdd32b8..9455bd3 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -16,20 +16,24 @@
 package com.android.launcher3.logging;
 
 import android.content.Context;
+import android.util.Log;
 
 import com.android.launcher3.R;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ItemInfo;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
  * Handles the user event logging in R+.
- * All of the event id is defined here.
+ * All of the event ids are defined here.
  * Most of the methods are dummy methods for Launcher3
  * Actual call happens only for Launcher variant that implements QuickStep.
  */
 public class StatsLogManager implements ResourceBasedOverride {
 
+    private static final String TAG = "StatsLogManager";
+
     interface EventEnum {
         int getId();
     }
@@ -37,19 +41,46 @@
     public enum LauncherEvent implements EventEnum {
         @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
         LAUNCHER_APP_LAUNCH_TAP(338),
+
         @LauncherUiEvent(doc = "Task launched from overview using TAP")
         LAUNCHER_TASK_LAUNCH_TAP(339),
+
         @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
         LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
+
         @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
         LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
+
         @LauncherUiEvent(doc = "User dragged a launcher item")
         LAUNCHER_ITEM_DRAG_STARTED(383),
+
         @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
         LAUNCHER_ITEM_DROP_COMPLETED(385),
+
         @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
-                + "resulting in new folder creation")
-        LAUNCHER_ITEM_DROP_FOLDER_CREATED(386);
+                + "resulting in a new folder creation")
+        LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
+
+        @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
+        LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
+
+        @LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
+        LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
+
+        @LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+                + " button in the target bar")
+        LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
+
+        @LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
+        LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
+
+        @LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
+                + "the icon onto 'Uninstall' button in the target bar")
+        LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
+
+        @LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
+                + "the icon onto 'Uninstall' button in the target bar")
+        LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
         // ADD MORE
 
         private final int mId;
@@ -78,6 +109,14 @@
     }
 
     /**
+     * Logs an event and accompanying {@link ItemInfo}
+     */
+    public void log(LauncherEvent event, InstanceId instanceId) {
+        Log.d(TAG, String.format("%s(InstanceId:%s)", event.name(), instanceId));
+        // Call StatsLog method
+    }
+
+    /**
      * Logs an event and accompanying {@link LauncherAtom.ItemInfo}
      */
     public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) { }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 4359f25..7611ee7 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -303,7 +303,7 @@
                     break;
             }
             itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
-        } else {
+        } else if (getContainerInfo().getContainerCase().getNumber() > 0) {
             itemBuilder.setContainerInfo(getContainerInfo());
         }
         return itemBuilder.build();
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index da631bd..e6de06d 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -167,8 +167,8 @@
 
     @Override
     public void onLongPress(MotionEvent event) {
-        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Workspace.longPress");
         if (mLongPressState == STATE_REQUESTED) {
+            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Workspace.longPress");
             if (canHandleLongPress()) {
                 mLongPressState = STATE_PENDING_PARENT_INFORM;
                 mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 94ab780..ce94a3e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -86,6 +86,11 @@
                         zeroButtonToOverviewGestureStartsInLauncher()
                                 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
                                 : LauncherInstrumentation.GestureScope.OUTSIDE;
+
+                // b/156044202
+                mLauncher.log("Hierarchy before swiping up to overview:");
+                mLauncher.dumpViewHierarchy();
+
                 mLauncher.sendPointer(
                         downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
                 mLauncher.executeAndWaitForEvent(