Merge "Fade in grid preview" into ub-launcher3-rvc-dev
diff --git a/Android.mk b/Android.mk
index 9cfcf17..fcd4a94 100644
--- a/Android.mk
+++ b/Android.mk
@@ -129,6 +129,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    SystemUI-statsd \
     SystemUISharedLib \
     launcherprotosnano \
     launcher_log_protos_lite
@@ -201,6 +202,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    SystemUI-statsd \
     SystemUISharedLib \
     launcherprotosnano \
     launcher_log_protos_lite
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index a89fe5c..cac2d8f 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -23,9 +23,10 @@
 message ItemInfo {
   oneof Item {
     Application application = 1;
-    Task task= 2;
+    Task task = 2;
     Shortcut shortcut = 3;
     Widget widget = 4;
+    FolderIcon folder_icon = 9;
   }
   // When used for launch event, stores the global predictive rank
   optional int32 rank = 5;
@@ -34,13 +35,23 @@
   optional bool is_work = 6;
 
   // Item can be child node to parent container or parent containers (nested)
-  oneof Container {
-    WorkspaceContainer workspace = 7;
-    HotseatContainer hotseat = 8;
-    FolderContainer folder = 9;
-  }
+  optional ContainerInfo container_info = 7;
+
   // Stores the origin of the Item
-  optional Origin source = 10;
+  optional Origin source = 8;
+}
+
+// Represents various launcher surface where items are placed.
+message ContainerInfo {
+  oneof Container {
+    WorkspaceContainer workspace = 1;
+    HotseatContainer hotseat = 2;
+    FolderContainer folder = 3;
+    AllAppsContainer all_apps_container = 4;
+  }
+}
+
+message AllAppsContainer {
 }
 
 enum Origin {
@@ -68,8 +79,8 @@
 
 // AppWidgets handled by AppWidgetManager
 message Widget {
-  optional int32 span_x = 1;
-  optional int32 span_y = 2;
+  optional int32 span_x = 1 [default = 1];
+  optional int32 span_y = 2 [default = 1];
   optional int32 app_widget_id = 3;
   optional string package_name = 4; // only populated during snapshot if from workspace
   optional string component_name = 5; // only populated during snapshot if from workspace
@@ -82,13 +93,18 @@
   optional int32 index = 3;
 }
 
+// Represents folder in a closed state.
+message FolderIcon {
+  optional int32 cardinality = 1;
+}
+
 //////////////////////////////////////////////
 // Containers
 
 message WorkspaceContainer {
-  optional int32 page_index = 1; // range [-1, l], 0 is the index of the main homescreen
-  optional int32 grid_x = 2;     // [0, m], m varies based on the display density and resolution
-  optional int32 grid_y = 3;     // [0, n], n varies based on the display density and resolution
+  optional int32 page_index = 1 [default = -2]; // range [-1, l], 0 is the index of the main homescreen
+  optional int32 grid_x = 2 [default = -1]; // [0, m], m varies based on the display density and resolution
+  optional int32 grid_y = 3 [default = -1]; // [0, n], n varies based on the display density and resolution
 }
 
 message HotseatContainer {
@@ -96,13 +112,11 @@
 }
 
 message FolderContainer {
-  optional int32 page_index = 1;
-  optional int32 grid_x = 2;
-  optional int32 grid_y = 3;
-  oneof Container {
+  optional int32 page_index = 1 [default = -1];
+  optional int32 grid_x = 2 [default = -1];
+  optional int32 grid_y = 3 [default = -1];
+  oneof ParentContainer {
     WorkspaceContainer workspace = 4;
     HotseatContainer hotseat = 5;
   }
 }
-
-
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index c93cad6..b9621e4 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -24,13 +24,13 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="32dp"
-        android:backgroundTint="?android:attr/colorAccent"
+        android:backgroundTint="?attr/eduHalfSheetBGColor"
         android:background="@drawable/bottom_sheet_top_border" />
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?android:attr/colorAccent"
+        android:background="?attr/eduHalfSheetBGColor"
         android:orientation="vertical">
 
         <TextView
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 79b4002..c037e44 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -16,40 +16,24 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
 import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -60,13 +44,6 @@
  */
 public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
 
-    public static final int INDEX_SHELF_ANIM = 0;
-    public static final int INDEX_RECENTS_FADE_ANIM = 1;
-    public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
-    public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = 3;
-
-    public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
-
     public LauncherAppTransitionManagerImpl(Context context) {
         super(context);
     }
@@ -86,11 +63,8 @@
         boolean skipLauncherChanges = !launcherClosing;
 
         TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
-
-        AppWindowAnimationHelper helper =
-            new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher);
         Animator recentsAnimator = getRecentsWindowAnimator(taskView, skipLauncherChanges,
-                appTargets, wallpaperTargets, mLauncher.getDepthController(), helper);
+                appTargets, wallpaperTargets, mLauncher.getDepthController());
         anim.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
 
         Animator childStateAnimation = null;
@@ -98,7 +72,7 @@
         Animator launcherAnim;
         final AnimatorListenerAdapter windowAnimEndListener;
         if (launcherClosing) {
-            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
+            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
             launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
 
@@ -151,80 +125,8 @@
 
         return () -> {
             overview.setFreezeViewVisibility(false);
+            overview.setTranslationY(0);
             mLauncher.getStateManager().reapplyState();
         };
     }
-
-    @Override
-    public int getStateElementAnimationsCount() {
-        return 4;
-    }
-
-    @Override
-    public Animator createStateElementAnimation(int index, float... values) {
-        switch (index) {
-            case INDEX_SHELF_ANIM: {
-                AllAppsTransitionController aatc = mLauncher.getAllAppsController();
-                Animator springAnim = aatc.createSpringAnimation(values);
-
-                if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
-                    // Translate hotseat with the shelf until reaching overview.
-                    float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
-                    ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mLauncher);
-                    float shiftRange = aatc.getShiftRange();
-                    if (values.length == 1) {
-                        values = new float[] {aatc.getProgress(), values[0]};
-                    }
-                    ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
-                    hotseatAnim.addUpdateListener(anim -> {
-                        float progress = (Float) anim.getAnimatedValue();
-                        if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
-                            float hotseatShift = (progress - overviewProgress) * shiftRange;
-                            mLauncher.getHotseat().setTranslationY(hotseatShift + sat.translationY);
-                        }
-                    });
-                    hotseatAnim.setInterpolator(LINEAR);
-                    hotseatAnim.setDuration(springAnim.getDuration());
-
-                    AnimatorSet anim = new AnimatorSet();
-                    anim.play(hotseatAnim);
-                    anim.play(springAnim);
-                    return anim;
-                }
-
-                return springAnim;
-            }
-            case INDEX_RECENTS_FADE_ANIM:
-                return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
-                        RecentsView.CONTENT_ALPHA, values);
-            case INDEX_RECENTS_TRANSLATE_X_ANIM: {
-                RecentsView rv = mLauncher.getOverviewPanel();
-                return new SpringAnimationBuilder(mLauncher)
-                        .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
-                        .setDampingRatio(0.8f)
-                        .setStiffness(250)
-                        .setValues(values)
-                        .build(rv, ADJACENT_PAGE_OFFSET);
-            }
-            case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
-                StateAnimationConfig config = new StateAnimationConfig();
-                config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
-
-                config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
-                config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
-                if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
-                    config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
-                    config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
-                }
-
-
-                LauncherStateManager stateManager = mLauncher.getStateManager();
-                return stateManager.createAtomicAnimation(
-                        stateManager.getCurrentStableState(), OVERVIEW, config);
-            }
-
-            default:
-                return super.createStateElementAnimation(index, values);
-        }
-    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
index d93aea4..8477b10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -26,10 +26,10 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.views.ArrowTipView;
 import com.android.systemui.shared.system.LauncherEventUtil;
 
@@ -71,17 +71,16 @@
 
     public static void scheduleShowIfNeeded(Launcher launcher) {
         if (!hasSeenAllAppsTip(launcher)) {
-            launcher.getStateManager().addStateListener(
-                    new LauncherStateManager.StateListener() {
-                        @Override
-                        public void onStateTransitionComplete(LauncherState finalState) {
-                            if (finalState == ALL_APPS) {
-                                if (showAllAppsTipIfNecessary(launcher)) {
-                                    launcher.getStateManager().removeStateListener(this);
-                                }
-                            }
+            launcher.getStateManager().addStateListener(new StateListener<LauncherState>() {
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    if (finalState == ALL_APPS) {
+                        if (showAllAppsTipIfNecessary(launcher)) {
+                            launcher.getStateManager().removeStateListener(this);
                         }
-                    });
+                    }
+                }
+            });
         }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
index 81a6070..914d9e9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -38,18 +38,18 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.Themes;
 
 /**
  * A view which shows a horizontal divider
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class AppsDividerView extends View implements LauncherStateManager.StateListener,
+public class AppsDividerView extends View implements StateListener<LauncherState>,
         FloatingHeaderRow {
 
     private static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index e68627a..ab3c71a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -32,7 +32,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
@@ -41,6 +40,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.MainThreadInitializedObject;
@@ -63,8 +63,8 @@
  * 4) Maintains the current active client id (for the predictions) and all updates are performed on
  * that client id.
  */
-public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
-        OnIDPChangeListener, OnUpdateListener {
+public class PredictionUiStateManager implements StateListener<LauncherState>,
+        ItemInfoUpdateReceiver, OnIDPChangeListener, OnUpdateListener {
 
     public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 78cc2dc..7f8f0a0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -48,6 +48,7 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.IntStream;
 
 /**
  * Controller class for managing user onboaridng flow for hybrid hotseat
@@ -110,7 +111,8 @@
             ItemInfo info = (ItemInfo) view.getTag();
             if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
                 folders.add((FolderInfo) info);
-            } else if (info instanceof WorkspaceItemInfo) {
+            } else if (info instanceof WorkspaceItemInfo && info.container == LauncherSettings
+                    .Favorites.CONTAINER_HOTSEAT) {
                 putIntoFolder.add((WorkspaceItemInfo) info);
             }
         }
@@ -206,6 +208,7 @@
             View child = mHotseat.getChildAt(i, 0);
             if (child == null || child.getTag() == null) continue;
             ItemInfo tag = (ItemInfo) child.getTag();
+            if (tag.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) continue;
             mLauncher.getModelWriter().moveItemInDatabase(tag,
                     LauncherSettings.Favorites.CONTAINER_DESKTOP, pageId, i, toRow);
             mNewItems.add(tag);
@@ -300,13 +303,23 @@
     }
 
     void showEdu() {
+        int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
+        CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID);
         // hotseat is already empty and does not require migration. show edu tip
-        if (mHotseat.getShortcutsAndWidgets().getChildCount() == 0) {
-            new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_auto_enrolled),
+        boolean requiresMigration = IntStream.range(0, childCount).anyMatch(i -> {
+            View v = mHotseat.getShortcutsAndWidgets().getChildAt(i);
+            return v != null && v.getTag() != null && ((ItemInfo) v.getTag()).container
+                    != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+        });
+        boolean canMigrateToFirstPage = cellLayout.makeSpaceForHotseatMigration(false);
+        if (requiresMigration && canMigrateToFirstPage) {
+            showDialog();
+        } else {
+            new ArrowTipView(mLauncher).show(mLauncher.getString(
+                    requiresMigration ? R.string.hotseat_tip_no_empty_slots
+                            : R.string.hotseat_auto_enrolled),
                     mHotseat.getTop());
             finishOnboarding();
-        } else {
-            showDialog();
         }
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 1aff8e9..e9f3534 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -16,9 +16,6 @@
 package com.android.launcher3.hybridhotseat;
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.logging.LoggerUtils.newAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -32,7 +29,6 @@
 import android.content.ComponentName;
 import android.os.Bundle;
 import android.os.Process;
-import android.provider.DeviceConfig;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,6 +43,7 @@
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
@@ -60,7 +57,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.PredictionModel;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -70,12 +67,13 @@
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.uioverrides.DeviceFlag;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IntArray;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -98,8 +96,6 @@
     //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
     private static final int APPTARGET_ACTION_UNPIN = 4;
 
-    private static final String PREDICTED_ITEMS_CACHE_KEY = "predicted_item_keys";
-
     private static final String APP_LOCATION_HOTSEAT = "hotseat";
     private static final String APP_LOCATION_WORKSPACE = "workspace";
 
@@ -120,11 +116,13 @@
 
     private DynamicItemCache mDynamicItemCache;
 
+    private final PredictionModel mPredictionModel;
     private AppPredictor mAppPredictor;
     private AllAppsStore mAllAppsStore;
     private AnimatorSet mIconRemoveAnimators;
     private boolean mUIUpdatePaused = false;
-    private boolean mRequiresCacheUpdate = false;
+    private boolean mRequiresCacheUpdate = true;
+    private boolean mIsCacheEmpty;
 
     private HotseatEduController mHotseatEduController;
 
@@ -143,21 +141,22 @@
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
         mAllAppsStore = mLauncher.getAppsView().getAppsStore();
+        mPredictionModel = LauncherAppState.INSTANCE.get(launcher).getPredictionModel();
         mAllAppsStore.addUpdateListener(this);
         mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
         mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
         launcher.getDeviceProfile().inv.addOnChangeListener(this);
         mHotseat.addOnAttachStateChangeListener(this);
+        mIsCacheEmpty = mPredictionModel.getPredictionComponentKeys().isEmpty();
         if (mHotseat.isAttachedToWindow()) {
             onViewAttachedToWindow(mHotseat);
         }
-        showCachedItems();
     }
 
     /**
-     * Returns whether or not the prediction controller is ready to show predictions
+     * Returns whether or not user has seen hybrid hotseat education
      */
-    public boolean isReady() {
+    public boolean isEduSeen() {
         return mLauncher.getSharedPrefs().getBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN,
                 false);
     }
@@ -186,10 +185,15 @@
     }
 
     private void fillGapsWithPrediction(boolean animate, Runnable callback) {
-        if (!isReady() || mUIUpdatePaused || mDragObject != null) {
+        if (mUIUpdatePaused || mDragObject != null) {
             return;
         }
         List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
+        if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) {
+            // Safely ignore update as AppsList is not ready yet. This will called again once
+            // apps are ready (HotseatPredictionController#onAppsUpdated)
+            return;
+        }
         int predictionIndex = 0;
         ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
         // make sure predicted icon removal and filling predictions don't step on each other
@@ -262,6 +266,10 @@
         if (mAppPredictor != null) {
             mAppPredictor.destroy();
         }
+        if (mHotseatEduController != null) {
+            mHotseatEduController.destroy();
+            mHotseatEduController = null;
+        }
     }
 
     /**
@@ -291,24 +299,38 @@
                         .setPredictedTargetCount(mHotSeatItemsCount)
                         .setExtras(getAppPredictionContextExtra())
                         .build());
-        mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
-                this::setPredictedApps);
+        WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
+        mAppPredictor.registerPredictionUpdates(mLauncher.getApplicationContext().getMainExecutor(),
+                list -> {
+                    if (controllerRef.get() != null) {
+                        controllerRef.get().setPredictedApps(list);
+                    }
+                });
+
         setPauseUIUpdate(false);
-        performBetaCheck();
-        if (!isReady()) {
+        if (!isEduSeen()) {
             mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
         }
         mAppPredictor.requestPredictionUpdate();
     }
 
-    private void showCachedItems() {
-        ArrayList<ComponentKey> componentKeys = getCachedComponentKeys();
+    /**
+     * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
+     */
+    public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
+        int count = Math.min(ranks.size(), apps.size());
+        List<WorkspaceItemInfo> items = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
+            preparePredictionInfo(item, ranks.get(i));
+            items.add(item);
+        }
         mComponentKeyMappers.clear();
-        for (ComponentKey key : componentKeys) {
+        for (ComponentKey key : mPredictionModel.getPredictionComponentKeys()) {
             mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
         }
         updateDependencies();
-        fillGapsWithPrediction();
+        bindItems(items, false, null);
     }
 
     private Bundle getAppPredictionContextExtra() {
@@ -386,42 +408,20 @@
         predictionLog.append("]");
         if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
         updateDependencies();
-        if (isReady()) {
-            fillGapsWithPrediction();
-        } else if (mHotseatEduController != null) {
+        fillGapsWithPrediction();
+        if (!isEduSeen() && mHotseatEduController != null) {
             mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
         }
-        // should invalidate cache if AiAi sends empty list of AppTargets
-        if (appTargets.isEmpty()) {
-            mRequiresCacheUpdate = true;
-        }
-        cachePredictionComponentKeys(componentKeys);
+        cachePredictionComponentKeysIfNecessary(componentKeys);
     }
 
-    private void cachePredictionComponentKeys(ArrayList<ComponentKey> componentKeys) {
-        if (!mRequiresCacheUpdate) return;
-        StringBuilder builder = new StringBuilder();
-        for (ComponentKey componentKey : componentKeys) {
-            builder.append(componentKey);
-            builder.append("\n");
-        }
-        mLauncher.getDevicePrefs().edit().putString(PREDICTED_ITEMS_CACHE_KEY,
-                builder.toString()).apply();
+    private void cachePredictionComponentKeysIfNecessary(ArrayList<ComponentKey> componentKeys) {
+        if (!mRequiresCacheUpdate && componentKeys.isEmpty() == mIsCacheEmpty) return;
+        mPredictionModel.cachePredictionComponentKeys(componentKeys);
+        mIsCacheEmpty = componentKeys.isEmpty();
         mRequiresCacheUpdate = false;
     }
 
-    private ArrayList<ComponentKey> getCachedComponentKeys() {
-        String cachedBlob = mLauncher.getDevicePrefs().getString(PREDICTED_ITEMS_CACHE_KEY, "");
-        ArrayList<ComponentKey> results = new ArrayList<>();
-        for (String line : cachedBlob.split("\n")) {
-            ComponentKey key = ComponentKey.fromString(line);
-            if (key != null) {
-                results.add(key);
-            }
-        }
-        return results;
-    }
-
     private void updateDependencies() {
         mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
                 mHotSeatItemsCount);
@@ -682,42 +682,6 @@
         }
     }
 
-    private void performBetaCheck() {
-        if (isReady()) return;
-        int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount();
-
-        int maxItems = DeviceConfig.getInt(
-                DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5);
-
-        // -1 to exclude smart space
-        int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
-                Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
-
-        // opt user into the feature without onboarding tip or migration if they don't have any
-        // open spots in their hotseat and have more than maxItems in their hotseat + workspace
-
-        if (hotseatItemsCount == mHotSeatItemsCount && workspaceItemCount + hotseatItemsCount
-                > maxItems) {
-            mLauncher.getSharedPrefs().edit().putBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN,
-                    true).apply();
-
-            LauncherLogProto.Action action = newAction(LauncherLogProto.Action.Type.TOUCH);
-            LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.TIP);
-            action.touch = LauncherLogProto.Action.Touch.TAP;
-            target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
-            target.controlType = LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
-
-            // temporarily encode details in log target (go/hotseat_migration)
-            target.rank = 2;
-            target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
-            target.pageIndex = maxItems;
-            LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
-            UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null);
-
-
-        }
-    }
-
     /**
      * Fill in predicted_rank field based on app prediction.
      * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index e074b03..6cfc846 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -15,14 +15,22 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -31,13 +39,19 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
 import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
 import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
@@ -49,6 +63,7 @@
 import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -56,8 +71,12 @@
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.stream.Stream;
 
 public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -166,15 +185,70 @@
     }
 
     @Override
+    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
+        super.bindPredictedItems(appInfos, ranks);
+        if (mHotseatPredictionController != null) {
+            mHotseatPredictionController.showCachedItems(appInfos, ranks);
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         if (mHotseatPredictionController != null) {
             mHotseatPredictionController.destroy();
+            mHotseatPredictionController = null;
+        }
+    }
+
+    @Override
+    public void onStateSetEnd(LauncherState state) {
+        super.onStateSetEnd(state);
+
+        switch (state.ordinal) {
+            case HINT_STATE_ORDINAL: {
+                Workspace workspace = getWorkspace();
+                boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
+                getStateManager().goToState(NORMAL, true,
+                        willMoveScreens ? null : getScrimView()::startDragHandleEducationAnim);
+                if (willMoveScreens) {
+                    workspace.post(workspace::moveToDefaultScreen);
+                }
+                break;
+            }
+            case OVERVIEW_STATE_ORDINAL: {
+                DiscoveryBounce.showForOverviewIfNeeded(this);
+                RecentsView rv = getOverviewPanel();
+                sendCustomAccessibilityEvent(
+                        rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
+                break;
+            }
+            case QUICK_SWITCH_STATE_ORDINAL: {
+                RecentsView rv = getOverviewPanel();
+                TaskView tasktolaunch = rv.getTaskViewAt(0);
+                if (tasktolaunch != null) {
+                    tasktolaunch.launchTask(false, success -> {
+                        if (!success) {
+                            getStateManager().goToState(OVERVIEW);
+                            tasktolaunch.notifyTaskLaunchFailed(TAG);
+                        } else {
+                            getStateManager().moveToRestState();
+                        }
+                    }, MAIN_EXECUTOR.getHandler());
+                } else {
+                    getStateManager().goToState(NORMAL);
+                }
+                break;
+            }
+
         }
     }
 
     @Override
     public TouchController[] createTouchControllers() {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.1");
+        }
         Mode mode = SysUINavigationMode.getMode(this);
 
         ArrayList<TouchController> list = new ArrayList<>();
@@ -182,7 +256,13 @@
         if (mode == NO_BUTTON) {
             list.add(new NoButtonQuickSwitchTouchController(this));
             list.add(new NavBarToHomeTouchController(this));
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.2");
+            }
             if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.3");
+                }
                 list.add(new NoButtonNavbarToOverviewTouchController(this));
             } else {
                 list.add(new FlingAndHoldTouchController(this));
@@ -211,6 +291,11 @@
         return list.toArray(new TouchController[list.size()]);
     }
 
+    @Override
+    public AtomicAnimationFactory createAtomicAnimationFactory() {
+        return new QuickstepAtomicAnimationFactory(this);
+    }
+
     private static final class LauncherTaskViewController extends
             TaskViewTouchController<Launcher> {
 
@@ -233,4 +318,13 @@
             mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
         }
     }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        RecentsView recentsView = getOverviewPanel();
+        writer.println("\nQuickstepLauncher:");
+        writer.println(prefix + "\tmOrientationState: " + (recentsView == null ? "recentsNull" :
+                recentsView.getPagedViewOrientedState()));
+    }
 }
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 e57e841..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,22 +17,20 @@
 
 import android.content.Context;
 
-import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 
 /**
  * State indicating that the Launcher is behind an app
  */
 public class BackgroundAppState extends OverviewState {
 
-    private static final int STATE_FLAGS =
-            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY
-                    | FLAG_DISABLE_INTERACTION;
+    private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI
+            | FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS;
 
     public BackgroundAppState(int id) {
         this(id, LauncherLogProto.ContainerType.TASKSWITCHER);
@@ -43,11 +41,6 @@
     }
 
     @Override
-    public void onStateEnabled(Launcher launcher) {
-        AbstractFloatingView.closeAllOpenViews(launcher, false);
-    }
-
-    @Override
     public float getVerticalProgress(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return super.getVerticalProgress(launcher);
@@ -62,27 +55,7 @@
 
     @Override
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        return new float[] {getOverviewScale(launcher), NO_OFFSET};
-    }
-
-    private float getOverviewScale(Launcher launcher) {
-        // Initialize the recents view scale to what it would be when starting swipe up
-        RecentsView recentsView = launcher.getOverviewPanel();
-        int taskCount = recentsView.getTaskViewCount();
-        if (taskCount == 0) return 1;
-
-        TaskView dummyTask;
-        if (recentsView.getCurrentPage() >= recentsView.getTaskViewStartIndex()) {
-            if (recentsView.getCurrentPage() <= taskCount - 1) {
-                dummyTask = recentsView.getCurrentPageTaskView();
-            } else {
-                dummyTask = recentsView.getTaskViewAt(taskCount - 1);
-            }
-        } else {
-            dummyTask = recentsView.getTaskViewAt(0);
-        }
-        return recentsView.getTempAppWindowAnimationHelper()
-                .updateForFullscreenOverview(dummyTask).getSrcToTargetScale();
+        return getOverviewScaleAndOffsetForBackgroundState(launcher);
     }
 
     @Override
@@ -112,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 b238200..414d389 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
@@ -15,9 +15,11 @@
  */
 package com.android.launcher3.uioverrides.states;
 
+import android.content.Context;
 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;
@@ -30,15 +32,15 @@
 public class OverviewModalTaskState extends OverviewState {
 
     private static final int STATE_FLAGS =
-            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE;
 
     public OverviewModalTaskState(int id) {
         super(id, ContainerType.OVERVIEW, STATE_FLAGS);
     }
 
     @Override
-    public int getTransitionDuration(Launcher launcher) {
-        return 100;
+    public int getTransitionDuration(Context launcher) {
+        return 300;
     }
 
     @Override
@@ -48,22 +50,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.task_thumbnail_bottom_margin_with_actions);
-        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();
+        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, NO_OFFSET};
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
index b27f16a..fc9a11b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -15,16 +15,7 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig;
 
 public class OverviewPeekState extends OverviewState {
     private static final float OVERVIEW_OFFSET = 0.7f;
@@ -37,14 +28,4 @@
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
         return new float[] {NO_SCALE, OVERVIEW_OFFSET};
     }
-
-    @Override
-    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
-            StateAnimationConfig config) {
-        if (this == OVERVIEW_PEEK && fromState == NORMAL) {
-            config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
-            config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
-        }
-    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index fad9ea5..9f31608 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -15,40 +15,21 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import static android.view.View.VISIBLE;
-
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-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.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Interpolator;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.SysUINavigationMode;
@@ -61,13 +42,11 @@
  */
 public class OverviewState extends LauncherState {
 
-    // Scale recents takes before animating in
-    private static final float RECENTS_PREPARE_SCALE = 1.33f;
-
     protected static final Rect sTempRect = new Rect();
 
     private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
-            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE
+            | FLAG_CLOSE_POPUPS;
 
     public OverviewState(int id) {
         this(id, STATE_FLAGS);
@@ -82,9 +61,9 @@
     }
 
     @Override
-    public int getTransitionDuration(Launcher launcher) {
+    public int getTransitionDuration(Context context) {
         // In no-button mode, overview comes in all the way from the left, so give it more time.
-        boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(launcher).getMode() == NO_BUTTON;
+        boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(context).getMode() == NO_BUTTON;
         return isNoButtonMode && ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250;
     }
 
@@ -137,20 +116,6 @@
     }
 
     @Override
-    public void onStateEnabled(Launcher launcher) {
-        AbstractFloatingView.closeAllOpenViews(launcher);
-    }
-
-    @Override
-    public void onStateTransitionEnd(Launcher launcher) {
-        DiscoveryBounce.showForOverviewIfNeeded(launcher);
-        RecentsView recentsView = launcher.getOverviewPanel();
-        AccessibilityManagerCompat.sendCustomAccessibilityEvent(
-                recentsView.getPageAt(recentsView.getCurrentPage()),
-                AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
-    }
-
-    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
@@ -219,35 +184,6 @@
         }
     }
 
-    @Override
-    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
-            StateAnimationConfig config) {
-        if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) {
-            if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) {
-                config.setInterpolator(ANIM_WORKSPACE_SCALE,
-                        fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
-                config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
-            } else {
-                config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
-
-                // Scale up the recents, if it is not coming from the side
-                RecentsView overview = launcher.getOverviewPanel();
-                if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
-                    SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
-                }
-            }
-            config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
-            config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
-            Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
-                    && removeShelfFromOverview(launcher)
-                    ? OVERSHOOT_1_2
-                    : OVERSHOOT_1_7;
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
-            config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
-        }
-    }
-
     public static OverviewState newBackgroundState(int id) {
         return new BackgroundAppState(id);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 7b4bb02..2c7373e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -15,24 +15,16 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import android.os.Handler;
-import android.os.Looper;
-
 import com.android.launcher3.Launcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.GestureState;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 
 /**
  * State to indicate we are about to launch a recent task. Note that this state is only used when
  * quick switching from launcher; quick switching from an app uses LauncherSwipeHandler.
- * @see GestureState.GestureEndTarget#NEW_TASK
+ * @see com.android.quickstep.GestureState.GestureEndTarget#NEW_TASK
  */
 public class QuickSwitchState extends BackgroundAppState {
 
-    private static final String TAG = "QuickSwitchState";
-
     public QuickSwitchState(int id) {
         super(id, LauncherLogProto.ContainerType.APP);
     }
@@ -49,21 +41,4 @@
     public int getVisibleElements(Launcher launcher) {
         return NONE;
     }
-
-    @Override
-    public void onStateTransitionEnd(Launcher launcher) {
-        TaskView tasktolaunch = launcher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
-        if (tasktolaunch != null) {
-            tasktolaunch.launchTask(false, success -> {
-                if (!success) {
-                    launcher.getStateManager().goToState(OVERVIEW);
-                    tasktolaunch.notifyTaskLaunchFailed(TAG);
-                } else {
-                    launcher.getStateManager().moveToRestState();
-                }
-            }, new Handler(Looper.getMainLooper()));
-        } else {
-            launcher.getStateManager().goToState(NORMAL);
-        }
-    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
new file mode 100644
index 0000000..11593a1
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -0,0 +1,236 @@
+/*
+ * 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.launcher3.uioverrides.states;
+
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.HINT_STATE;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Animation factory for quickstep specific transitions
+ */
+public class QuickstepAtomicAnimationFactory extends AtomicAnimationFactory<LauncherState> {
+
+    // Scale recents takes before animating in
+    private static final float RECENTS_PREPARE_SCALE = 1.33f;
+
+    public static final int INDEX_SHELF_ANIM = 0;
+    public static final int INDEX_RECENTS_FADE_ANIM = 1;
+    public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
+    public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = 3;
+    private static final int ANIM_COUNT = 4;
+
+    public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
+
+    private final QuickstepLauncher mLauncher;
+
+    public QuickstepAtomicAnimationFactory(QuickstepLauncher launcher) {
+        super(ANIM_COUNT);
+        mLauncher = launcher;
+    }
+
+    @Override
+    public Animator createStateElementAnimation(int index, float... values) {
+        switch (index) {
+            case INDEX_SHELF_ANIM: {
+                AllAppsTransitionController aatc = mLauncher.getAllAppsController();
+                Animator springAnim = aatc.createSpringAnimation(values);
+
+                if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+                    // Translate hotseat with the shelf until reaching overview.
+                    float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
+                    ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mLauncher);
+                    float shiftRange = aatc.getShiftRange();
+                    if (values.length == 1) {
+                        values = new float[] {aatc.getProgress(), values[0]};
+                    }
+                    ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
+                    hotseatAnim.addUpdateListener(anim -> {
+                        float progress = (Float) anim.getAnimatedValue();
+                        if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
+                            float hotseatShift = (progress - overviewProgress) * shiftRange;
+                            mLauncher.getHotseat().setTranslationY(hotseatShift + sat.translationY);
+                        }
+                    });
+                    hotseatAnim.setInterpolator(LINEAR);
+                    hotseatAnim.setDuration(springAnim.getDuration());
+
+                    AnimatorSet anim = new AnimatorSet();
+                    anim.play(hotseatAnim);
+                    anim.play(springAnim);
+                    return anim;
+                }
+
+                return springAnim;
+            }
+            case INDEX_RECENTS_FADE_ANIM:
+                return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
+                        RecentsView.CONTENT_ALPHA, values);
+            case INDEX_RECENTS_TRANSLATE_X_ANIM: {
+                RecentsView rv = mLauncher.getOverviewPanel();
+                return new SpringAnimationBuilder(mLauncher)
+                        .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
+                        .setDampingRatio(0.8f)
+                        .setStiffness(250)
+                        .setValues(values)
+                        .build(rv, ADJACENT_PAGE_OFFSET);
+            }
+            case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
+                StateAnimationConfig config = new StateAnimationConfig();
+                config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
+
+                config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+                config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
+                if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+                    config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
+                    config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
+                }
+
+                StateManager<LauncherState> stateManager = mLauncher.getStateManager();
+                return stateManager.createAtomicAnimation(
+                        stateManager.getCurrentStableState(), OVERVIEW, config);
+            }
+
+            default:
+                return super.createStateElementAnimation(index, values);
+        }
+    }
+
+    @Override
+    public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
+            StateAnimationConfig config) {
+        if (toState == NORMAL && fromState == OVERVIEW) {
+            config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+            config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+            config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
+            config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+            Workspace workspace = mLauncher.getWorkspace();
+
+            // Start from a higher workspace scale, but only if we're invisible so we don't jump.
+            boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
+            if (isWorkspaceVisible) {
+                CellLayout currentChild = (CellLayout) workspace.getChildAt(
+                        workspace.getCurrentPage());
+                isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
+                        && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
+            }
+            if (!isWorkspaceVisible) {
+                workspace.setScaleX(0.92f);
+                workspace.setScaleY(0.92f);
+            }
+            Hotseat hotseat = mLauncher.getHotseat();
+            boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
+            if (!isHotseatVisible) {
+                hotseat.setScaleX(0.92f);
+                hotseat.setScaleY(0.92f);
+                if (ENABLE_OVERVIEW_ACTIONS.get()) {
+                    AllAppsContainerView qsbContainer = mLauncher.getAppsView();
+                    View qsb = qsbContainer.getSearchView();
+                    boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
+                    if (!qsbVisible) {
+                        qsbContainer.setScaleX(0.92f);
+                        qsbContainer.setScaleY(0.92f);
+                    }
+                }
+            }
+        } else if (toState == NORMAL && fromState == OVERVIEW_PEEK) {
+            // Keep fully visible until the very end (when overview is offscreen) to make invisible.
+            config.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
+        } else if (toState == OVERVIEW_PEEK && fromState == NORMAL) {
+            config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+            config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
+        } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
+            if (SysUINavigationMode.getMode(mLauncher) == NO_BUTTON) {
+                config.setInterpolator(ANIM_WORKSPACE_SCALE,
+                        fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
+                config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+            } else {
+                config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
+
+                // Scale up the recents, if it is not coming from the side
+                RecentsView overview = mLauncher.getOverviewPanel();
+                if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
+                    SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+                }
+            }
+            config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+            Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
+                    && removeShelfFromOverview(mLauncher)
+                    ? OVERSHOOT_1_2
+                    : OVERSHOOT_1_7;
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
+            config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 8af2747..fac478e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.uioverrides.touchcontrollers;
 
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -31,23 +30,26 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppTransitionManagerImpl;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.SystemUiProxy;
@@ -82,7 +84,7 @@
 
     @Override
     protected long getAtomicDuration() {
-        return LauncherAppTransitionManagerImpl.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
+        return QuickstepAtomicAnimationFactory.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
     }
 
     @Override
@@ -178,6 +180,9 @@
 
     @Override
     public boolean onDrag(float displacement, MotionEvent event) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "FlingAndHoldTouchController");
+        }
         float upDisplacement = -displacement;
         mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
                 || upDisplacement < mMotionPauseMinDisplacement
@@ -206,8 +211,8 @@
             mPeekAnim.cancel();
         }
 
-        Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
-                INDEX_PAUSE_TO_OVERVIEW_ANIM);
+        Animator overviewAnim = mLauncher.createAtomicAnimationFactory()
+                .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM);
         mAtomicAnim = new AnimatorSet();
         mAtomicAnim.addListener(new AnimationSuccessListener() {
             @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 06a481b..bf0690c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -25,6 +25,7 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
 import android.animation.ValueAnimator;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
@@ -41,6 +42,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -94,18 +96,37 @@
     }
 
     private boolean canInterceptTouch(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NavBarToHomeTouchController.canInterceptTouch "
+                    + ev);
+        }
         boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
         if (!cameFromNavBar) {
             return false;
         }
         if (mStartState.overviewUi || mStartState == ALL_APPS) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED,
+                        "NavBarToHomeTouchController.canInterceptTouch true 1 "
+                                + mStartState.overviewUi + " " + (mStartState == ALL_APPS));
+            }
             return true;
         }
         if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED,
+                        "NavBarToHomeTouchController.canInterceptTouch true 2 "
+                                + AbstractFloatingView.getTopOpenView(mLauncher).getClass()
+                                .getSimpleName());
+            }
             return true;
         }
         if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
                 && AssistantUtilities.isExcludedAssistantRunning()) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED,
+                        "NavBarToHomeTouchController.canInterceptTouch true 3");
+            }
             return true;
         }
         return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 381ecf1..966e25b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -27,14 +27,16 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.graphics.PointF;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -63,6 +65,9 @@
     public NoButtonNavbarToOverviewTouchController(Launcher l) {
         super(l);
         mRecentsView = l.getOverviewPanel();
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController.ctor");
+        }
     }
 
     @Override
@@ -146,6 +151,9 @@
 
     @Override
     public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController");
+        }
         if (mMotionPauseDetector.isPaused()) {
             if (!mReachedOverview) {
                 mStartDisplacement.set(xDisplacement, yDisplacement);
@@ -165,7 +173,7 @@
     protected void goToOverviewOnDragEnd(float velocity) {
         float velocityDp = dpiFromPx(velocity);
         boolean isFling = Math.abs(velocityDp) > 1;
-        LauncherStateManager stateManager = mLauncher.getStateManager();
+        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
         boolean goToHomeInsteadOfOverview = isFling;
         if (goToHomeInsteadOfOverview) {
             if (velocity > 0) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index f4f8bc9..7385658 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -35,6 +34,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
+import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL;
@@ -312,8 +312,8 @@
         if (mMotionPauseDetector.isPaused() && noFling) {
             cancelAnimations();
 
-            Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
-                    INDEX_PAUSE_TO_OVERVIEW_ANIM);
+            Animator overviewAnim = mLauncher.createAtomicAnimationFactory()
+                    .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM);
             overviewAnim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 9e53959..c643858 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -35,8 +35,6 @@
 
 import android.view.MotionEvent;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
@@ -57,7 +55,7 @@
  */
 public class QuickSwitchTouchController extends AbstractStateChangeTouchController {
 
-    private @Nullable TaskView mTaskToLaunch;
+    protected final RecentsView mOverviewPanel;
 
     public QuickSwitchTouchController(Launcher launcher) {
         this(launcher, SingleAxisSwipeDetector.HORIZONTAL);
@@ -65,6 +63,7 @@
 
     protected QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
         super(l, dir);
+        mOverviewPanel = l.getOverviewPanel();
     }
 
     @Override
@@ -94,7 +93,6 @@
     public void onDragStart(boolean start, float startDisplacement) {
         super.onDragStart(start, startDisplacement);
         mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
-        mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
         ActivityManagerWrapper.getInstance()
                 .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
     }
@@ -102,7 +100,6 @@
     @Override
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
-        mTaskToLaunch = null;
     }
 
     @Override
@@ -141,13 +138,15 @@
     }
 
     private void updateFullscreenProgress(float progress) {
-        if (mTaskToLaunch != null) {
-            mTaskToLaunch.setFullscreenProgress(progress);
-            int sysuiFlags = progress > UPDATE_SYSUI_FLAGS_THRESHOLD
-                    ? mTaskToLaunch.getThumbnail().getSysUiStatusNavFlags()
-                    : 0;
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
+        mOverviewPanel.setFullscreenProgress(progress);
+        int sysuiFlags = 0;
+        if (progress > UPDATE_SYSUI_FLAGS_THRESHOLD) {
+            TaskView tv = mOverviewPanel.getTaskViewAt(0);
+            if (tv != null) {
+                sysuiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
+            }
         }
+        mLauncher.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index e182c59..1dd5fb7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -36,8 +35,8 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.quickstep.util.AppWindowAnimationHelper;
-import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -75,14 +74,10 @@
     boolean onActivityReady(T activity, Boolean wasVisible) {
         activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
         AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
-        BaseActivityInterface.AnimationFactory factory =
-                mActivityInterface.prepareRecentsUI(wasVisible,
-                false /* animate activity */, (controller) -> {
+        BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
+                wasVisible, (controller) -> {
                     controller.dispatchOnStart();
-                    ValueAnimator anim = controller.getAnimationPlayer()
-                            .setDuration(RECENTS_LAUNCH_DURATION);
-                    anim.setInterpolator(FAST_OUT_SLOW_IN);
-                    anim.start();
+                    controller.getAnimationPlayer().end();
                 });
         factory.onRemoteAnimationReceived(null);
         factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
@@ -156,7 +151,7 @@
         mActivityInterface.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
                 targetRect);
         clipHelper.updateTargetRect(targetRect);
-        clipHelper.prepareAnimation(mActivity.getDeviceProfile(), false /* isOpening */);
+        clipHelper.prepareAnimation(mActivity.getDeviceProfile());
 
         TransformParams params = new TransformParams()
                 .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
@@ -164,9 +159,7 @@
         valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
         valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
         valueAnimator.addUpdateListener((v) -> {
-            params.setProgress((float) v.getAnimatedValue())
-                    .setTargetSet(targets)
-                    .setLauncherOnTop(true);
+            params.setProgress((float) v.getAnimatedValue()).setTargetSet(targets);
             clipHelper.applyTransform(params);
         });
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index d22e5af..76c6060 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -23,45 +25,49 @@
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 
 import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Point;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
-import android.util.Pair;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
 import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.AppWindowAnimationHelper;
-import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
-import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
+import com.android.quickstep.util.WindowSizeStrategy;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 import java.util.ArrayList;
 import java.util.function.Consumer;
@@ -94,7 +100,9 @@
     protected final BaseActivityInterface<T> mActivityInterface;
     protected final InputConsumerController mInputConsumer;
 
-    protected AppWindowAnimationHelper mAppWindowAnimationHelper;
+    protected final TaskViewSimulator mTaskViewSimulator;
+    private AnimatorPlaybackController mWindowTransitionController;
+
     protected final TransformParams mTransformParams = new TransformParams();
 
     // Shift in the range of [0, 1].
@@ -114,27 +122,25 @@
     protected T mActivity;
     protected Q mRecentsView;
     protected DeviceProfile mDp;
-    private final int mPageSpacing;
 
     protected Runnable mGestureEndCallback;
 
     protected MultiStateCallback mStateCallback;
 
     protected boolean mCanceled;
-    protected int mFinishingRecentsAnimationForNewTaskId = -1;
 
-    private RecentsOrientedState mOrientedState;
+    private boolean mRecentsViewScrollLinked = false;
 
     protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, InputConsumerController inputConsumer) {
+            GestureState gestureState, InputConsumerController inputConsumer,
+            WindowSizeStrategy windowSizeStrategy) {
         mContext = context;
         mDeviceState = deviceState;
         mGestureState = gestureState;
         mActivityInterface = gestureState.getActivityInterface();
         mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
         mInputConsumer = inputConsumer;
-        mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
-        mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+        mTaskViewSimulator = new TaskViewSimulator(context, windowSizeStrategy);
     }
 
     /**
@@ -194,13 +200,13 @@
                 updateFinalShift();
             }
         });
-        mRecentsView.setAppWindowAnimationHelper(mAppWindowAnimationHelper);
         runOnRecentsAnimationStart(() ->
                 mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                         mRecentsAnimationTargets));
+        mRecentsViewScrollLinked = true;
     }
 
-    protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
+    protected void startNewTask(Consumer<Boolean> resultCallback) {
         // Launch the task user scrolled to (mRecentsView.getNextPage()).
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             // We finish recents animation inside launchTask() when live tile is enabled.
@@ -211,18 +217,22 @@
             if (!mCanceled) {
                 TaskView nextTask = mRecentsView.getTaskView(taskId);
                 if (nextTask != null) {
+                    mGestureState.updateLastStartedTaskId(taskId);
                     nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
                             success -> {
                                 resultCallback.accept(success);
-                                if (!success) {
+                                if (success) {
+                                    if (mRecentsView.indexOfChild(nextTask)
+                                            == getLastAppearedTaskIndex()) {
+                                        onRestartLastAppearedTask();
+                                    }
+                                } else {
                                     mActivityInterface.onLaunchTaskFailed();
                                     nextTask.notifyTaskLaunchFailed(TAG);
-                                } else {
-                                    mActivityInterface.onLaunchTaskSuccess();
+                                    mRecentsAnimationController.finish(true /* toRecents */, null);
                                 }
                             }, MAIN_EXECUTOR.getHandler());
                 }
-                mStateCallback.setStateOnUiThread(successStateFlag);
             }
             mCanceled = false;
         }
@@ -230,6 +240,19 @@
     }
 
     /**
+     * Called when we successfully startNewTask() on the task that was previously running. Normally
+     * we call resumeLastTask() when returning to the previously running task, but this handles a
+     * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+     * start A again to ensure it stays on top.
+     */
+    @CallSuper
+    protected void onRestartLastAppearedTask() {
+        // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+        // appeared.
+        mRecentsAnimationController.finish(false, null);
+    }
+
+    /**
      * Runs the given {@param action} if the recents animation has already started, or queues it to
      * be run when it is next started.
      */
@@ -242,43 +265,36 @@
     }
 
     /**
+     * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
      * @return whether the recents animation has started and there are valid app targets.
      */
     protected boolean hasTargets() {
         return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
     }
 
-    protected void updateSource(Rect stackBounds, RemoteAnimationTargetCompat runningTarget) {
-        mAppWindowAnimationHelper.updateSource(stackBounds, runningTarget);
-    }
-
     @Override
     public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
             RecentsAnimationTargets targets) {
         mRecentsAnimationController = recentsAnimationController;
         mRecentsAnimationTargets = targets;
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
-        final Rect overviewStackBounds;
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
                 mGestureState.getRunningTaskId());
 
         if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
-            overviewStackBounds = mActivityInterface
+            Rect overviewStackBounds = mActivityInterface
                     .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
-            dp = dp.getMultiWindowProfile(mContext, new Point(
-                    overviewStackBounds.width(), overviewStackBounds.height()));
+            dp = dp.getMultiWindowProfile(mContext, overviewStackBounds);
         } else {
             // If we are not in multi-window mode, home insets should be same as system insets.
             dp = dp.copy(mContext);
-            overviewStackBounds = getStackBounds(dp);
         }
         dp.updateInsets(targets.homeContentInsets);
         dp.updateIsSeascape(mContext);
         if (runningTaskTarget != null) {
-            updateSource(overviewStackBounds, runningTaskTarget);
+            mTaskViewSimulator.setPreview(runningTaskTarget);
         }
 
-        mAppWindowAnimationHelper.prepareAnimation(dp, false /* isOpening */);
         initTransitionEndpoints(dp);
 
         // Notify when the animation starts
@@ -308,54 +324,73 @@
         }
     }
 
-    private Rect getStackBounds(DeviceProfile dp) {
-        if (mActivity != null) {
-            int loc[] = new int[2];
-            View rootView = mActivity.getRootView();
-            rootView.getLocationOnScreen(loc);
-            return new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
-                    loc[1] + rootView.getHeight());
-        } else {
-            return new Rect(0, 0, dp.widthPx, dp.heightPx);
+    @Override
+    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        if (mRecentsAnimationController != null) {
+            if (handleTaskAppeared(appearedTaskTarget)) {
+                mRecentsAnimationController.finish(false /* toRecents */,
+                        null /* onFinishComplete */);
+                mActivityInterface.onLaunchTaskSuccess();
+            }
         }
     }
 
+    /** @return Whether this was the task we were waiting to appear, and thus handled it. */
+    protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
+
+    /**
+     * @return The index of the TaskView in RecentsView whose taskId matches the task that will
+     * resume if we finish the controller.
+     */
+    protected int getLastAppearedTaskIndex() {
+        return mGestureState.getLastAppearedTaskId() != -1
+                ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
+                : mRecentsView.getRunningTaskIndex();
+    }
+
+    /**
+     * @return Whether we are continuing a gesture that already landed on a new task,
+     * but before that task appeared.
+     */
+    protected boolean hasStartedNewTask() {
+        return mGestureState.getLastStartedTaskId() != -1;
+    }
+
     protected void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
 
         mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
                 dp, mContext, TEMP_RECT);
+        mTaskViewSimulator.setDp(dp);
+        mTaskViewSimulator.setLayoutRotation(
+                mDeviceState.getCurrentActiveRotation(),
+                mDeviceState.getDisplayRotation());
 
-        if (!dp.isMultiWindowMode) {
-            // When updating the target rect, also update the home bounds since the location on
-            // screen of the launcher window may be stale (position is not updated until first
-            // traversal after the window is resized).  We only do this for non-multiwindow because
-            // we otherwise use the minimized home bounds provided by the system.
-            mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
-        }
-        int displayRotation = 0;
-        if (mOrientedState != null && mOrientedState.isMultipleOrientationSupportedByDevice()) {
-            // TODO(b/150300347): The first recents animation after launcher is started with the
-            //  foreground app not in landscape will look funky until that bug is fixed
-            displayRotation = mOrientedState.getDisplayRotation();
-
-            RectF tempRectF = new RectF(TEMP_RECT);
-            mOrientedState.mapRectFromRotation(displayRotation,
-                    tempRectF, dp.widthPx, dp.heightPx);
-            tempRectF.roundOut(TEMP_RECT);
-        }
-        mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
         if (mDeviceState.isFullyGesturalNavMode()) {
             // We can drag all the way to the top of the screen.
-            // TODO(b/149609070): Landscape apps are currently limited in
-            //   their ability to scale past the target rect.
-            float dragFactor = (float) dp.heightPx / mTransitionDragLength;
-            mDragLengthFactor = displayRotation == 0 ? dragFactor : Math.min(1.0f, dragFactor);
-            Pair<Float, Float> dragFactorStartAndMaxProgress =
-                    mActivityInterface.getSwipeUpPullbackStartAndMaxProgress();
-            mDragLengthFactorStartPullback = dragFactorStartAndMaxProgress.first;
-            mDragLengthFactorMaxPullback = dragFactorStartAndMaxProgress.second;
+            mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
+
+            float startScale = mTaskViewSimulator.getFullScreenScale();
+            // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
+            mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale);
+            mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale);
+        } else {
+            mDragLengthFactor = 1;
+            mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
         }
+
+        AnimatorSet anim = new AnimatorSet();
+        anim.setDuration(mTransitionDragLength * 2);
+        anim.setInterpolator(t -> t * mDragLengthFactor);
+        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
+                AnimatedFloat.VALUE,
+                mTaskViewSimulator.getFullScreenScale(), 1));
+        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
+                AnimatedFloat.VALUE,
+                BACKGROUND_APP.getOverviewFullscreenProgress(),
+                OVERVIEW.getOverviewFullscreenProgress()));
+        mWindowTransitionController =
+                AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
     }
 
     /**
@@ -366,9 +401,6 @@
     protected boolean onActivityInit(Boolean alreadyOnHome) {
         T createdActivity = mActivityInterface.getCreatedActivity();
         if (createdActivity != null) {
-            mOrientedState = ((RecentsView) createdActivity.getOverviewPanel())
-                .getPagedViewOrientedState();
-            mAppWindowAnimationHelper = new AppWindowAnimationHelper(mOrientedState, mContext);
             initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
                 .getDeviceProfile(mContext));
         }
@@ -417,35 +449,23 @@
     }
 
     /**
-     * Applies the transform on the recents animation without any additional null checks
+     * Applies the transform on the recents animation
      */
-    protected void applyTransformUnchecked() {
-        float shift = mCurrentShift.value;
-        float offset = mRecentsView == null ? 0 : mRecentsView.getScrollOffsetScaled();
-        float taskSize = getOrientationHandler()
-            .getPrimarySize(mAppWindowAnimationHelper.getTargetRect());
-        float offsetScale = getTaskCurveScaleForOffset(offset, taskSize);
-        mTransformParams
-                .setProgress(shift)
-                .setOffset(offset)
-                .setOffsetScale(offsetScale)
-                .setTargetSet(mRecentsAnimationTargets)
-                .setLauncherOnTop(true);
-        mAppWindowAnimationHelper.applyTransform(mTransformParams);
-    }
+    protected void applyWindowTransform() {
+        if (mWindowTransitionController != null) {
+            float progress = mCurrentShift.value / mDragLengthFactor;
+            mWindowTransitionController.setPlayFraction(progress);
+            mTransformParams.setTargetSet(mRecentsAnimationTargets);
 
-    private float getTaskCurveScaleForOffset(float offset, float taskSize) {
-        int dpPixel = getOrientationHandler().getShortEdgeLength(mDp);
-        float distanceToReachEdge = dpPixel / 2 + taskSize / 2 + mPageSpacing;
-        float interpolation = Math.min(1, offset / distanceToReachEdge);
-        return TaskView.getCurveScaleForInterpolation(interpolation);
+            if (mRecentsViewScrollLinked) {
+                mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+            }
+            mTaskViewSimulator.apply(mTransformParams);
+        }
     }
 
     protected PagedOrientationHandler getOrientationHandler() {
-        if (mOrientedState == null) {
-            return PagedOrientationHandler.PORTRAIT;
-        }
-        return mOrientedState.getOrientationHandler();
+        return mTaskViewSimulator.getOrientationState().getOrientationHandler();
     }
 
     /**
@@ -456,104 +476,40 @@
     protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
             HomeAnimationFactory homeAnimationFactory) {
         final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
-        final View floatingView = homeAnimationFactory.getFloatingView();
-        final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
-        final RectF startRect = new RectF(
-            mAppWindowAnimationHelper.applyTransform(
-                mTransformParams.setProgress(startProgress)
-                    .setTargetSet(mRecentsAnimationTargets)
-                    .setLauncherOnTop(false)));
-        if (isFloatingIconView) {
-            mOrientedState.mapInverseRectFromNormalOrientation(
-                    startRect, mDp.widthPx, mDp.heightPx);
-        }
+        final FloatingIconView fiv = homeAnimationFactory.mIconView;
+        final boolean isFloatingIconView = fiv != null;
+
+        mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
+        mTaskViewSimulator.apply(mTransformParams
+                .setProgress(startProgress)
+                .setTargetSet(mRecentsAnimationTargets));
+        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+
+        // Matrix to map a rect in Launcher space to window space
+        Matrix homeToWindowPositionMap = new Matrix();
+        mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
+
+        final RectF startRect = new RectF(cropRectF);
+        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
+        // Move the startRect to Launcher space as floatingIconView runs in Launcher
+        Matrix windowToHomePositionMap = new Matrix();
+        homeToWindowPositionMap.invert(windowToHomePositionMap);
+        windowToHomePositionMap.mapRect(startRect);
+
         RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
         if (isFloatingIconView) {
-            FloatingIconView fiv = (FloatingIconView) floatingView;
             anim.addAnimatorListener(fiv);
             fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
             fiv.setFastFinishRunnable(anim::end);
         }
 
-        AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
-
-        // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
-        // rounding at the end of the animation.
-        float startRadius = mAppWindowAnimationHelper.getCurrentCornerRadius();
-        float endRadius = startRect.width() / 6f;
-
-        float startTransformProgress = mTransformParams.getProgress();
-        float endTransformProgress = 1;
-
-        // We want the window alpha to be 0 once this threshold is met, so that the
-        // FolderIconView can be seen morphing into the icon shape.
-        final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
-        final RectF rotatedRect = new RectF();
-        anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
-
-            @Override
-            public void onUpdate(RectF currentRect, float progress) {
-                homeAnim.setPlayFraction(progress);
-
-                rotatedRect.set(currentRect);
-                if (isFloatingIconView) {
-                    mOrientedState.mapRectFromNormalOrientation(
-                            rotatedRect, mDp.widthPx, mDp.heightPx);
-                    mTransformParams.setCornerRadius(endRadius * progress + startRadius
-                        * (1f - progress));
-                }
-                mTransformParams.setProgress(
-                    Utilities.mapRange(progress, startTransformProgress, endTransformProgress))
-                    .setCurrentRect(rotatedRect)
-                    .setTargetAlpha(getWindowAlpha(progress));
-                mAppWindowAnimationHelper.applyTransform(mTransformParams);
-
-                if (isFloatingIconView) {
-                    ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
-                            windowAlphaThreshold, mAppWindowAnimationHelper.getCurrentCornerRadius(),
-                            false);
-                }
-            }
-
-            @Override
-            public void onCancel() {
-                if (isFloatingIconView) {
-                    ((FloatingIconView) floatingView).fastFinish();
-                }
-            }
-        });
-        anim.addAnimatorListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                homeAnim.dispatchOnStart();
-            }
-
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                homeAnim.getAnimationPlayer().end();
-            }
-        });
+        SpringAnimationRunner runner = new SpringAnimationRunner(
+                homeAnimationFactory, cropRectF, homeToWindowPositionMap);
+        anim.addOnUpdateListener(runner);
+        anim.addAnimatorListener(runner);
         return anim;
     }
 
-    /**
-     * @param progress The progress of the animation to the home screen.
-     * @return The current alpha to set on the animating app window.
-     */
-    protected float getWindowAlpha(float progress) {
-        // Alpha interpolates between [1, 0] between progress values [start, end]
-        final float start = 0f;
-        final float end = 0.85f;
-
-        if (progress <= start) {
-            return 1f;
-        }
-        if (progress >= end) {
-            return 0f;
-        }
-        return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
-    }
-
     public interface Factory {
 
         BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
@@ -593,4 +549,135 @@
             };
         }
     }
+
+    /**
+     * @param progress The progress of the animation to the home screen.
+     * @return The current alpha to set on the animating app window.
+     */
+    protected float getWindowAlpha(float progress) {
+        // Alpha interpolates between [1, 0] between progress values [start, end]
+        final float start = 0f;
+        final float end = 0.85f;
+
+        if (progress <= start) {
+            return 1f;
+        }
+        if (progress >= end) {
+            return 0f;
+        }
+        return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+    }
+
+    protected abstract class HomeAnimationFactory {
+
+        private FloatingIconView mIconView;
+
+        public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
+            mIconView = iconView;
+        }
+
+        public @NonNull RectF getWindowTargetRect() {
+            PagedOrientationHandler orientationHandler = getOrientationHandler();
+            DeviceProfile dp = mDp;
+            final int halfIconSize = dp.iconSizePx / 2;
+            float primaryDimension = orientationHandler
+                    .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
+            float secondaryDimension = orientationHandler
+                    .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
+            final float targetX =  primaryDimension / 2f;
+            final float targetY = secondaryDimension - dp.hotseatBarSizePx;
+            // Fallback to animate to center of screen.
+            return new RectF(targetX - halfIconSize, targetY - halfIconSize,
+                    targetX + halfIconSize, targetY + halfIconSize);
+        }
+
+        public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
+
+        public void playAtomicAnimation(float velocity) {
+            // No-op
+        }
+    }
+
+    private class SpringAnimationRunner extends AnimationSuccessListener
+            implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
+
+        final Rect mCropRect = new Rect();
+        final Matrix mMatrix = new Matrix();
+
+        final RectF mWindowCurrentRect = new RectF();
+        final Matrix mHomeToWindowPositionMap;
+
+        final FloatingIconView mFIV;
+        final AnimatorPlaybackController mHomeAnim;
+        final RectF mCropRectF;
+
+        final float mStartRadius;
+        final float mEndRadius;
+        final float mWindowAlphaThreshold;
+
+        SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
+                Matrix homeToWindowPositionMap) {
+            mHomeAnim = factory.createActivityAnimationToHome();
+            mCropRectF = cropRectF;
+            mHomeToWindowPositionMap = homeToWindowPositionMap;
+
+            cropRectF.roundOut(mCropRect);
+            mFIV = factory.mIconView;
+
+            // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
+            // rounding at the end of the animation.
+            mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
+            mEndRadius = cropRectF.width() / 2f;
+
+            // We want the window alpha to be 0 once this threshold is met, so that the
+            // FolderIconView can be seen morphing into the icon shape.
+            mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
+        }
+
+        @Override
+        public void onUpdate(RectF currentRect, float progress) {
+            mHomeAnim.setPlayFraction(progress);
+            mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
+
+            mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
+            float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
+            mTransformParams
+                    .setTargetAlpha(getWindowAlpha(progress))
+                    .setCornerRadius(cornerRadius);
+
+            mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+            if (mFIV != null) {
+                mFIV.update(currentRect, 1f, progress,
+                        mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
+            }
+        }
+
+        @Override
+        public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
+                TransformParams params) {
+            if (app.mode == targetMode
+                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                builder.withMatrix(mMatrix)
+                        .withWindowCrop(mCropRect)
+                        .withCornerRadius(params.getCornerRadius());
+            }
+        }
+
+        @Override
+        public void onCancel() {
+            if (mFIV != null) {
+                mFIV.fastFinish();
+            }
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mHomeAnim.dispatchOnStart();
+        }
+
+        @Override
+        public void onAnimationSuccess(Animator animator) {
+            mHomeAnim.getAnimationPlayer().end();
+        }
+    }
 }
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 2214dd0..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,28 +15,26 @@
  */
 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.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
-import android.graphics.RectF;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.AnimationSuccessListener;
 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;
-import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
@@ -60,7 +58,7 @@
 
     @Override
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
-        LayoutUtils.calculateFallbackTaskSize(context, dp, outRect);
+        FALLBACK_RECENTS_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
         if (dp.isVerticalBarLayout()
                 && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
             Rect targetInsets = dp.getInsets();
@@ -89,54 +87,17 @@
         // set to zero prior to this class becoming active.
     }
 
-    @NonNull
     @Override
-    public HomeAnimationFactory prepareHomeUI() {
+    public AnimationFactory prepareRecentsUI(
+            boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
         RecentsActivity activity = getCreatedActivity();
-        RecentsView recentsView = activity.getOverviewPanel();
-
-        return new HomeAnimationFactory() {
-            @NonNull
-            @Override
-            public RectF getWindowTargetRect() {
-                float centerX = recentsView.getPivotX();
-                float centerY = recentsView.getPivotY();
-                return new RectF(centerX, centerY, centerX, centerY);
-            }
-
-            @NonNull
-            @Override
-            public AnimatorPlaybackController createActivityAnimationToHome() {
-                Animator anim = ObjectAnimator.ofFloat(recentsView, CONTENT_ALPHA, 0);
-                anim.addListener(new AnimationSuccessListener() {
-                    @Override
-                    public void onAnimationSuccess(Animator animator) {
-                        recentsView.startHome();
-                    }
-                });
-                AnimatorSet animatorSet = new AnimatorSet();
-                animatorSet.play(anim);
-                long accuracy = 2 * Math.max(recentsView.getWidth(), recentsView.getHeight());
-                return AnimatorPlaybackController.wrap(animatorSet, accuracy);
-            }
-        };
-    }
-
-    @Override
-    public AnimationFactory prepareRecentsUI(boolean activityVisible,
-            boolean animateActivity, 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);
-
-        boolean fromState = !animateActivity;
-        rv.setInOverviewState(fromState);
 
         return new AnimationFactory() {
 
@@ -154,27 +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(() -> {
-                    boolean endState = true;
-                    rv.setInOverviewState(controller.getInterpolatedProgress() > 0.5 ?
-                                    endState : fromState);
-                });
-
+                controller.setEndAction(() -> activity.getStateManager().goToState(
+                        controller.getInterpolatedProgress() > 0.5 ? DEFAULT : BACKGROUND_APP));
                 callback.accept(controller);
             }
         };
@@ -190,7 +143,7 @@
     @Nullable
     @Override
     public RecentsActivity getCreatedActivity() {
-        return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
+        return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
     }
 
     @Nullable
@@ -215,7 +168,7 @@
     }
 
     @Override
-    public boolean shouldMinimizeSplitScreen() {
+    public boolean allowMinimizeSplitScreen() {
         // TODO: Remove this once b/77875376 is fixed
         return false;
     }
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 1b2979b..77e50ca 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -24,6 +24,7 @@
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
 import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
+import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 
 import android.animation.Animator;
@@ -32,25 +33,24 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PointF;
-import android.graphics.RectF;
 import android.os.Bundle;
 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.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ObjectWrapper;
-import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 /**
  * Handles the navigation gestures when a 3rd party launcher is the default home activity.
@@ -105,10 +105,16 @@
     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) {
-        super(context, deviceState, gestureState, inputConsumer);
+        super(context, deviceState, gestureState, inputConsumer, FALLBACK_RECENTS_SIZE_STRATEGY);
 
         mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
         mContinuingLastGesture = continuingLastGesture;
@@ -118,9 +124,9 @@
         // Keep the home launcher invisible until we decide to land there.
         mLauncherAlpha.value = mRunningOverHome ? 1 : 0;
         if (mSwipeUpOverHome) {
-            mAppWindowAnimationHelper.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
+            mTransformParams.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
         } else {
-            mAppWindowAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
+            mTransformParams.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
         }
 
         // Going home has an extra long progress to ensure that it animates into the screen
@@ -156,7 +162,7 @@
 
     private void onLauncherAlphaChanged() {
         if (mRecentsAnimationTargets != null && mGestureState.getEndTarget() == null) {
-            applyTransformUnchecked();
+            applyWindowTransform();
         }
     }
 
@@ -167,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());
@@ -180,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;
     }
@@ -250,17 +291,19 @@
     public void updateFinalShift() {
         mTransformParams.setProgress(mCurrentShift.value);
         if (mRecentsAnimationController != null) {
-            mRecentsAnimationController.setWindowThresholdCrossed(!mInQuickSwitchMode
-                    && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
+            boolean swipeUpThresholdPassed = mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
+            mRecentsAnimationController.setUseLauncherSystemBarFlags(mInQuickSwitchMode
+                    || swipeUpThresholdPassed);
+            mRecentsAnimationController.setSplitScreenMinimized(!mInQuickSwitchMode
+                    && swipeUpThresholdPassed);
         }
 
         if (!mInQuickSwitchMode && !mDeviceState.isFullyGesturalNavMode()) {
             updateOverviewThresholdPassed(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW);
         }
 
-        if (mRecentsAnimationTargets != null) {
-            applyTransformUnchecked();
-        }
+        applyWindowTransform();
+        updateLauncherTransitionProgress();
     }
 
     @Override
@@ -317,15 +360,6 @@
             }
 
             if (mRecentsView != null) {
-                if (mFinishingRecentsAnimationForNewTaskId != -1) {
-                    TaskView newRunningTaskView = mRecentsView.getTaskView(
-                            mFinishingRecentsAnimationForNewTaskId);
-                    int newRunningTaskId = newRunningTaskView != null
-                            ? newRunningTaskView.getTask().key.id
-                            : -1;
-                    mRecentsView.setCurrentTask(newRunningTaskId);
-                    mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
-                }
                 mRecentsView.setOnScrollChangeListener(null);
             }
         } else {
@@ -398,7 +432,7 @@
                 break;
             }
             case NEW_TASK: {
-                startNewTask(STATE_HANDLER_INVALIDATED, b -> {});
+                startNewTask(success -> { });
                 break;
             }
         }
@@ -413,7 +447,7 @@
             if (mRecentsView == null || !hasTargets()) {
                 mGestureState.setEndTarget(LAST_TASK);
             } else {
-                final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+                final int runningTaskIndex = getLastAppearedTaskIndex();
                 final int taskToLaunch = mRecentsView.getNextPage();
                 mGestureState.setEndTarget(
                         (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
@@ -474,11 +508,7 @@
             RecentsAnimationTargets targets) {
         super.onRecentsAnimationStart(controller, targets);
         mRecentsAnimationController.enableInputConsumer();
-
-        if (mRunningOverHome) {
-            mAppWindowAnimationHelper.prepareAnimation(mDp, true);
-        }
-        applyTransformUnchecked();
+        applyWindowTransform();
 
         mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
     }
@@ -491,23 +521,17 @@
         super.onRecentsAnimationCanceled(thumbnailData);
     }
 
+    @Override
+    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        return true;
+    }
+
     /**
      * Creates an animation that transforms the current app window into the home app.
      * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
      */
     private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) {
-        HomeAnimationFactory factory = new HomeAnimationFactory() {
-            @Override
-            public RectF getWindowTargetRect() {
-                PagedOrientationHandler orientationHandler = mRecentsView != null
-                        ? mRecentsView.getPagedOrientationHandler()
-                        : (mDp.isLandscape
-                                ? PagedOrientationHandler.LANDSCAPE
-                                : PagedOrientationHandler.PORTRAIT);
-                return HomeAnimationFactory
-                    .getDefaultWindowTargetRect(orientationHandler, mDp);
-            }
-
+        HomeAnimationFactory factory = new HomeAnimationFactory(null) {
             @Override
             public AnimatorPlaybackController createActivityAnimationToHome() {
                 AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index b4764dc..9ff5d942 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -16,31 +16,26 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_FADE_ANIM;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
+import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
+import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
 import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 
 import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.UserHandle;
-import android.util.Pair;
+import android.util.Log;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
@@ -51,20 +46,19 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -77,12 +71,9 @@
  */
 public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
 
-    private Pair<Float, Float> mSwipeUpPullbackStartAndMaxProgress =
-            BaseActivityInterface.super.getSwipeUpPullbackStartAndMaxProgress();
-
     @Override
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
-        LayoutUtils.calculateLauncherTaskSize(context, dp, outRect);
+        LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
         if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
             Rect targetInsets = dp.getInsets();
             int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
@@ -93,11 +84,6 @@
     }
 
     @Override
-    public Pair<Float, Float> getSwipeUpPullbackStartAndMaxProgress() {
-        return mSwipeUpPullbackStartAndMaxProgress;
-    }
-
-    @Override
     public void onTransitionCancelled(boolean activityVisible) {
         Launcher launcher = getCreatedActivity();
         if (launcher == null) {
@@ -147,77 +133,19 @@
         launcher.onAssistantVisibilityChanged(visibility);
     }
 
-    @NonNull
     @Override
-    public HomeAnimationFactory prepareHomeUI() {
-        Launcher launcher = getCreatedActivity();
-        final DeviceProfile dp = launcher.getDeviceProfile();
-        final RecentsView recentsView = launcher.getOverviewPanel();
-        final TaskView runningTaskView = recentsView.getRunningTaskView();
-        final View workspaceView;
-        if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
-            workspaceView = launcher.getWorkspace().getFirstMatchForAppClose(
-                    runningTaskView.getTask().key.getComponent().getPackageName(),
-                    UserHandle.of(runningTaskView.getTask().key.userId));
-        } else {
-            workspaceView = null;
-        }
-        final RectF iconLocation = new RectF();
-        boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
-        FloatingIconView floatingIconView = canUseWorkspaceView
-                ? FloatingIconView.getFloatingIconView(launcher, workspaceView,
-                        true /* hideOriginal */, iconLocation, false /* isOpening */)
-                : null;
-        setLauncherHideBackArrow(true);
-        return new HomeAnimationFactory() {
-            @Nullable
-            @Override
-            public View getFloatingView() {
-                return floatingIconView;
-            }
-
-            @NonNull
-            @Override
-            public RectF getWindowTargetRect() {
-                if (canUseWorkspaceView) {
-                    return iconLocation;
-                } else {
-                    return HomeAnimationFactory
-                        .getDefaultWindowTargetRect(recentsView.getPagedOrientationHandler(), dp);
-                }
-            }
-
-            @NonNull
-            @Override
-            public AnimatorPlaybackController createActivityAnimationToHome() {
-                // Return an empty APC here since we have an non-user controlled animation to home.
-                long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
-                return launcher.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
-                        0 /* animComponents */);
-            }
-
-            @Override
-            public void playAtomicAnimation(float velocity) {
-                new StaggeredWorkspaceAnim(launcher, velocity, true /* animateOverviewScrim */)
-                        .start();
-            }
-        };
-    }
-
-    @Override
-    public AnimationFactory prepareRecentsUI(boolean activityVisible,
-            boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+    public AnimationFactory prepareRecentsUI(
+            boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
         BaseQuickstepLauncher launcher = getCreatedActivity();
         final LauncherState startState = launcher.getStateManager().getState();
 
         LauncherState resetState = startState;
-        if (startState.disableRestore) {
+        if (startState.shouldDisableRestore()) {
             resetState = launcher.getStateManager().getRestState();
         }
         launcher.getStateManager().setRestState(resetState);
 
-        final LauncherState fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
-        launcher.getStateManager().goToState(fromState, false);
+        launcher.getStateManager().goToState(BACKGROUND_APP, false);
         // Since all apps is not visible, we can safely reset the scroll position.
         // This ensures then the next swipe up to all-apps starts from scroll 0.
         launcher.getAppsView().reset(false /* animate */);
@@ -228,7 +156,7 @@
 
             @Override
             public void createActivityInterface(long transitionLength) {
-                createActivityInterfaceInternal(launcher, fromState, transitionLength, callback);
+                callback.accept(createBackgroundToOverviewAnim(launcher, transitionLength));
                 // Creating the activity controller animation sometimes reapplies the launcher state
                 // (because we set the animation as the current state animation), so we reapply the
                 // attached state here as well to ensure recents is shown/hidden appropriately.
@@ -282,74 +210,45 @@
         };
     }
 
-    private void createActivityInterfaceInternal(Launcher activity, LauncherState fromState,
-            long transitionLength, Consumer<AnimatorPlaybackController> callback) {
-        LauncherState endState = OVERVIEW;
-        if (fromState == endState) {
-            return;
-        }
+    private AnimatorPlaybackController createBackgroundToOverviewAnim(
+            Launcher activity, long transitionLength) {
 
-        AnimatorSet anim = new AnimatorSet();
+        PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+
         if (!activity.getDeviceProfile().isVerticalBarLayout()
                 && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
             // Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically.
-            anim.play(activity.getStateManager().createStateElementAnimation(
+            pa.add(activity.getStateManager().createStateElementAnimation(
                     INDEX_SHELF_ANIM,
-                    fromState.getVerticalProgress(activity),
-                    endState.getVerticalProgress(activity)));
+                    BACKGROUND_APP.getVerticalProgress(activity),
+                    OVERVIEW.getVerticalProgress(activity)));
         }
 
         // Animate the blur and wallpaper zoom
-        DepthController depthController = getDepthController();
-        float fromDepthRatio = fromState.getDepth(activity);
-        float toDepthRatio = endState.getDepth(activity);
-        Animator depthAnimator = ObjectAnimator.ofFloat(depthController,
-                new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
-                fromDepthRatio, toDepthRatio);
-        anim.play(depthAnimator);
+        float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
+        float toDepthRatio = OVERVIEW.getDepth(activity);
+        pa.addFloat(getDepthController(), new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+                fromDepthRatio, toDepthRatio, LINEAR);
 
-        playScaleDownAnim(anim, activity, fromState, endState);
 
-        anim.setDuration(transitionLength * 2);
-        anim.setInterpolator(LINEAR);
-        AnimatorPlaybackController controller =
-                AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+        //  Scale down recents from being full screen to being in overview.
+        RecentsView recentsView = activity.getOverviewPanel();
+        pa.addFloat(recentsView, SCALE_PROPERTY,
+                BACKGROUND_APP.getOverviewScaleAndOffset(activity)[0],
+                OVERVIEW.getOverviewScaleAndOffset(activity)[0],
+                LINEAR);
+        pa.addFloat(recentsView, FULLSCREEN_PROGRESS,
+                BACKGROUND_APP.getOverviewFullscreenProgress(),
+                OVERVIEW.getOverviewFullscreenProgress(),
+                LINEAR);
+
+        AnimatorPlaybackController controller = pa.createPlaybackController();
         activity.getStateManager().setCurrentUserControlledAnimation(controller);
 
         // Since we are changing the start position of the UI, reapply the state, at the end
-        controller.setEndAction(() -> {
-            activity.getStateManager().goToState(
-                    controller.getInterpolatedProgress() > 0.5 ? endState : fromState, false);
-        });
-        callback.accept(controller);
-    }
-
-    /**
-     * Scale down recents from the center task being full screen to being in overview.
-     */
-    private void playScaleDownAnim(AnimatorSet anim, Launcher launcher, LauncherState fromState,
-            LauncherState endState) {
-        RecentsView recentsView = launcher.getOverviewPanel();
-        if (recentsView.getCurrentPageTaskView() == null) {
-            return;
-        }
-
-        float fromFullscreenProgress = fromState.getOverviewFullscreenProgress();
-        float endFullscreenProgress = endState.getOverviewFullscreenProgress();
-
-        float fromScale = fromState.getOverviewScaleAndOffset(launcher)[0];
-        float endScale = endState.getOverviewScaleAndOffset(launcher)[0];
-
-        Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, endScale);
-        Animator applyFullscreenProgress = ObjectAnimator.ofFloat(recentsView,
-                RecentsView.FULLSCREEN_PROGRESS, fromFullscreenProgress, endFullscreenProgress);
-        anim.playTogether(scale, applyFullscreenProgress);
-
-        // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
-        float pullbackStartProgress = (0.75f - fromScale) / (endScale - fromScale);
-        float pullbackMaxProgress = (0.5f - fromScale) / (endScale - fromScale);
-        mSwipeUpPullbackStartAndMaxProgress = new Pair<>(
-                pullbackStartProgress, pullbackMaxProgress);
+        controller.setEndAction(() -> activity.getStateManager().goToState(
+                controller.getInterpolatedProgress() > 0.5 ? OVERVIEW : BACKGROUND_APP, false));
+        return controller;
     }
 
     @Override
@@ -382,6 +281,9 @@
 
     @Override
     public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "switchToRecentsIfVisible");
+        }
         Launcher launcher = getVisibleLauncher();
         if (launcher == null) {
             return false;
@@ -407,7 +309,7 @@
     }
 
     @Override
-    public boolean shouldMinimizeSplitScreen() {
+    public boolean allowMinimizeSplitScreen() {
         return true;
     }
 
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 52b40a9..e7fe142 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -17,8 +17,7 @@
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -32,26 +31,25 @@
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
-import static com.android.quickstep.GestureState.STATE_TASK_APPEARED_DURING_SWITCH;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
+import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.view.View;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.ViewTreeObserver.OnDrawListener;
@@ -64,6 +62,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
@@ -74,17 +73,16 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory;
-import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
 import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
-import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.TransformParams.TargetAlphaProvider;
 import com.android.quickstep.views.LiveTileOverlay;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -97,8 +95,8 @@
  * Handles the navigation gestures when Launcher is the default home activity.
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class LauncherSwipeHandler<T extends BaseDraggingActivity>
-        extends BaseSwipeUpHandler<T, RecentsView> implements OnApplyWindowInsetsListener {
+public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsView>
+        implements OnApplyWindowInsetsListener {
     private static final String TAG = LauncherSwipeHandler.class.getSimpleName();
 
     private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
@@ -152,7 +150,6 @@
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
 
     public static final long MAX_SWIPE_DURATION = 350;
-    public static final long MIN_SWIPE_DURATION = 80;
     public static final long MIN_OVERSHOOT_DURATION = 120;
 
     public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
@@ -181,9 +178,6 @@
     private AnimatorPlaybackController mLauncherTransitionController;
     private boolean mHasLauncherTransitionControllerStarted;
 
-    private final TaskViewSimulator mTaskViewSimulator;
-    private AnimatorPlaybackController mWindowTransitionController;
-
     private AnimationFactory mAnimationFactory = (t) -> { };
 
     private boolean mWasLauncherAlreadyVisible;
@@ -204,12 +198,10 @@
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
             InputConsumerController inputConsumer) {
-        super(context, deviceState, gestureState, inputConsumer);
+        super(context, deviceState, gestureState, inputConsumer, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
         mTaskAnimationManager = taskAnimationManager;
         mTouchTimeMs = touchTimeMs;
         mContinuingLastGesture = continuingLastGesture;
-        mTaskViewSimulator = new TaskViewSimulator(
-                context, LayoutUtils::calculateLauncherTaskSize, true);
 
         initAfterSubclassConstructor();
         initStateCallbacks();
@@ -265,8 +257,6 @@
                         | STATE_RECENTS_SCROLLING_FINISHED,
                 this::onSettledOnEndTarget);
 
-        mGestureState.runOnceAtState(STATE_TASK_APPEARED_DURING_SWITCH, this::onTaskAppeared);
-
         mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
         mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                 this::invalidateHandlerWithLauncher);
@@ -283,7 +273,7 @@
     @Override
     protected boolean onActivityInit(Boolean alreadyOnHome) {
         super.onActivityInit(alreadyOnHome);
-        final T activity = mActivityInterface.getCreatedActivity();
+        final Launcher activity = mActivityInterface.getCreatedActivity();
         if (mActivity == activity) {
             return true;
         }
@@ -305,7 +295,6 @@
 
         mRecentsView = activity.getOverviewPanel();
         mRecentsView.setOnPageTransitionEndCallback(null);
-        linkRecentsViewScroll();
         addLiveTileOverlay();
 
         mStateCallback.setState(STATE_LAUNCHER_PRESENT);
@@ -322,6 +311,8 @@
             // so we need to kick off switching to the overview predictions as soon as possible
             mActivityInterface.updateOverviewPredictionState();
         }
+        linkRecentsViewScroll();
+
         return true;
     }
 
@@ -331,7 +322,7 @@
     }
 
     private void onLauncherStart() {
-        final T activity = mActivityInterface.getCreatedActivity();
+        final Launcher activity = mActivityInterface.getCreatedActivity();
         if (mActivity != activity) {
             return;
         }
@@ -344,8 +335,7 @@
         if (mGestureState.getEndTarget() != HOME) {
             Runnable initAnimFactory = () -> {
                 mAnimationFactory = mActivityInterface.prepareRecentsUI(
-                        mWasLauncherAlreadyVisible, true,
-                        this::onAnimatorPlaybackControllerCreated);
+                        mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
                 maybeUpdateRecentsAttachedState(false /* animate */);
             };
             if (mWasLauncherAlreadyVisible) {
@@ -522,34 +512,6 @@
         mAnimationFactory.createActivityInterface(mTransitionDragLength);
     }
 
-    @Override
-    protected void updateSource(Rect stackBounds, RemoteAnimationTargetCompat runningTarget) {
-        super.updateSource(stackBounds, runningTarget);
-        mTaskViewSimulator.setPreview(runningTarget, mRecentsAnimationTargets);
-    }
-
-    @Override
-    protected void initTransitionEndpoints(DeviceProfile dp) {
-        super.initTransitionEndpoints(dp);
-        mTaskViewSimulator.setDp(dp, false /* isOpening */);
-        mTaskViewSimulator.setLayoutRotation(
-                mDeviceState.getCurrentActiveRotation(),
-                mDeviceState.getDisplayRotation());
-
-        AnimatorSet anim = new AnimatorSet();
-        anim.setDuration(mTransitionDragLength * 2);
-        anim.setInterpolator(t -> t * mDragLengthFactor);
-        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
-                AnimatedFloat.VALUE,
-                mTaskViewSimulator.getFullScreenScale(), 1));
-        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
-                AnimatedFloat.VALUE,
-                BACKGROUND_APP.getOverviewFullscreenProgress(),
-                OVERVIEW.getOverviewFullscreenProgress()));
-        mWindowTransitionController =
-                AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
-    }
-
     /**
      * 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.
@@ -580,18 +542,11 @@
 
     @Override
     public void updateFinalShift() {
-        if (mRecentsAnimationTargets != null) {
-            // Base class expects applyTransformUnchecked to be called here.
-            // TODO: Remove this dependency for swipe-up animation.
-            // applyTransformUnchecked();
-            updateSysUiFlags(mCurrentShift.value);
-        }
-
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mRecentsAnimationTargets != null) {
                 LiveTileOverlay.INSTANCE.update(
-                        mAppWindowAnimationHelper.getCurrentRectWithInsets(),
-                        mAppWindowAnimationHelper.getCurrentCornerRadius());
+                        mTaskViewSimulator.getCurrentCropRect(),
+                        mTaskViewSimulator.getCurrentCornerRadius());
             }
         }
 
@@ -603,16 +558,8 @@
             }
         }
 
-        if (mWindowTransitionController != null) {
-            float progress = mCurrentShift.value / mDragLengthFactor;
-            mWindowTransitionController.setPlayFraction(progress);
-            mTransformParams
-                    .setTargetSet(mRecentsAnimationTargets)
-                    .setLauncherOnTop(true);
-
-            mTaskViewSimulator.setScroll(mRecentsView == null ? 0 : mRecentsView.getScrollOffset());
-            mTaskViewSimulator.apply(mTransformParams);
-        }
+        updateSysUiFlags(mCurrentShift.value);
+        applyWindowTransform();
         updateLauncherTransitionProgress();
     }
 
@@ -631,17 +578,21 @@
      * @param windowProgress 0 == app, 1 == overview
      */
     private void updateSysUiFlags(float windowProgress) {
-        if (mRecentsView != null) {
+        if (mRecentsAnimationController != null && mRecentsView != null) {
+            TaskView runningTask = mRecentsView.getRunningTaskView();
             TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
             int centermostTaskFlags = centermostTask == null ? 0
                     : centermostTask.getThumbnail().getSysUiStatusNavFlags();
-            boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
+            boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
+            boolean quickswitchThresholdPassed = centermostTask != runningTask;
+
             // We will handle the sysui flags based on the centermost task view.
-            if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.setWindowThresholdCrossed(centermostTaskFlags != 0
-                        && useHomeScreenFlags);
-            }
-            int sysuiFlags = useHomeScreenFlags ? 0 : centermostTaskFlags;
+            mRecentsAnimationController.setUseLauncherSystemBarFlags(
+                    (swipeUpThresholdPassed || quickswitchThresholdPassed)
+                            && centermostTaskFlags != 0);
+            mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
+
+            int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags;
             mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
         }
     }
@@ -682,7 +633,7 @@
      */
     @UiThread
     private void notifyGestureStartedAsync() {
-        final T curActivity = mActivity;
+        final Launcher 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
@@ -768,20 +719,17 @@
         }
     }
 
-    private void onTaskAppeared() {
-        RemoteAnimationTargetCompat app = mGestureState.getAnimationTarget();
-        if (mRecentsAnimationController != null && app != null) {
-
-            // TODO(b/152480470): Update Task target animation after onTaskAppeared holistically.
-            /* android.util.Log.d("LauncherSwipeHandler", "onTaskAppeared");
-
-            final boolean result = mRecentsAnimationController.removeTaskTarget(app);
-            mGestureState.setAnimationTarget(null);
-            android.util.Log.d("LauncherSwipeHandler", "removeTask, result=" + result); */
-
-            mRecentsAnimationController.finish(false /* toRecents */,
-                    null /* onFinishComplete */);
+    @Override
+    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return false;
         }
+        if (mGestureState.getEndTarget() == NEW_TASK
+                && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
+            reset();
+            return true;
+        }
+        return false;
     }
 
     private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
@@ -968,26 +916,63 @@
             Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
         // Set the state, but don't notify until the animation completes
         mGestureState.setEndTarget(target, false /* isAtomic */);
-
         maybeUpdateRecentsAttachedState();
 
         if (mGestureState.getEndTarget() == HOME) {
             HomeAnimationFactory homeAnimFactory;
             if (mActivity != null) {
-                homeAnimFactory = mActivityInterface.prepareHomeUI();
-            } else {
-                homeAnimFactory = new HomeAnimationFactory() {
-                    @NonNull
+                final TaskView runningTaskView = mRecentsView.getRunningTaskView();
+                final View workspaceView;
+                if (runningTaskView != null
+                        && runningTaskView.getTask().key.getComponent() != null) {
+                    workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
+                            runningTaskView.getTask().key.getComponent().getPackageName(),
+                            UserHandle.of(runningTaskView.getTask().key.userId));
+                } else {
+                    workspaceView = null;
+                }
+                final RectF iconLocation = new RectF();
+                boolean canUseWorkspaceView =
+                        workspaceView != null && workspaceView.isAttachedToWindow();
+                FloatingIconView floatingIconView = canUseWorkspaceView
+                        ? FloatingIconView.getFloatingIconView(mActivity, workspaceView,
+                        true /* hideOriginal */, iconLocation, false /* isOpening */)
+                        : null;
+
+                mActivity.getRootView().setForceHideBackArrow(true);
+
+                homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
+
                     @Override
                     public RectF getWindowTargetRect() {
-                        RectF fallbackTarget = new RectF(mAppWindowAnimationHelper.getTargetRect());
-                        Utilities.scaleRectFAboutCenter(fallbackTarget, 0.25f);
-                        return fallbackTarget;
+                        if (canUseWorkspaceView) {
+                            return iconLocation;
+                        } else {
+                            return super.getWindowTargetRect();
+                        }
                     }
 
                     @NonNull
                     @Override
                     public AnimatorPlaybackController createActivityAnimationToHome() {
+                        // Return an empty APC here since we have an non-user controlled animation
+                        // to home.
+                        long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+                        return mActivity.getStateManager().createAnimationToNewWorkspace(
+                                NORMAL, accuracy, 0 /* animComponents */);
+                    }
+
+                    @Override
+                    public void playAtomicAnimation(float velocity) {
+                        new StaggeredWorkspaceAnim(mActivity, velocity,
+                                true /* animateOverviewScrim */).start();
+                    }
+                };
+
+            } else {
+                homeAnimFactory = new HomeAnimationFactory(null) {
+                    @Override
+                    public AnimatorPlaybackController createActivityAnimationToHome() {
                         return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
                     }
                 };
@@ -1028,12 +1013,24 @@
                         // skip doing any future work here for the current gesture.
                         return;
                     }
-                    if (target == NEW_TASK && mRecentsView != null
-                            && mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
-                        // We are about to launch the current running task, so use LAST_TASK state
-                        // instead of NEW_TASK. This could happen, for example, if our scroll is
-                        // aborted after we determined the target to be NEW_TASK.
-                        mGestureState.setEndTarget(LAST_TASK);
+                    if (mRecentsView != null) {
+                        int taskToLaunch = mRecentsView.getNextPage();
+                        int runningTask = getLastAppearedTaskIndex();
+                        boolean hasStartedNewTask = hasStartedNewTask();
+                        if (target == NEW_TASK && taskToLaunch == runningTask
+                                && !hasStartedNewTask) {
+                            // We are about to launch the current running task, so use LAST_TASK
+                            // state instead of NEW_TASK. This could happen, for example, if our
+                            // scroll is aborted after we determined the target to be NEW_TASK.
+                            mGestureState.setEndTarget(LAST_TASK);
+                        } else if (target == LAST_TASK && hasStartedNewTask) {
+                            // We are about to re-launch the previously running task, but we can't
+                            // just finish the controller like we normally would because that would
+                            // instead resume the last task that appeared, and not ensure that this
+                            // task is restored to the top. To address this, re-launch the task as
+                            // if it were a new task.
+                            mGestureState.setEndTarget(NEW_TASK);
+                        }
                     }
                     mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
                 }
@@ -1078,9 +1075,10 @@
 
     private void continueComputingRecentsScrollIfNecessary() {
         if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED)
-                && !mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+                && !mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)
+                && !mCanceled) {
             computeRecentsScrollIfInvisible();
-            mRecentsView.post(this::continueComputingRecentsScrollIfNecessary);
+            mRecentsView.postOnAnimation(this::continueComputingRecentsScrollIfNecessary);
         }
     }
 
@@ -1156,8 +1154,9 @@
 
     @UiThread
     private void startNewTaskInternal() {
-        startNewTask(STATE_HANDLER_INVALIDATED, success -> {
+        startNewTask(success -> {
             if (!success) {
+                reset();
                 // We couldn't launch the task, so take user to overview so they can
                 // decide what to do instead of staying in this broken state.
                 endLauncherTransitionController();
@@ -1167,6 +1166,12 @@
         });
     }
 
+    @Override
+    protected void onRestartLastAppearedTask() {
+        super.onRestartLastAppearedTask();
+        reset();
+    }
+
     private void reset() {
         mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
     }
@@ -1182,19 +1187,6 @@
                 .getAnimationPlayer().isStarted()) {
             mLauncherTransitionController.getAnimationPlayer().cancel();
         }
-
-        if (mFinishingRecentsAnimationForNewTaskId != -1) {
-            // If we are canceling mid-starting a new task, switch to the screenshot since the
-            // recents animation has finished
-            switchToScreenshot();
-            TaskView newRunningTaskView = mRecentsView.getTaskView(
-                    mFinishingRecentsAnimationForNewTaskId);
-            int newRunningTaskId = newRunningTaskView != null
-                    ? newRunningTaskView.getTask().key.id
-                    : -1;
-            mRecentsView.setCurrentTask(newRunningTaskId);
-            mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
-        }
     }
 
     private void invalidateHandler() {
@@ -1333,8 +1325,7 @@
     }
 
     private void setTargetAlphaProvider(TargetAlphaProvider provider) {
-        mAppWindowAnimationHelper.setTaskAlphaCallback(provider);
-        mTaskViewSimulator.setTaskAlphaCallback(provider);
+        mTransformParams.setTaskAlphaCallback(provider);
         updateFinalShift();
     }
 
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 52a2558..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,22 +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.util.AppWindowAnimationHelper;
+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;
@@ -52,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
@@ -104,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, mRecentsRootView.getLastKnownSize())
-                : super.createDeviceProfile();
+                ? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
+                : dp1.copy(this);
     }
 
     @Override
@@ -140,6 +180,10 @@
         return (T) mFallbackRecentsView;
     }
 
+    public OverviewActionsView getActionsView() {
+        return mActionsView;
+    }
+
     @Override
     public void returnToHomescreen() {
         super.returnToHomescreen();
@@ -161,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);
             }
         };
@@ -184,25 +223,17 @@
             RemoteAnimationTargetCompat[] wallpaperTargets) {
         AnimatorSet target = new AnimatorSet();
         boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
-        AppWindowAnimationHelper helper = new AppWindowAnimationHelper(
-            mFallbackRecentsView.getPagedViewOrientedState(), this);
         Animator recentsAnimator = getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
-                wallpaperTargets, null /* depthController */,
-                helper);
+                wallpaperTargets, null /* depthController */);
         target.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
 
         // Found a visible recents task that matches the opening app, lets launch the app from there
         if (activityClosing) {
             Animator adjacentAnimation = mFallbackRecentsView
-                    .createAdjacentPageAnimForTaskLaunch(taskView, helper);
+                    .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;
@@ -214,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
@@ -232,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/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index b44d6df..042c542 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -22,6 +22,7 @@
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.widget.Toast;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
@@ -109,16 +110,26 @@
         public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
             ImageActionsApi imageApi = new ImageActionsApi(
                     mApplicationContext, mThumbnailView::getThumbnail);
+            final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+
             getActionsView().setCallbacks(new OverlayUICallbacks() {
                 @Override
                 public void onShare() {
-                    imageApi.startShareActivity();
+                    if (isAllowedByPolicy) {
+                        imageApi.startShareActivity();
+                    } else {
+                        showBlockedByPolicyMessage();
+                    }
                 }
 
                 @Override
                 public void onScreenshot() {
-                    imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
-                            getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key.id);
+                    if (isAllowedByPolicy) {
+                        imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+                                getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key.id);
+                    } else {
+                        showBlockedByPolicyMessage();
+                    }
                 }
             });
         }
@@ -152,6 +163,13 @@
             // TODO: return the real insets
             return Insets.of(0, 0, 0, 0);
         }
+
+        private void showBlockedByPolicyMessage() {
+            Toast.makeText(
+                    mThumbnailView.getContext(),
+                    R.string.blocked_by_policy,
+                    Toast.LENGTH_LONG).show();
+        }
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 47c07af..9a7a491 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -38,6 +38,7 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
@@ -123,18 +124,19 @@
     public static Animator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
-            DepthController depthController,
-            final AppWindowAnimationHelper inOutHelper) {
+            DepthController depthController) {
+        AppWindowAnimationHelper inOutHelper = new AppWindowAnimationHelper(
+                v.getRecentsView().getPagedViewOrientedState(), v.getContext());
+
         SyncRtSurfaceTransactionApplierCompat applier =
                 new SyncRtSurfaceTransactionApplierCompat(v);
         final RemoteAnimationTargets targets =
                 new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
         targets.addDependentTransactionApplier(applier);
-        AppWindowAnimationHelper.TransformParams params =
-                new AppWindowAnimationHelper.TransformParams()
+        TransformParams params =
+                new TransformParams()
                     .setSyncTransactionApplier(applier)
-                    .setTargetSet(targets)
-                    .setLauncherOnTop(true);
+                    .setTargetSet(targets);
 
         AnimatorSet animatorSet = new AnimatorSet();
         final RecentsView recentsView = v.getRecentsView();
@@ -148,11 +150,9 @@
             final RectF mThumbnailRect;
 
             {
-                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
-
+                params.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
                 inOutHelper.prepareAnimation(
-                        BaseActivity.fromContext(v.getContext()).getDeviceProfile(),
-                        true /* isOpening */);
+                        BaseActivity.fromContext(v.getContext()).getDeviceProfile());
                 inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
                         targets.apps.length == 0 ? null : targets.apps[0]);
 
@@ -175,7 +175,7 @@
                             v.getRecentsView().getClipAnimationHelper();
                     if (liveTileAnimationHelper != null) {
                         // Append the surface transform params for the live tile app.
-                        AppWindowAnimationHelper.TransformParams liveTileParams =
+                        TransformParams liveTileParams =
                                 v.getRecentsView().getLiveTileParams(true /* mightNeedToRefill */);
                         if (liveTileParams != null) {
                             SurfaceParams[] liveTileSurfaceParams =
@@ -186,7 +186,7 @@
                         }
                     }
                     // Apply surface transform using the surface params list.
-                    AppWindowAnimationHelper.applySurfaceParams(params.getSyncTransactionApplier(),
+                    params.applySurfaceParams(
                             surfaceParamsList.toArray(new SurfaceParams[surfaceParamsList.size()]));
                     // Get the task bounds for the app that's being opened after surface transform
                     // update.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index ce7a141..4b2fc75 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -30,8 +30,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
 import android.annotation.TargetApi;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.Service;
@@ -518,8 +516,13 @@
     private GestureState createGestureState() {
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
-        gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
-                () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
+        if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+            gestureState.updateRunningTask(mGestureState.getRunningTask());
+            gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
+        } else {
+            gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
+                    () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
+        }
         return gestureState;
     }
 
@@ -599,15 +602,24 @@
     }
 
     private void handleOrientationSetup(InputConsumer baseInputConsumer) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.1");
+        }
         if (!isFixedRotationTransformEnabled(this)) {
             return;
         }
         mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
         BaseDraggingActivity activity =
                 mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.2");
+        }
         if (activity == null || !(activity.getOverviewPanel() instanceof RecentsView)) {
             return;
         }
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.3");
+        }
         ((RecentsView) activity.getOverviewPanel())
             .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
                 mDeviceState.getDisplayRotation());
@@ -637,14 +649,7 @@
                     runningComponent != null && runningComponent.equals(homeComponent);
         }
 
-        if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
-            // If the finish animation was interrupted, then continue using the other activity input
-            // consumer but with the next task as the running task
-            RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
-            info.id = previousGestureState.getFinishingRecentsAnimationTaskId();
-            gestureState.updateRunningTask(info);
-            return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
-        } else if (gestureState.getRunningTask() == null) {
+        if (gestureState.getRunningTask() == null) {
             return mResetGestureInputConsumer;
         } else if (previousGestureState.isRunningAnimationToLauncher()
                 || gestureState.getActivityInterface().isResumed()
@@ -658,25 +663,22 @@
         } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
             return mResetGestureInputConsumer;
         } else {
-            return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
+            return createOtherActivityInputConsumer(gestureState, event);
         }
     }
 
-    private InputConsumer createOtherActivityInputConsumer(GestureState previousGestureState,
-            GestureState gestureState, MotionEvent event) {
+    private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
+            MotionEvent event) {
 
-        final boolean shouldDefer;
         final BaseSwipeUpHandler.Factory factory;
-
         if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
-            shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
             factory = mFallbackSwipeHandlerFactory;
         } else {
-            shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
-                    event);
             factory = mLauncherSwipeHandlerFactory;
         }
 
+        final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
+                || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
         final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
         return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
                 gestureState, shouldDefer, this::onConsumerInactive,
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 3cf9b2c..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,48 +15,28 @@
  */
 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.graphics.Rect;
+import android.os.Build;
 import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.view.View;
 
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.quickstep.RecentsActivity;
-import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 
 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;
 
@@ -65,7 +45,8 @@
     }
 
     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr, false);
+        super(context, attrs, defStyleAttr, FALLBACK_RECENTS_SIZE_STRATEGY);
+        mActivity.getStateManager().addStateListener(this);
     }
 
     @Override
@@ -81,84 +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
-    protected void getTaskSize(DeviceProfile dp, Rect outRect) {
-        LayoutUtils.calculateFallbackTaskSize(getContext(), dp, outRect);
-    }
-
-    @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);
-
-        if (getTaskViewCount() == 0) {
-            mZoomScale = 1f;
-        } else {
-            TaskView dummyTask = getTaskViewAt(0);
-            mZoomScale = getTempAppWindowAnimationHelper()
-                    .updateForFullscreenOverview(dummyTask)
-                    .getSrcToTargetScale();
-        }
-
-        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);
@@ -195,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/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 7b8d40c..adf19df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -21,8 +21,8 @@
 
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
 import android.content.ComponentName;
@@ -44,12 +44,13 @@
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.LockScreenRecentsActivity;
 import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.TransformParams;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -84,7 +85,7 @@
 
     private final PointF mTouchDown = new PointF();
     private final AppWindowAnimationHelper mAppWindowAnimationHelper;
-    private final AppWindowAnimationHelper.TransformParams mTransformParams;
+    private final TransformParams mTransformParams;
     private final Point mDisplaySize;
     private final MultiStateCallback mStateCallback;
 
@@ -105,7 +106,7 @@
         mGestureState = gestureState;
         mTouchSlopSquared = squaredTouchSlop(context);
         mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
-        mTransformParams = new AppWindowAnimationHelper.TransformParams();
+        mTransformParams = new TransformParams();
         mInputMonitorCompat = inputMonitorCompat;
 
         // Do not use DeviceProfile as the user data might be locked
@@ -230,8 +231,7 @@
 
         Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
         displaySize.offsetTo(displaySize.left, 0);
-        mTransformParams.setTargetSet(mRecentsAnimationTargets)
-                .setLauncherOnTop(true);
+        mTransformParams.setTargetSet(mRecentsAnimationTargets);
         mAppWindowAnimationHelper.updateTargetRect(displaySize);
         mAppWindowAnimationHelper.applyTransform(mTransformParams);
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 6bfabcd..c82d4b5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -95,6 +96,9 @@
             ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
         }
         ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
+        }
         boolean handled = mEventReceiver.test(ev);
         ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
         ev.setEdgeFlags(flags);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 5abbd86..a7979cc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -15,12 +15,10 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.Utilities.boundToRange;
+import static com.android.launcher3.Utilities.mapRange;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -37,25 +35,22 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.utilities.RectFEvaluator;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 /**
  * Utility class to handle window clip animation
  */
 @TargetApi(Build.VERSION_CODES.P)
-public class AppWindowAnimationHelper {
+public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
 
     // The bounds of the source app in device coordinates
     private final RectF mSourceStackBounds = new RectF();
@@ -96,12 +91,6 @@
     // Corner radius currently applied to transformed window.
     private float mCurrentCornerRadius;
 
-    // Whether to boost the opening animation target layers, or the closing
-    private int mBoostModeTargetLayers = -1;
-
-    private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
-    private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
-
     public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
         Resources res = context.getResources();
         mOrientedState = orientedState;
@@ -163,8 +152,7 @@
                 mTargetRect.width(), mTargetRect.height());
     }
 
-    public void prepareAnimation(DeviceProfile dp, boolean isOpening) {
-        mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
+    public void prepareAnimation(DeviceProfile dp) {
         mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode;
     }
 
@@ -173,7 +161,7 @@
         if (surfaceParams == null) {
             return null;
         }
-        applySurfaceParams(params.mSyncTransactionApplier, surfaceParams);
+        params.applySurfaceParams(surfaceParams);
         return mCurrentRect;
     }
 
@@ -182,97 +170,60 @@
      * the SurfaceParams to apply via {@link SyncRtSurfaceTransactionApplierCompat#applyParams}.
      */
     public SurfaceParams[] computeSurfaceParams(TransformParams params) {
-        if (params.mTargetSet == null) {
+        if (params.getTargetSet() == null) {
             return null;
         }
 
-        float progress = Utilities.boundToRange(params.mProgress, 0, 1);
         updateCurrentRect(params);
+        return params.createSurfaceParams(this);
+    }
 
-        SurfaceParams[] surfaceParams = new SurfaceParams[params.mTargetSet.unfilteredApps.length];
-        for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) {
-            RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i];
-            SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+    @Override
+    public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
+            int targetMode, TransformParams params) {
+        Rect crop = mTmpRect;
+        crop.set(app.screenSpaceBounds);
+        crop.offsetTo(0, 0);
+        float cornerRadius = 0f;
+        float scale = Math.max(mCurrentRect.width(), mTargetRect.width()) / crop.width();
+        if (app.mode == targetMode
+                && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+            mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
             if (app.localBounds != null) {
-                mTmpMatrix.setTranslate(0, 0);
-                if (app.activityType == ACTIVITY_TYPE_HOME && app.mode == MODE_CLOSING) {
-                    mTmpMatrix.setTranslate(app.localBounds.left, app.localBounds.top);
-                }
+                mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
             } else {
-                mTmpMatrix.setTranslate(app.position.x, app.position.y);
+                mTmpMatrix.postTranslate(app.position.x, app.position.y);
+            }
+            mCurrentClipRectF.roundOut(crop);
+            if (mSupportsRoundedCornersOnWindows) {
+                if (params.getCornerRadius() > -1) {
+                    cornerRadius = params.getCornerRadius();
+                    scale = mCurrentRect.width() / crop.width();
+                } else {
+                    float windowCornerRadius = mUseRoundedCornersOnWindows
+                            ? mWindowCornerRadius : 0;
+                    cornerRadius = mapRange(boundToRange(params.getProgress(), 0, 1),
+                            windowCornerRadius, mTaskCornerRadius);
+                }
+                mCurrentCornerRadius = cornerRadius;
             }
 
-            Rect crop = mTmpRect;
-            crop.set(app.screenSpaceBounds);
-            crop.offsetTo(0, 0);
-            float alpha;
-            float cornerRadius = 0f;
-            float scale = Math.max(mCurrentRect.width(), mTargetRect.width()) / crop.width();
-            if (app.mode == params.mTargetSet.targetMode) {
-                alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha);
-                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                    mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
-                    if (app.localBounds != null) {
-                        mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
-                    } else {
-                        mTmpMatrix.postTranslate(app.position.x, app.position.y);
-                    }
-                    mCurrentClipRectF.roundOut(crop);
-                    if (mSupportsRoundedCornersOnWindows) {
-                        if (params.mCornerRadius > -1) {
-                            cornerRadius = params.mCornerRadius;
-                            scale = mCurrentRect.width() / crop.width();
-                        } else {
-                            float windowCornerRadius = mUseRoundedCornersOnWindows
-                                    ? mWindowCornerRadius : 0;
-                            cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
-                                    mTaskCornerRadius);
-                        }
-                        mCurrentCornerRadius = cornerRadius;
-                    }
-                    // Fade out Assistant overlay.
-                    if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
-                            && app.isNotInRecents) {
-                        alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress);
-                    }
-                } else if (params.mTargetSet.hasRecents) {
-                    // If home has a different target then recents, reverse anim the
-                    // home target.
-                    alpha = 1 - (progress * params.mTargetAlpha);
-                }
-            } else {
-                alpha = mBaseAlphaCallback.getAlpha(app, progress);
-                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.mLauncherOnTop) {
-                    crop = null;
-                }
-            }
-            builder.withAlpha(alpha)
-                    .withMatrix(mTmpMatrix)
+            builder.withMatrix(mTmpMatrix)
                     .withWindowCrop(crop)
                     // Since radius is in Surface space, but we draw the rounded corners in screen
                     // space, we have to undo the scale
                     .withCornerRadius(cornerRadius / scale);
-            surfaceParams[i] = builder.build();
+
         }
-        return surfaceParams;
     }
 
     public RectF updateCurrentRect(TransformParams params) {
-        if (params.mCurrentRect != null) {
-            mCurrentRect.set(params.mCurrentRect);
+        if (params.getCurrentRect() != null) {
+            mCurrentRect.set(params.getCurrentRect());
         } else {
             mTmpRectF.set(mTargetRect);
-            Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale);
-            mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF));
-            if (mOrientedState == null
-                    || !mOrientedState.isMultipleOrientationSupportedByDevice()) {
-                mCurrentRect.offset(params.mOffset, 0);
-            } else {
-                int displayRotation = mOrientedState.getDisplayRotation();
-                int launcherRotation = mOrientedState.getLauncherRotation();
-                mOrientedState.getOrientationHandler().offsetTaskRect(mCurrentRect,
-                    params.mOffset, displayRotation, launcherRotation);
-            }
+            mCurrentRect.set(mRectFEvaluator.evaluate(
+                    params.getProgress(), mSourceRect, mTmpRectF));
         }
 
         updateClipRect(params);
@@ -281,7 +232,7 @@
 
     private void updateClipRect(TransformParams params) {
         // Don't clip past progress > 1.
-        float progress = Math.min(1, params.mProgress);
+        float progress = Math.min(1, params.getProgress());
         mCurrentClipRectF.left = mSourceWindowClipInsets.left * progress;
         mCurrentClipRectF.top = mSourceWindowClipInsets.top * progress;
         mCurrentClipRectF.right =
@@ -295,32 +246,6 @@
         return mCurrentRectWithInsets;
     }
 
-    public static void applySurfaceParams(@Nullable SyncRtSurfaceTransactionApplierCompat
-            syncTransactionApplier, SurfaceParams[] params) {
-        if (syncTransactionApplier != null) {
-            syncTransactionApplier.scheduleApply(params);
-        } else {
-            TransactionCompat t = new TransactionCompat();
-            for (SurfaceParams param : params) {
-                SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
-            }
-            t.setEarlyWakeup();
-            t.apply();
-        }
-    }
-
-    public void setTaskAlphaCallback(TargetAlphaProvider callback) {
-        mTaskAlphaCallback = callback;
-    }
-
-    public void setBaseAlphaCallback(TargetAlphaProvider callback) {
-        mBaseAlphaCallback = callback;
-    }
-
-    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
-        fromTaskThumbnailView(ttv, rv, null);
-    }
-
     public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
             @Nullable RemoteAnimationTargetCompat target) {
         BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
@@ -357,19 +282,6 @@
         }
     }
 
-    /**
-     * Compute scale and translation y such that the specified task view fills the screen.
-     */
-    public AppWindowAnimationHelper updateForFullscreenOverview(TaskView v) {
-        TaskThumbnailView thumbnailView = v.getThumbnail();
-        RecentsView recentsView = v.getRecentsView();
-        fromTaskThumbnailView(thumbnailView, recentsView);
-        Rect taskSize = new Rect();
-        recentsView.getTaskSize(taskSize);
-        updateTargetRect(taskSize);
-        return this;
-    }
-
     private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
         SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(activity);
         if (proxy.isActive()) {
@@ -409,157 +321,4 @@
         return mCurrentCornerRadius;
     }
 
-    public interface TargetAlphaProvider {
-        float getAlpha(RemoteAnimationTargetCompat target, float expectedAlpha);
-    }
-
-    public static class TransformParams {
-        private float mProgress;
-        private float mOffset;
-        private float mOffsetScale;
-        private @Nullable RectF mCurrentRect;
-        private float mTargetAlpha;
-        private float mCornerRadius;
-        private boolean mLauncherOnTop;
-        private RemoteAnimationTargets mTargetSet;
-        private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
-
-        public TransformParams() {
-            mProgress = 0;
-            mOffset = 0;
-            mOffsetScale = 1;
-            mCurrentRect = null;
-            mTargetAlpha = 1;
-            mCornerRadius = -1;
-            mLauncherOnTop = false;
-        }
-
-        /**
-         * Sets the progress of the transformation, where 0 is the source and 1 is the target. We
-         * automatically adjust properties such as currentRect and cornerRadius based on this
-         * progress, unless they are manually overridden by setting them on this TransformParams.
-         */
-        public TransformParams setProgress(float progress) {
-            mProgress = progress;
-            return this;
-        }
-
-        /**
-         * Sets the corner radius of the transformed window, in pixels. If unspecified (-1), we
-         * simply interpolate between the window's corner radius to the task view's corner radius,
-         * based on {@link #mProgress}.
-         */
-        public TransformParams setCornerRadius(float cornerRadius) {
-            mCornerRadius = cornerRadius;
-            return this;
-        }
-
-        /**
-         * Sets the current rect to show the transformed window, in device coordinates. This gives
-         * the caller manual control of where to show the window. If unspecified (null), we
-         * interpolate between {@link AppWindowAnimationHelper#mSourceRect} and
-         * {@link AppWindowAnimationHelper#mTargetRect}, based on {@link #mProgress}.
-         */
-        public TransformParams setCurrentRect(RectF currentRect) {
-            mCurrentRect = currentRect;
-            return this;
-        }
-
-        /**
-         * Specifies the alpha of the transformed window. Default is 1.
-         */
-        public TransformParams setTargetAlpha(float targetAlpha) {
-            mTargetAlpha = targetAlpha;
-            return this;
-        }
-
-        /**
-         * If {@link #mCurrentRect} is null (i.e. {@link #setCurrentRect(RectF)} hasn't overridden
-         * the default), then offset the current rect by this amount after computing the rect based
-         * on {@link #mProgress}.
-         */
-        public TransformParams setOffset(float offset) {
-            mOffset = offset;
-            return this;
-        }
-
-        /**
-         * If {@link #mCurrentRect} is null (i.e. {@link #setCurrentRect(RectF)} hasn't overridden
-         * the default), then scale the current rect by this amount after computing the rect based
-         * on {@link #mProgress}.
-         */
-        public TransformParams setOffsetScale(float offsetScale) {
-            mOffsetScale = offsetScale;
-            return this;
-        }
-
-        /**
-         * If true, sets the crop = null and layer = Integer.MAX_VALUE for targets that don't match
-         * {@link #mTargetSet}.targetMode. (Currently only does this when live tiles are enabled.)
-         */
-        public TransformParams setLauncherOnTop(boolean launcherOnTop) {
-            mLauncherOnTop = launcherOnTop;
-            return this;
-        }
-
-        /**
-         * Specifies the set of RemoteAnimationTargetCompats that are included in the transformation
-         * that these TransformParams help compute. These TransformParams generally only apply to
-         * the targetSet.apps which match the targetSet.targetMode (e.g. the MODE_CLOSING app when
-         * swiping to home).
-         */
-        public TransformParams setTargetSet(RemoteAnimationTargets targetSet) {
-            mTargetSet = targetSet;
-            return this;
-        }
-
-        /**
-         * Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
-         * are computed based on these TransformParams.
-         */
-        public TransformParams setSyncTransactionApplier(
-                SyncRtSurfaceTransactionApplierCompat applier) {
-            mSyncTransactionApplier = applier;
-            return this;
-        }
-
-        // Pubic getters so outside packages can read the values.
-
-        public float getProgress() {
-            return mProgress;
-        }
-
-        public float getOffset() {
-            return mOffset;
-        }
-
-        public float getOffsetScale() {
-            return mOffsetScale;
-        }
-
-        @Nullable
-        public RectF getCurrentRect() {
-            return mCurrentRect;
-        }
-
-        public float getTargetAlpha() {
-            return mTargetAlpha;
-        }
-
-        public float getCornerRadius() {
-            return mCornerRadius;
-        }
-
-        public boolean isLauncherOnTop() {
-            return mLauncherOnTop;
-        }
-
-        public RemoteAnimationTargets getTargetSet() {
-            return mTargetSet;
-        }
-
-        public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
-            return mSyncTransactionApplier;
-        }
-    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
index 217eca5..85006da 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
@@ -15,10 +15,10 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 820bd17..32fc0de 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -202,6 +202,12 @@
         alpha.setInterpolator(LINEAR);
         alpha.setDuration(ALPHA_DURATION_MS);
         alpha.setStartDelay(startDelay);
+        alpha.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                v.setAlpha(1f);
+            }
+        });
         mAnimators.play(alpha);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 9781300..b8f0f4d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -17,14 +17,9 @@
 
 import static android.view.Surface.ROTATION_0;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.states.RotationHelper.deltaRotation;
 import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
-import static com.android.quickstep.util.AppWindowAnimationHelper.applySurfaceParams;
-import static com.android.quickstep.util.RecentsOrientedState.isFixedRotationTransformEnabled;
 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
 import android.content.Context;
@@ -36,24 +31,20 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.RecentsAnimationTargets;
-import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
-import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
 import com.android.quickstep.views.RecentsView.ScrollState;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
 import com.android.quickstep.views.TaskView;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
  * A utility class which emulates the layout behavior of TaskView and RecentsView
  */
-public class TaskViewSimulator {
+public class TaskViewSimulator implements TransformParams.BuilderProxy {
 
     private final Rect mTmpCropRect = new Rect();
     private final RectF mTempRectF = new RectF();
@@ -61,7 +52,7 @@
 
     private final RecentsOrientedState mOrientationState;
     private final Context mContext;
-    private final TaskSizeProvider mSizeProvider;
+    private final WindowSizeStrategy mSizeStrategy;
 
     private final Rect mTaskRect = new Rect();
     private final PointF mPivot = new PointF();
@@ -69,16 +60,11 @@
 
     private final Matrix mMatrix = new Matrix();
     private RemoteAnimationTargetCompat mRunningTarget;
-    private RecentsAnimationTargets mAllTargets;
-
-    // Whether to boost the opening animation target layers, or the closing
-    private int mBoostModeTargetLayers = -1;
-    private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
 
     // Thumbnail view properties
     private final Rect mThumbnailPosition = new Rect();
     private final ThumbnailData mThumbnailData = new ThumbnailData();
-    private final PreviewPositionHelper mPositionHelper;
+    private final PreviewPositionHelper mPositionHelper = new PreviewPositionHelper();
     private final Matrix mInversePositionMatrix = new Matrix();
 
     // TaskView properties
@@ -95,17 +81,11 @@
     private boolean mLayoutValid = false;
     private boolean mScrollValid = false;
 
-    public TaskViewSimulator(Context context, TaskSizeProvider sizeProvider,
-            boolean rotationSupportedByActivity) {
+    public TaskViewSimulator(Context context, WindowSizeStrategy sizeStrategy) {
         mContext = context;
-        mSizeProvider = sizeProvider;
-        mPositionHelper = new PreviewPositionHelper(context);
+        mSizeStrategy = sizeStrategy;
 
-        mOrientationState = new RecentsOrientedState(context, rotationSupportedByActivity,
-                i -> { });
-        // We do not need to attach listeners as the simulator is created just for the gesture
-        // duration, and any settings are unlikely to change during this
-        mOrientationState.initWithoutListeners();
+        mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
 
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
         mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
@@ -114,9 +94,9 @@
     /**
      * Sets the device profile for the current state
      */
-    public void setDp(DeviceProfile dp, boolean isOpening) {
+    public void setDp(DeviceProfile dp) {
         mDp = dp;
-        mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
+        mOrientationState.setMultiWindowMode(mDp.isMultiWindowMode);
         mLayoutValid = false;
     }
 
@@ -124,15 +104,7 @@
      * @see com.android.quickstep.views.RecentsView#setLayoutRotation(int, int)
      */
     public void setLayoutRotation(int touchRotation, int displayRotation) {
-        int launcherRotation;
-        if (!mOrientationState.isMultipleOrientationSupportedByDevice()
-                || mOrientationState.isHomeRotationAllowed()) {
-            launcherRotation = displayRotation;
-        } else {
-            launcherRotation = ROTATION_0;
-        }
-
-        mOrientationState.update(touchRotation, displayRotation, launcherRotation);
+        mOrientationState.update(touchRotation, displayRotation);
         mLayoutValid = false;
     }
 
@@ -143,17 +115,15 @@
         if (mDp == null) {
             return 1;
         }
-        mSizeProvider.calculateTaskSize(mContext, mDp, mTaskRect);
+        mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect);
         return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
     }
 
     /**
      * Sets the targets which the simulator will control
      */
-    public void setPreview(
-            RemoteAnimationTargetCompat runningTarget, RecentsAnimationTargets allTargets) {
+    public void setPreview(RemoteAnimationTargetCompat runningTarget) {
         mRunningTarget = runningTarget;
-        mAllTargets = allTargets;
 
         mThumbnailData.insets.set(mRunningTarget.contentInsets);
         // TODO: What is this?
@@ -161,8 +131,7 @@
 
         mThumbnailPosition.set(runningTarget.screenSpaceBounds);
         // TODO: Should sourceContainerBounds already have this offset?
-        mThumbnailPosition.offsetTo(mRunningTarget.position.x, mRunningTarget.position.y);
-
+        mThumbnailPosition.offset(-mRunningTarget.position.x, -mRunningTarget.position.y);
         mLayoutValid = false;
     }
 
@@ -177,10 +146,40 @@
     }
 
     /**
-     * Sets an alternate function which can be used to control the alpha
+     * Returns the current clipped/visible window bounds in the window coordinate space
      */
-    public void setTaskAlphaCallback(TargetAlphaProvider callback) {
-        mTaskAlphaCallback = callback;
+    public RectF getCurrentCropRect() {
+        // Crop rect is the inverse of thumbnail matrix
+        RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
+        mTempRectF.set(-insets.left, -insets.top,
+                mTaskRect.width() + insets.right, mTaskRect.height() + insets.bottom);
+        mInversePositionMatrix.mapRect(mTempRectF);
+        return mTempRectF;
+    }
+
+    public RecentsOrientedState getOrientationState() {
+        return mOrientationState;
+    }
+
+    /**
+     * Returns the current transform applied to the window
+     */
+    public Matrix getCurrentMatrix() {
+        return mMatrix;
+    }
+
+    /**
+     * Applies the rotation on the matrix to so that it maps from launcher coordinate space to
+     * window coordinate space.
+     */
+    public void applyWindowToHomeRotation(Matrix matrix) {
+        mMatrix.postTranslate(mDp.windowX, mDp.windowY);
+        postDisplayRotation(deltaRotation(
+                mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
+                mDp.widthPx, mDp.heightPx, matrix);
+        if (mRunningTarget != null) {
+            matrix.postTranslate(-mRunningTarget.position.x, -mRunningTarget.position.y);
+        }
     }
 
     /**
@@ -194,12 +193,12 @@
             mLayoutValid = true;
 
             getFullScreenScale();
-            mThumbnailData.rotation = isFixedRotationTransformEnabled(mContext)
-                    ? mOrientationState.getDisplayRotation() : mPositionHelper.getCurrentRotation();
+            mThumbnailData.rotation = mOrientationState.getDisplayRotation();
 
-            mPositionHelper.updateThumbnailMatrix(mThumbnailPosition, mThumbnailData,
-                    mDp.isMultiWindowMode, mTaskRect.width(), mTaskRect.height());
-
+            mPositionHelper.updateThumbnailMatrix(
+                    mThumbnailPosition, mThumbnailData,
+                    mTaskRect.width(), mTaskRect.height(),
+                    mDp, mOrientationState.getLauncherRotation());
             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
 
             PagedOrientationHandler poh = mOrientationState.getOrientationHandler();
@@ -240,9 +239,7 @@
 
         // Apply recensView matrix
         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
-        postDisplayRotation(deltaRotation(
-                mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
-                mDp.widthPx, mDp.heightPx, mMatrix);
+        applyWindowToHomeRotation(mMatrix);
 
         // Crop rect is the inverse of thumbnail matrix
         mTempRectF.set(-insets.left, -insets.top,
@@ -250,39 +247,18 @@
         mInversePositionMatrix.mapRect(mTempRectF);
         mTempRectF.roundOut(mTmpCropRect);
 
-        SurfaceParams[] surfaceParams = new SurfaceParams[mAllTargets.unfilteredApps.length];
-        for (int i = 0; i < mAllTargets.unfilteredApps.length; i++) {
-            RemoteAnimationTargetCompat app = mAllTargets.unfilteredApps[i];
-            SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash)
-                    .withLayer(RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers));
+        params.applySurfaceParams(params.createSurfaceParams(this));
+    }
 
-            if (app.mode == mAllTargets.targetMode) {
-                float alpha = mTaskAlphaCallback.getAlpha(app, params.getTargetAlpha());
-                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                    // Fade out Assistant overlay.
-                    if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
-                            && app.isNotInRecents) {
-                        alpha = Interpolators.ACCEL_2.getInterpolation(fullScreenProgress.value);
-                    }
-
-                    builder.withAlpha(alpha)
-                            .withMatrix(mMatrix)
-                            .withWindowCrop(mTmpCropRect)
-                            .withCornerRadius(getCurrentCornerRadius());
-                } else if (params.getTargetSet().hasRecents) {
-                    // If home has a different target then recents, reverse anim the home target.
-                    builder.withAlpha(fullScreenProgress.value * params.getTargetAlpha());
-                }
-            } else {
-                builder.withAlpha(1);
-                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.isLauncherOnTop()) {
-                    builder.withLayer(Integer.MAX_VALUE);
-                }
-            }
-            surfaceParams[i] = builder.build();
+    @Override
+    public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
+            int targetMode, TransformParams params) {
+        if (app.mode == targetMode
+                && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+            builder.withMatrix(mMatrix)
+                    .withWindowCrop(mTmpCropRect)
+                    .withCornerRadius(getCurrentCornerRadius());
         }
-
-        applySurfaceParams(params.getSyncTransactionApplier(), surfaceParams);
     }
 
     /**
@@ -298,16 +274,4 @@
         // Ideally we should use square-root. This is an optimization as one of the dimension is 0.
         return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
     }
-
-    /**
-     * Interface for calculating taskSize
-     */
-    public interface TaskSizeProvider {
-
-        /**
-         * Sets the outRect to the expected taskSize
-         */
-        void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect);
-    }
-
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
new file mode 100644
index 0000000..83b64db
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -0,0 +1,205 @@
+/*
+ * 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.util;
+
+import android.graphics.RectF;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.RemoteAnimationTargets;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.TransactionCompat;
+
+public class TransformParams {
+
+    private float mProgress;
+    private @Nullable RectF mCurrentRect;
+    private float mTargetAlpha;
+    private float mCornerRadius;
+    private RemoteAnimationTargets mTargetSet;
+    private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
+
+    private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
+    private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
+
+    public TransformParams() {
+        mProgress = 0;
+        mCurrentRect = null;
+        mTargetAlpha = 1;
+        mCornerRadius = -1;
+    }
+
+    /**
+     * Sets the progress of the transformation, where 0 is the source and 1 is the target. We
+     * automatically adjust properties such as currentRect and cornerRadius based on this
+     * progress, unless they are manually overridden by setting them on this TransformParams.
+     */
+    public TransformParams setProgress(float progress) {
+        mProgress = progress;
+        return this;
+    }
+
+    /**
+     * Sets the corner radius of the transformed window, in pixels. If unspecified (-1), we
+     * simply interpolate between the window's corner radius to the task view's corner radius,
+     * based on {@link #mProgress}.
+     */
+    public TransformParams setCornerRadius(float cornerRadius) {
+        mCornerRadius = cornerRadius;
+        return this;
+    }
+
+    /**
+     * Sets the current rect to show the transformed window, in device coordinates. This gives
+     * the caller manual control of where to show the window. If unspecified (null), we
+     * interpolate between {@link AppWindowAnimationHelper#mSourceRect} and
+     * {@link AppWindowAnimationHelper#mTargetRect}, based on {@link #mProgress}.
+     */
+    public TransformParams setCurrentRect(RectF currentRect) {
+        mCurrentRect = currentRect;
+        return this;
+    }
+
+    /**
+     * Specifies the alpha of the transformed window. Default is 1.
+     */
+    public TransformParams setTargetAlpha(float targetAlpha) {
+        mTargetAlpha = targetAlpha;
+        return this;
+    }
+
+    /**
+     * Specifies the set of RemoteAnimationTargetCompats that are included in the transformation
+     * that these TransformParams help compute. These TransformParams generally only apply to
+     * the targetSet.apps which match the targetSet.targetMode (e.g. the MODE_CLOSING app when
+     * swiping to home).
+     */
+    public TransformParams setTargetSet(RemoteAnimationTargets targetSet) {
+        mTargetSet = targetSet;
+        return this;
+    }
+
+    /**
+     * Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
+     * are computed based on these TransformParams.
+     */
+    public TransformParams setSyncTransactionApplier(
+            SyncRtSurfaceTransactionApplierCompat applier) {
+        mSyncTransactionApplier = applier;
+        return this;
+    }
+
+    /**
+     * Sets an alternate function which can be used to control the alpha of target app
+     */
+    public TransformParams setTaskAlphaCallback(TargetAlphaProvider callback) {
+        mTaskAlphaCallback = callback;
+        return this;
+    }
+
+    /**
+     * Sets an alternate function which can be used to control the alpha of non-target app
+     */
+    public TransformParams setBaseAlphaCallback(TargetAlphaProvider callback) {
+        mBaseAlphaCallback = callback;
+        return this;
+    }
+
+    public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+        RemoteAnimationTargets targets = mTargetSet;
+        SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
+        for (int i = 0; i < targets.unfilteredApps.length; i++) {
+            RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+            SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+
+            float progress = Utilities.boundToRange(getProgress(), 0, 1);
+            float alpha;
+            if (app.mode == targets.targetMode) {
+                alpha = mTaskAlphaCallback.getAlpha(app, getTargetAlpha());
+                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                    // Fade out Assistant overlay.
+                    if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
+                            && app.isNotInRecents) {
+                        alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress);
+                    }
+                } else if (targets.hasRecents) {
+                    // If home has a different target then recents, reverse anim the
+                    // home target.
+                    alpha = 1 - (progress * getTargetAlpha());
+                }
+            } else {
+                alpha = mBaseAlphaCallback.getAlpha(app, progress);
+            }
+            proxy.onBuildParams(builder.withAlpha(alpha), app, targets.targetMode, this);
+            surfaceParams[i] = builder.build();
+        }
+        return surfaceParams;
+    }
+
+    // Pubic getters so outside packages can read the values.
+
+    public float getProgress() {
+        return mProgress;
+    }
+
+    @Nullable
+    public RectF getCurrentRect() {
+        return mCurrentRect;
+    }
+
+    public float getTargetAlpha() {
+        return mTargetAlpha;
+    }
+
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
+    public RemoteAnimationTargets getTargetSet() {
+        return mTargetSet;
+    }
+
+    public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
+        return mSyncTransactionApplier;
+    }
+
+    public void applySurfaceParams(SurfaceParams[] params) {
+        if (mSyncTransactionApplier != null) {
+            mSyncTransactionApplier.scheduleApply(params);
+        } else {
+            TransactionCompat t = new TransactionCompat();
+            for (SurfaceParams param : params) {
+                SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
+            }
+            t.setEarlyWakeup();
+            t.apply();
+        }
+    }
+
+    public interface TargetAlphaProvider {
+        float getAlpha(RemoteAnimationTargetCompat target, float expectedAlpha);
+    }
+
+    public interface BuilderProxy {
+
+        void onBuildParams(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, int targetMode, TransformParams params);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index e455939..1018211 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -56,7 +56,8 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mScrollOffset = mIsRtl ? mParent.getPaddingRight() / 2 : - mParent.getPaddingLeft() / 2;
+        PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler();
+        mScrollOffset = orientationHandler.getClearAllScrollOffset(mParent, mIsRtl);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
index eb8da6e..7cc00b7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
@@ -21,12 +21,12 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.FastBitmapDrawable;
 
 import java.util.ArrayList;
 
-import androidx.annotation.NonNull;
-
 /**
  * A view which draws a drawable stretched to fit its size. Unlike ImageView, it avoids relayout
  * when the drawable changes.
@@ -130,4 +130,14 @@
             mScaleListeners.remove(listener);
         }
     }
+
+    @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+        if (alpha > 0) {
+            setVisibility(VISIBLE);
+        } else {
+            setVisibility(INVISIBLE);
+        }
+    }
 }
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 9005651..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
@@ -25,35 +25,30 @@
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
 
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
 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;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.AppWindowAnimationHelper;
-import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
-import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.TransformParams;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.RecentsExtraCard;
 
@@ -62,9 +57,7 @@
  */
 @TargetApi(Build.VERSION_CODES.O)
 public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
-        implements StateListener {
-
-    private static final Rect sTempRect = new Rect();
+        implements StateListener<LauncherState> {
 
     private final TransformParams mTransformParams = new TransformParams();
 
@@ -96,7 +89,7 @@
     }
 
     public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr, true);
+        super(context, attrs, defStyleAttr, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
         mActivity.getStateManager().addStateListener(this);
     }
 
@@ -128,31 +121,12 @@
         }
     }
 
-    @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.
      */
     @Override
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv,
-            AppWindowAnimationHelper helper) {
-        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
+    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
+        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
 
         if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
             // Hotseat doesn't move when opening recents with the button,
@@ -179,11 +153,6 @@
     }
 
     @Override
-    protected void getTaskSize(DeviceProfile dp, Rect outRect) {
-        LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
-    }
-
-    @Override
     protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (tv.isRunningTask()) {
@@ -223,14 +192,14 @@
 
     @Override
     public void redrawLiveTile(boolean mightNeedToRefill) {
-        AppWindowAnimationHelper.TransformParams transformParams = getLiveTileParams(mightNeedToRefill);
+        TransformParams transformParams = getLiveTileParams(mightNeedToRefill);
         if (transformParams != null) {
             mAppWindowAnimationHelper.applyTransform(transformParams);
         }
     }
 
     @Override
-    public AppWindowAnimationHelper.TransformParams getLiveTileParams(
+    public TransformParams getLiveTileParams(
             boolean mightNeedToRefill) {
         if (!mEnableDrawingLiveTile || mRecentsAnimationController == null
                 || mRecentsAnimationTargets == null || mAppWindowAnimationHelper == null) {
@@ -258,8 +227,7 @@
                     .setCurrentRect(mTempRectF)
                     .setTargetAlpha(taskView.getAlpha())
                     .setSyncTransactionApplier(mSyncTransactionApplier)
-                    .setTargetSet(mRecentsAnimationTargets)
-                    .setLauncherOnTop(true);
+                    .setTargetSet(mRecentsAnimationTargets);
         }
         return mTransformParams;
     }
@@ -383,4 +351,16 @@
     protected DepthController getDepthController() {
         return mActivity.getDepthController();
     }
+
+    @Override
+    public void setModalStateEnabled(boolean isModalState) {
+        super.setModalStateEnabled(isModalState);
+        if (isModalState) {
+            mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK);
+        } else {
+            if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
+                mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+            }
+        }
+    }
 }
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 d160686..7201b02 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
@@ -31,6 +31,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
 
 import java.lang.annotation.Retention;
@@ -42,6 +43,8 @@
 public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
         implements OnClickListener {
 
+    public static final long VISIBILITY_TRANSITION_DURATION_MS = 80;
+
     @IntDef(flag = true, value = {
             HIDDEN_UNSUPPORTED_NAVIGATION,
             HIDDEN_DISABLED_FEATURE,
@@ -139,4 +142,21 @@
     public AlphaProperty getVisibilityAlpha() {
         return mMultiValueAlpha.getProperty(INDEX_VISIBILITY_ALPHA);
     }
+
+    /** 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()
+                    .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_three_button);
+        } else {
+            bottomMargin = getResources()
+                    .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_gesture);
+        }
+        LayoutParams params = (LayoutParams) getLayoutParams();
+        params.setMargins(
+                params.leftMargin, 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 cd3abed..979e3ef 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
@@ -16,12 +16,15 @@
 
 package com.android.quickstep.views;
 
+import static android.view.Surface.ROTATION_0;
+
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
@@ -29,8 +32,8 @@
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_DISMISS_SWIPE_UP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
@@ -54,7 +57,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -109,6 +111,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DynamicResource;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.OverScroller;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewPool;
@@ -121,13 +124,15 @@
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.ViewUtils;
 import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.WindowSizeStrategy;
 import com.android.systemui.plugins.ResourceProvider;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ConfigurationCompat;
 import com.android.systemui.shared.system.LauncherEventUtil;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -202,6 +207,7 @@
             };
 
     protected final RecentsOrientedState mOrientationState;
+    protected final WindowSizeStrategy mSizeStrategy;
     protected RecentsAnimationController mRecentsAnimationController;
     protected RecentsAnimationTargets mRecentsAnimationTargets;
     protected AppWindowAnimationHelper mAppWindowAnimationHelper;
@@ -212,6 +218,7 @@
     protected final Rect mTempRect = new Rect();
     protected final RectF mTempRectF = new RectF();
     private final PointF mTempPointF = new PointF();
+    private final float[] mTempFloatPoint = new float[2];
 
     private static final int DISMISS_TASK_DURATION = 300;
     private static final int ADDITION_TASK_DURATION = 200;
@@ -225,7 +232,6 @@
     private final ClearAllButton mClearAllButton;
     private final Rect mClearAllButtonDeadZoneRect = new Rect();
     private final Rect mTaskViewDeadZoneRect = new Rect();
-    protected final AppWindowAnimationHelper mTempAppWindowAnimationHelper;
 
     private final ScrollState mScrollState = new ScrollState();
     // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
@@ -373,20 +379,19 @@
     };
 
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
-            boolean rotationSupportedByActivity) {
+            WindowSizeStrategy sizeStrategy) {
         super(context, attrs, defStyleAttr);
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
         setEnableFreeScroll(true);
+        mSizeStrategy = sizeStrategy;
         mOrientationState = new RecentsOrientedState(
-                context, rotationSupportedByActivity, this::animateRecentsRotationInPlace);
+                context, mSizeStrategy, this::animateRecentsRotationInPlace);
 
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
         mActivity = BaseActivity.fromContext(context);
         mModel = RecentsModel.INSTANCE.get(context);
         mIdp = InvariantDeviceProfile.INSTANCE.get(context);
-        mTempAppWindowAnimationHelper =
-            new AppWindowAnimationHelper(getPagedViewOrientedState(), context);
 
         mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
                 .inflate(R.layout.overview_clear_all_button, this, false);
@@ -504,7 +509,7 @@
         mIPinnedStackAnimationListener.setActivity(mActivity);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
                 mIPinnedStackAnimationListener);
-        mOrientationState.init();
+        mOrientationState.initListeners();
     }
 
     @Override
@@ -519,7 +524,7 @@
         mIdp.removeOnChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
         mIPinnedStackAnimationListener.setActivity(null);
-        mOrientationState.destroy();
+        mOrientationState.destroyListeners();
     }
 
     @Override
@@ -545,6 +550,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) {
@@ -595,20 +607,20 @@
     }
 
     @Override
-    protected void onPageEndTransition() {
-        super.onPageEndTransition();
-        if (getNextPage() > 0) {
-            setSwipeDownShouldLaunchApp(true);
-        }
+    protected void onPageBeginTransition() {
+        super.onPageBeginTransition();
+        LayoutUtils.setViewEnabled(mActionsView, false);
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        int windowConfigurationRotation = ConfigurationCompat
-                .getWindowConfigurationRotation(getResources().getConfiguration());
-        setLayoutInternal(mOrientationState.getTouchRotation(),
-                mOrientationState.getDisplayRotation(), windowConfigurationRotation);
+    protected void onPageEndTransition() {
+        super.onPageEndTransition();
+        if (getScrollX() == getScrollForPage(getPageNearestToCenterOfScreen())) {
+            LayoutUtils.setViewEnabled(mActionsView, true);
+        }
+        if (getNextPage() > 0) {
+            setSwipeDownShouldLaunchApp(true);
+        }
     }
 
     @Override
@@ -637,7 +649,7 @@
             case MotionEvent.ACTION_DOWN:
                 // Touch down anywhere but the deadzone around the visible clear all button and
                 // between the task views will start home on touch up
-                if (!isHandlingTouch()) {
+                if (!isHandlingTouch() && !isModal()) {
                     if (mShowEmptyMessage) {
                         mTouchDownToStartHome = true;
                     } else {
@@ -665,7 +677,7 @@
     @Override
     protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
         // Enables swiping to the left or right only if the task overlay is not modal.
-        if (mTaskModalness == 0f) {
+        if (!isModal()) {
             super.determineScrollingStart(ev, touchSlopScale);
         }
     }
@@ -712,7 +724,7 @@
             final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
             final Task task = tasks.get(i);
             final TaskView taskView = (TaskView) getChildAt(pageIndex);
-            taskView.bind(task, mOrientationState, mActivity.getDeviceProfile().isMultiWindowMode);
+            taskView.bind(task, mOrientationState);
         }
 
         if (mNextPage == INVALID_PAGE) {
@@ -736,6 +748,10 @@
         updateEnabledOverlays();
     }
 
+    private boolean isModal() {
+        return mTaskModalness > 0;
+    }
+
     private void removeTasksViewsAndClearAllButton() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
             removeView(getTaskViewAt(i));
@@ -753,7 +769,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--) {
@@ -784,7 +803,10 @@
         for (int i = 0; i < taskCount; i++) {
             getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
         }
-        mActionsView.updateHiddenFlags(HIDDEN_FULLESCREEN_PROGRESS, fullscreenProgress > 0);
+        // Fade out the actions view quickly (0.1 range)
+        mActionsView.getVisibilityAlpha().setValue(
+                mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
+        mActionsView.updateHiddenFlags(HIDDEN_FULLESCREEN_PROGRESS, fullscreenProgress == 1.0f);
     }
 
     private void updateTaskStackListenerState() {
@@ -802,7 +824,8 @@
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         DeviceProfile dp = mActivity.getDeviceProfile();
-        getTaskSize(dp, mTempRect);
+        mOrientationState.setMultiWindowMode(dp.isMultiWindowMode);
+        getTaskSize(mTempRect);
         mTaskWidth = mTempRect.width();
         mTaskHeight = mTempRect.height();
 
@@ -812,10 +835,8 @@
                 dp.heightPx - mInsets.bottom - mTempRect.bottom);
     }
 
-    protected abstract void getTaskSize(DeviceProfile dp, Rect outRect);
-
     public void getTaskSize(Rect outRect) {
-        getTaskSize(mActivity.getDeviceProfile(), outRect);
+        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
     }
 
     @Override
@@ -949,6 +970,7 @@
         setCurrentPage(0);
         mDwbToastShown = false;
         mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
+        LayoutUtils.setViewEnabled(mActionsView, true);
     }
 
     public @Nullable TaskView getRunningTaskView() {
@@ -956,7 +978,15 @@
     }
 
     public int getRunningTaskIndex() {
-        TaskView tv = getRunningTaskView();
+        return getTaskIndexForId(mRunningTaskId);
+    }
+
+    /**
+     * Get the index of the task view whose id matches {@param taskId}.
+     * @return -1 if there is no task view for the task id, else the index of the task view.
+     */
+    public int getTaskIndexForId(int taskId) {
+        TaskView tv = getTaskView(taskId);
         return tv == null ? -1 : indexOfChild(tv);
     }
 
@@ -1044,7 +1074,7 @@
         }
         setRunningTaskHidden(false);
         animateUpRunningTaskIconScale();
-        mActionsView.updateHiddenFlags(HIDDEN_GESTURE_RUNNING, false);
+        animateActionsViewIn();
     }
 
     /**
@@ -1068,8 +1098,7 @@
                     new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
                     false, true, false, false, new ActivityManager.TaskDescription(), 0,
                     new ComponentName("", ""), false);
-            taskView.bind(mTmpRunningTask, mOrientationState,
-                    mActivity.getDeviceProfile().isMultiWindowMode);
+            taskView.bind(mTmpRunningTask, mOrientationState);
         }
 
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -1158,6 +1187,14 @@
         }
     }
 
+    private void animateActionsViewIn() {
+        mActionsView.updateHiddenFlags(HIDDEN_GESTURE_RUNNING, false);
+        ObjectAnimator anim = ObjectAnimator.ofFloat(
+                mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 1);
+        anim.setDuration(OverviewActionsView.VISIBILITY_TRANSITION_DURATION_MS);
+        anim.start();
+    }
+
     public void animateUpRunningTaskIconScale() {
         animateUpRunningTaskIconScale(0);
     }
@@ -1278,7 +1315,8 @@
             ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     endState.logAction, Direction.UP, index, compKey);
-            mActivity.getStatsLogManager().log(TASK_DISMISS_SWIPE_UP, taskView.buildProto());
+            mActivity.getStatsLogManager().log(
+                    LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.buildProto());
         }
     }
 
@@ -1558,19 +1596,14 @@
     }
 
     public void setLayoutRotation(int touchRotation, int displayRotation) {
-        int launcherRotation = mOrientationState.getLauncherRotation();
-        setLayoutInternal(touchRotation, displayRotation, launcherRotation);
-    }
-
-    private void setLayoutInternal(int touchRotation, int displayRotation, int launcherRotation) {
-        if (mOrientationState.update(touchRotation, displayRotation, launcherRotation)) {
+        if (mOrientationState.update(touchRotation, displayRotation)) {
             mOrientationHandler = mOrientationState.getOrientationHandler();
             mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
             setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
             mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
             mActivity.getDragLayer().recreateControllers();
             mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
-                    touchRotation != 0 || launcherRotation != 0);
+                    touchRotation != 0 || mOrientationState.getLauncherRotation() != ROTATION_0);
             requestLayout();
         }
     }
@@ -1653,6 +1686,7 @@
                 mTempRect, mActivity.getDeviceProfile(), mTempPointF);
         setPivotX(mTempPointF.x);
         setPivotY(mTempPointF.y);
+        setTaskModalness(mTaskModalness);
         updatePageOffsets();
     }
 
@@ -1665,7 +1699,8 @@
         }
         int count = getChildCount();
 
-        TaskView runningTask = mRunningTaskId == -1 ? null : getTaskView(mRunningTaskId);
+        TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
+                ? null : getTaskView(mRunningTaskId);
         int midPoint = runningTask == null ? -1 : indexOfChild(runningTask);
         int currentPage = getCurrentPage();
 
@@ -1756,15 +1791,14 @@
      * If launching one of the adjacent tasks, parallax the center task and other adjacent task
      * to the right.
      */
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(
-            TaskView tv, AppWindowAnimationHelper appWindowAnimationHelper) {
+    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
         AnimatorSet anim = new AnimatorSet();
 
         int taskIndex = indexOfChild(tv);
         int centerTaskIndex = getCurrentPage();
         boolean launchingCenterTask = taskIndex == centerTaskIndex;
 
-        float toScale = appWindowAnimationHelper.getSrcToTargetScale();
+        float toScale = getMaxScaleForFullScreen();
         if (launchingCenterTask) {
             RecentsView recentsView = tv.getRecentsView();
             anim.play(ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, toScale));
@@ -1786,6 +1820,15 @@
         return anim;
     }
 
+    /**
+     * Returns the scale up required on the view, so that it coves the screen completely
+     */
+    public float getMaxScaleForFullScreen() {
+        getTaskSize(mTempRect);
+        return getPagedViewOrientedState().getFullScreenScaleAndPivot(
+                mTempRect, mActivity.getDeviceProfile(), mTempPointF);
+    }
+
     public PendingAnimation createTaskLaunchAnimation(
             TaskView tv, long duration, Interpolator interpolator) {
         if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
@@ -1820,11 +1863,7 @@
             }
         });
 
-        AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper(
-            getPagedViewOrientedState(), mActivity);
-        appWindowAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this);
-        appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
-        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
+        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv);
 
         DepthController depthController = getDepthController();
         if (depthController != null) {
@@ -1851,8 +1890,8 @@
                     mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                             endState.logAction, Direction.DOWN, indexOfChild(tv),
                             TaskUtils.getLaunchComponentKeyForTask(task.key));
-                    mActivity.getStatsLogManager().log(TASK_LAUNCH_SWIPE_DOWN, tv.buildProto()
-                    );
+                    mActivity.getStatsLogManager().log(
+                            LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.buildProto());
                 }
             } else {
                 onTaskLaunched(false);
@@ -2038,14 +2077,6 @@
         return getScrollForPage(getRunningTaskIndex()) - mOrientationHandler.getPrimaryScroll(this);
     }
 
-    /**
-     * @return How many pixels the running task is offset on the x-axis due to the current scrollX
-     * and parent scale.
-     */
-    public float getScrollOffsetScaled() {
-        return getScrollOffset() * mOrientationHandler.getPrimaryScale(this);
-    }
-
     public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
         float degreesRotated;
         if (navbarRotation == 0) {
@@ -2079,11 +2110,7 @@
         return mAppWindowAnimationHelper;
     }
 
-    public AppWindowAnimationHelper getTempAppWindowAnimationHelper() {
-        return mTempAppWindowAnimationHelper;
-    }
-
-    public AppWindowAnimationHelper.TransformParams getLiveTileParams(
+    public TransformParams getLiveTileParams(
             boolean mightNeedToRefill) {
         return null;
     }
@@ -2128,6 +2155,18 @@
         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);
         }
     }
 
@@ -2137,6 +2176,12 @@
     }
 
     /**
+     * Enables or disables modal state for RecentsView
+     * @param isModalState
+     */
+    public void setModalStateEnabled(boolean isModalState) { }
+
+    /**
      * Used to register callbacks for when our empty message state changes.
      *
      * @see #setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index 9b47520..512bbac 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -239,7 +239,8 @@
         setLayoutParams(params);
         setScaleX(taskView.getScaleX());
         setScaleY(taskView.getScaleY());
-        mOptionLayout.setOrientation(orientationHandler.getTaskMenuLayoutOrientation());
+        mOptionLayout.setOrientation(orientationHandler
+                .getTaskMenuLayoutOrientation(mOptionLayout));
         setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top,
             taskView.getPagedOrientationHandler());
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index e525842..a3e360f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -36,12 +36,12 @@
 import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
-import android.util.Log;
 import android.util.Property;
 import android.view.Surface;
 import android.view.View;
 
 import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -90,7 +90,7 @@
 
     // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0.
     private final Rect mPreviewRect = new Rect();
-    private final PreviewPositionHelper mPreviewPositionHelper;
+    private final PreviewPositionHelper mPreviewPositionHelper = new PreviewPositionHelper();
     // Initialize with dummy value. It is overridden later by TaskView
     private TaskView.FullscreenDrawParams mFullscreenParams = TEMP_PARAMS;
 
@@ -104,7 +104,6 @@
 
     private boolean mOverlayEnabled;
     private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
-    private boolean mIsMultiWindowMode;
 
     public TaskThumbnailView(Context context) {
         this(context, null);
@@ -123,11 +122,13 @@
         mDimmingPaintAfterClearing.setColor(Color.BLACK);
         mActivity = BaseActivity.fromContext(context);
         mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
-        mPreviewPositionHelper = new PreviewPositionHelper(context);
     }
 
-    public void bind(Task task, boolean isMultiWindowMode) {
-        mIsMultiWindowMode = isMultiWindowMode;
+    /**
+     * Updates the thumbnail to draw the provided task
+     * @param task
+     */
+    public void bind(Task task) {
         mOverlay.reset();
         mTask = task;
         int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
@@ -193,11 +194,6 @@
         updateThumbnailPaintFilter();
     }
 
-    public void setSaturation(float saturation) {
-        mSaturation = saturation;
-        updateThumbnailPaintFilter();
-    }
-
     public TaskOverlay getTaskOverlay() {
         return mOverlay;
     }
@@ -352,8 +348,11 @@
         if (mBitmapShader != null && mThumbnailData != null) {
             mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
                     mThumbnailData.thumbnail.getHeight());
+            int currentRotation = ConfigurationCompat.getWindowConfigurationRotation(
+                    mActivity.getResources().getConfiguration());
             mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
-                    mIsMultiWindowMode, getMeasuredWidth(), getMeasuredHeight());
+                    getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
+                    currentRotation);
 
             mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix);
             mPaint.setShader(mBitmapShader);
@@ -420,17 +419,6 @@
         private float mClipBottom = -1;
         private boolean mIsOrientationChanged;
 
-        private final Context mContext;
-
-        public PreviewPositionHelper(Context context) {
-            mContext = context;
-        }
-
-        public int getCurrentRotation() {
-            return ConfigurationCompat.getWindowConfigurationRotation(
-                    mContext.getResources().getConfiguration());
-        }
-
         public Matrix getMatrix() {
             return mMatrix;
         }
@@ -439,13 +427,14 @@
          * Updates the matrix based on the provided parameters
          */
         public void updateThumbnailMatrix(Rect thumbnailPosition, ThumbnailData thumbnailData,
-                boolean isInMultiWindowMode, int canvasWidth, int canvasHeight) {
+                int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation) {
             boolean isRotated = false;
             boolean isOrientationDifferent;
             mClipBottom = -1;
 
             float scale = thumbnailData.scale;
-            Rect thumbnailInsets = thumbnailData.insets;
+            Rect activityInsets = dp.getInsets();
+            Rect thumbnailInsets = getBoundedInsets(activityInsets, thumbnailData.insets);
             final float thumbnailWidth = thumbnailPosition.width()
                     - (thumbnailInsets.left + thumbnailInsets.right) * scale;
             final float thumbnailHeight = thumbnailPosition.height()
@@ -453,11 +442,11 @@
 
             final float thumbnailScale;
             int thumbnailRotation = thumbnailData.rotation;
-            int currentRotation = getCurrentRotation();
             int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
 
+            Rect deviceInsets = dp.getInsets();
             // Landscape vs portrait change
-            boolean windowingModeSupportsRotation = !isInMultiWindowMode
+            boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
                     && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
             isOrientationDifferent = isOrientationChange(deltaRotate)
                     && windowingModeSupportsRotation;
@@ -476,9 +465,10 @@
 
             if (!isRotated) {
                 // No Rotation
-                mClippedInsets.offsetTo(thumbnailInsets.left * scale,
-                        thumbnailInsets.top * scale);
-                mMatrix.setTranslate(-mClippedInsets.left, -mClippedInsets.top);
+                mClippedInsets.offsetTo(deviceInsets.left * scale, deviceInsets.top * scale);
+                mMatrix.setTranslate(
+                        -thumbnailInsets.left * scale,
+                        -thumbnailInsets.top * scale);
             } else {
                 setThumbnailRotation(deltaRotate, thumbnailInsets, scale, thumbnailPosition);
             }
@@ -495,8 +485,16 @@
             }
             mClippedInsets.left *= thumbnailScale;
             mClippedInsets.top *= thumbnailScale;
-            mClippedInsets.right = widthWithInsets - mClippedInsets.left - canvasWidth;
-            mClippedInsets.bottom = heightWithInsets - mClippedInsets.top - canvasHeight;
+
+            if (dp.isMultiWindowMode) {
+                mClippedInsets.right = deviceInsets.right * scale * thumbnailScale;
+                mClippedInsets.bottom = deviceInsets.bottom * scale * thumbnailScale;
+            } else {
+                mClippedInsets.right = Math.max(0,
+                        widthWithInsets - mClippedInsets.left - canvasWidth);
+                mClippedInsets.bottom = Math.max(0,
+                        heightWithInsets - mClippedInsets.top - canvasHeight);
+            }
 
             mMatrix.postScale(thumbnailScale, thumbnailScale);
 
@@ -508,6 +506,13 @@
             mIsOrientationChanged = isOrientationDifferent;
         }
 
+        private Rect getBoundedInsets(Rect activityInsets, Rect insets) {
+            return new Rect(Math.min(insets.left, activityInsets.left),
+                    Math.min(insets.top, activityInsets.top),
+                    Math.min(insets.right, activityInsets.right),
+                    Math.min(insets.bottom, activityInsets.bottom));
+        }
+
         private int getRotationDelta(int oldRotation, int newRotation) {
             int delta = newRotation - oldRotation;
             if (delta < 0) delta += 4;
@@ -557,9 +562,8 @@
         /**
          * Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
          */
-        public RectF getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
-            // Don't show insets in multi window mode.
-            return isMultiWindowMode ? EMPTY_RECT_F : mClippedInsets;
+        public RectF getInsetsToDrawInFullscreen() {
+            return mClippedInsets;
         }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index aea5b8e..da9468e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -30,7 +30,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -212,7 +212,7 @@
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-            mActivity.getStatsLogManager().log(TASK_LAUNCH_TAP, buildProto());
+            mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, buildProto());
         });
 
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -274,10 +274,10 @@
      * TODO(b/142282126) Re-evaluate if we need to pass in isMultiWindowMode after
      *   that issue is fixed
      */
-    public void bind(Task task, RecentsOrientedState orientedState, boolean isMultiWindowMode) {
+    public void bind(Task task, RecentsOrientedState orientedState) {
         cancelPendingLoadTasks();
         mTask = task;
-        mSnapshotView.bind(task, isMultiWindowMode);
+        mSnapshotView.bind(task);
         setOrientationState(orientedState);
     }
 
@@ -448,14 +448,12 @@
     }
 
     public void setOrientationState(RecentsOrientedState orientationState) {
-        int iconRotation = orientationState.getTouchRotation();
         PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
         boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources());
         LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
         LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        int rotation = orientationState.getTouchRotationDegrees();
-        switch (iconRotation) {
+        switch (orientationHandler.getRotation()) {
             case Surface.ROTATION_90:
                 iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
                 iconParams.rightMargin = -thumbnailPadding;
@@ -480,7 +478,7 @@
                 break;
         }
         mIconView.setLayoutParams(iconParams);
-        mIconView.setRotation(rotation);
+        mIconView.setRotation(orientationHandler.getDegreesRotated());
 
         if (mMenuView != null) {
             mMenuView.onRotationChanged();
@@ -671,7 +669,7 @@
             mContextualChip.setScaleY(0f);
             GradientDrawable scrimDrawable = (GradientDrawable) getResources().getDrawable(
                     R.drawable.chip_scrim_gradient, mActivity.getTheme());
-            float cornerRadius = TaskCornerRadius.get(mActivity);
+            float cornerRadius = getTaskCornerRadius();
             scrimDrawable.setCornerRadii(
                     new float[]{0, 0, 0, 0, cornerRadius, cornerRadius, cornerRadius,
                             cornerRadius});
@@ -690,6 +688,10 @@
         }
     }
 
+    public float getTaskCornerRadius() {
+        return TaskCornerRadius.get(mActivity);
+    }
+
     /**
      * Clears the contextual chip from TaskView.
      *
@@ -1017,14 +1019,13 @@
          */
         public void setProgress(float fullscreenProgress, float parentScale, int previewWidth,
                 DeviceProfile dp, PreviewPositionHelper pph) {
-            boolean isMultiWindowMode = dp.isMultiWindowMode;
-            RectF insets = pph.getInsetsToDrawInFullscreen(isMultiWindowMode);
+            RectF insets = pph.getInsetsToDrawInFullscreen();
 
             float currentInsetsLeft = insets.left * fullscreenProgress;
             float currentInsetsRight = insets.right * fullscreenProgress;
             mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
                     currentInsetsRight, insets.bottom * fullscreenProgress);
-            float fullscreenCornerRadius = isMultiWindowMode ? 0 : mWindowCornerRadius;
+            float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius;
 
             mCurrentDrawnCornerRadius =
                     Utilities.mapRange(fullscreenProgress, mCornerRadius, fullscreenCornerRadius)
diff --git a/quickstep/res/drawable/assistant_gesture.xml b/quickstep/res/drawable/assistant_gesture.xml
new file mode 100644
index 0000000..ba4331c
--- /dev/null
+++ b/quickstep/res/drawable/assistant_gesture.xml
@@ -0,0 +1,989 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="412dp"
+            android:height="890dp"
+            android:viewportWidth="412"
+            android:viewportHeight="890">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_4_G_N_2_N_3_N_4_T_1"
+                    android:rotation="-10"
+                    android:translateX="661.757"
+                    android:translateY="1026.235">
+                    <group
+                        android:name="_R_G_L_4_G_N_2_N_3_N_4_T_0"
+                        android:translateX="-148.438"
+                        android:translateY="-239.65">
+                        <group
+                            android:name="_R_G_L_4_G_N_2_N_3_T_0"
+                            android:translateX="-61.73500000000001"
+                            android:translateY="38.257000000000005">
+                            <group
+                                android:name="_R_G_L_4_G_N_2_T_0"
+                                android:pivotX="83.124"
+                                android:pivotY="89.259"
+                                android:rotation="-16"
+                                android:translateX="-50.44799999999999"
+                                android:translateY="-62.925">
+                                <group
+                                    android:name="_R_G_L_4_G"
+                                    android:translateX="-18.21"
+                                    android:translateY="-17.394">
+                                    <path
+                                        android:name="_R_G_L_4_G_D_0_P_0"
+                                        android:fillAlpha="0"
+                                        android:fillColor="#e8f0fe"
+                                        android:fillType="nonZero"
+                                        android:pathData=" M96.25 48.25 C96.25,74.76 74.76,96.25 48.25,96.25 C21.74,96.25 0.25,74.76 0.25,48.25 C0.25,21.74 21.74,0.25 48.25,0.25 C74.76,0.25 96.25,21.74 96.25,48.25c " />
+                                </group>
+                            </group>
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_3_G"
+                    android:pivotX="48.25"
+                    android:pivotY="48.25"
+                    android:rotation="11"
+                    android:translateX="227.046"
+                    android:translateY="642.467">
+                    <path
+                        android:name="_R_G_L_3_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#e8f0fe"
+                        android:fillType="nonZero"
+                        android:pathData=" M96.25 48.25 C96.25,74.76 74.76,96.25 48.25,96.25 C21.74,96.25 0.25,74.76 0.25,48.25 C0.25,21.74 21.74,0.25 48.25,0.25 C74.76,0.25 96.25,21.74 96.25,48.25c " />
+                </group>
+                <group
+                    android:name="_R_G_L_2_G_T_1"
+                    android:rotation="-10"
+                    android:translateX="661.757"
+                    android:translateY="1026.235">
+                    <group
+                        android:name="_R_G_L_2_G"
+                        android:translateX="-148.438"
+                        android:translateY="-239.65">
+                        <path
+                            android:name="_R_G_L_2_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#d2e3fc"
+                            android:fillType="nonZero"
+                            android:pathData=" M14.25 224.54 C40.46,320.73 128.6,415.05 212.61,415.05 C296.63,415.05 188.65,199.66 188.65,99.96 C188.65,0.25 122.97,160.22 18.3,95.88 C1.02,85.21 0.25,173.17 14.25,224.54c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G_N_4_T_1"
+                    android:rotation="-10"
+                    android:translateX="661.757"
+                    android:translateY="1026.235">
+                    <group
+                        android:name="_R_G_L_1_G_N_4_T_0"
+                        android:translateX="-148.438"
+                        android:translateY="-239.65">
+                        <group
+                            android:name="_R_G_L_1_G"
+                            android:translateX="-61.73500000000001"
+                            android:translateY="38.257000000000005">
+                            <path
+                                android:name="_R_G_L_1_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="#d2e3fc"
+                                android:fillType="nonZero"
+                                android:pathData=" M60.8 0.25 C60.8,0.25 76.32,20.36 105.56,60.3 C134.8,100.25 207.8,82.25 207.8,82.25 C207.8,82.25 211.01,150.18 211.01,150.18 C211.01,150.18 81.43,193.5 81.43,193.5 C81.43,193.5 31.8,174.25 31.8,174.25 C31.8,174.25 27.8,109.25 22.8,89.25 C17.8,69.25 6.15,49.9 2.17,41.17 C0.25,36.95 13.88,27.25 28.48,18.35 C44.09,8.84 60.8,0.25 60.8,0.25c " />
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_N_3_N_4_T_1"
+                    android:rotation="-10"
+                    android:translateX="661.757"
+                    android:translateY="1026.235">
+                    <group
+                        android:name="_R_G_L_0_G_N_3_N_4_T_0"
+                        android:translateX="-148.438"
+                        android:translateY="-239.65">
+                        <group
+                            android:name="_R_G_L_0_G_N_3_T_0"
+                            android:translateX="-61.73500000000001"
+                            android:translateY="38.257000000000005">
+                            <group
+                                android:name="_R_G_L_0_G"
+                                android:pivotX="83.124"
+                                android:pivotY="89.259"
+                                android:rotation="-16"
+                                android:translateX="-50.44799999999999"
+                                android:translateY="-62.925">
+                                <path
+                                    android:name="_R_G_L_0_G_D_0_P_0"
+                                    android:fillAlpha="1"
+                                    android:fillColor="#d2e3fc"
+                                    android:fillType="nonZero"
+                                    android:pathData=" M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 76.45,102.74 64.42,104.51 C52.38,106.27 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c " />
+                                <path
+                                    android:name="_R_G_L_0_G_D_1_P_0"
+                                    android:pathData=" M57.29 26.52 C59.8,29.86 61.05,33.88 55.15,38.41 C55.15,38.41 49.91,42.03 36.73,51.93 C34.96,53.26 32.72,55.37 30.64,55.46 C28.92,55.55 26.91,54.65 25.14,51.9 C19.1,42.49 15,36.09 17.17,30.02 "
+                                    android:strokeWidth="6"
+                                    android:strokeAlpha="1"
+                                    android:strokeColor="#a0c2f9" />
+                                <path
+                                    android:name="_R_G_L_0_G_D_2_P_0"
+                                    android:pathData=" M64.41 90.04 C64.41,90.04 69.67,62.88 99.15,59.26 "
+                                    android:strokeWidth="6"
+                                    android:strokeAlpha="1"
+                                    android:strokeColor="#a0c2f9" />
+                            </group>
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_4_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="233"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="333"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_4_G_N_2_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-16"
+                    android:valueTo="-16"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="783"
+                    android:propertyName="rotation"
+                    android:startOffset="450"
+                    android:valueFrom="-16"
+                    android:valueTo="11"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="1233"
+                    android:valueFrom="11"
+                    android:valueTo="11"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="rotation"
+                    android:startOffset="1683"
+                    android:valueFrom="11"
+                    android:valueTo="-16"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_4_G_N_2_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="661.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="661.757"
+                    android:valueTo="493.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateX"
+                    android:startOffset="1233"
+                    android:valueFrom="493.757"
+                    android:valueTo="497.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateX"
+                    android:startOffset="1683"
+                    android:valueFrom="497.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_4_G_N_2_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="1026.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="1026.235"
+                    android:valueTo="933.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateY"
+                    android:startOffset="1233"
+                    android:valueFrom="933.235"
+                    android:valueTo="939.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateY"
+                    android:startOffset="1683"
+                    android:valueFrom="939.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_4_G_N_2_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-10"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-10"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="1233"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="rotation"
+                    android:startOffset="1683"
+                    android:valueFrom="0"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_4_G_N_2_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleY"
+                    android:startOffset="1233"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="383"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="1233"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleX"
+                    android:startOffset="1233"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="661.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="661.757"
+                    android:valueTo="493.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateX"
+                    android:startOffset="1233"
+                    android:valueFrom="493.757"
+                    android:valueTo="497.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateX"
+                    android:startOffset="1683"
+                    android:valueFrom="497.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="1026.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="1026.235"
+                    android:valueTo="933.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateY"
+                    android:startOffset="1233"
+                    android:valueFrom="933.235"
+                    android:valueTo="939.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateY"
+                    android:startOffset="1683"
+                    android:valueFrom="939.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-10"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-10"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="1233"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="rotation"
+                    android:startOffset="1683"
+                    android:valueFrom="0"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="661.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="661.757"
+                    android:valueTo="493.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateX"
+                    android:startOffset="1233"
+                    android:valueFrom="493.757"
+                    android:valueTo="497.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateX"
+                    android:startOffset="1683"
+                    android:valueFrom="497.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="1026.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="1026.235"
+                    android:valueTo="933.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateY"
+                    android:startOffset="1233"
+                    android:valueFrom="933.235"
+                    android:valueTo="939.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateY"
+                    android:startOffset="1683"
+                    android:valueFrom="939.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-10"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-10"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="1233"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="rotation"
+                    android:startOffset="1683"
+                    android:valueFrom="0"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 76.45,102.74 64.42,104.51 C52.38,106.27 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueTo="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 76.45,102.74 64.42,104.51 C52.38,106.27 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="783"
+                    android:propertyName="pathData"
+                    android:startOffset="450"
+                    android:valueFrom="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 76.45,102.74 64.42,104.51 C52.38,106.27 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueTo="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 103.84,114.98 73.16,119.19 C61.1,120.84 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="pathData"
+                    android:startOffset="1233"
+                    android:valueFrom="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 103.84,114.98 73.16,119.19 C61.1,120.84 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueTo="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 103.84,114.98 73.16,119.19 C61.1,120.84 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="pathData"
+                    android:startOffset="1683"
+                    android:valueFrom="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 103.84,114.98 73.16,119.19 C61.1,120.84 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueTo="M19.19 14.59 C40.48,0.25 55.28,10.31 73.92,28.68 C92.56,47.05 106.44,53.33 112.22,64.44 C118,75.56 76.45,102.74 64.42,104.51 C52.38,106.27 30.98,101.06 19.76,76.77 C8.07,51.48 0.54,27.15 19.19,14.59c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-16"
+                    android:valueTo="-16"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="783"
+                    android:propertyName="rotation"
+                    android:startOffset="450"
+                    android:valueFrom="-16"
+                    android:valueTo="11"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="1233"
+                    android:valueFrom="11"
+                    android:valueTo="11"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="rotation"
+                    android:startOffset="1683"
+                    android:valueFrom="11"
+                    android:valueTo="-16"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="661.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="661.757"
+                    android:valueTo="493.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateX"
+                    android:startOffset="1233"
+                    android:valueFrom="493.757"
+                    android:valueTo="497.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateX"
+                    android:startOffset="1683"
+                    android:valueFrom="497.757"
+                    android:valueTo="661.757"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="1026.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="1026.235"
+                    android:valueTo="933.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="translateY"
+                    android:startOffset="1233"
+                    android:valueFrom="933.235"
+                    android:valueTo="939.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="translateY"
+                    android:startOffset="1683"
+                    android:valueFrom="939.235"
+                    android:valueTo="1026.235"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.303,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_3_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-10"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="900"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-10"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="rotation"
+                    android:startOffset="1233"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1400"
+                    android:propertyName="rotation"
+                    android:startOffset="1683"
+                    android:valueFrom="0"
+                    android:valueTo="-10"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.205,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="3167"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/back_gesture.xml b/quickstep/res/drawable/back_gesture.xml
index a5c57b4..18ad2cb 100644
--- a/quickstep/res/drawable/back_gesture.xml
+++ b/quickstep/res/drawable/back_gesture.xml
@@ -1,100 +1,121 @@
-<!--
-    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.
--->
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt">
     <aapt:attr name="android:drawable">
         <vector
-            android:width="206dp"
-            android:height="435dp"
-            android:viewportWidth="206"
-            android:viewportHeight="435">
-            <group android:name="edgeGroup"
-                android:translateX="197"
-                android:translateY="0">
-                <path
-                    android:name="edge"
-                    android:fillAlpha="0"
-                    android:fillType="nonZero"
-                    android:fillColor="#1a73eb"
-                    android:pathData=" M0,0 h9 v435 h-9 z " />
-            </group>
-            <group
-                android:name="trailGroup"
-                android:translateX="226"
-                android:translateY="200">
-                <path
-                    android:name="trail"
-                    android:fillAlpha="1"
-                    android:fillType="nonZero"
-                    android:pathData=" M0,0 h55 v36 h-55 z ">
-                    <aapt:attr name="android:fillColor">
-                        <gradient
-                            android:startX="0"
-                            android:endX="55"
-                            android:type="linear">
-                            <item
-                                android:color="#991a73eb"
-                                android:offset="0" />
-                            <item
-                                android:color="#401a73eb"
-                                android:offset="0.5" />
-                            <item
-                                android:color="#001a73eb"
-                                android:offset="1" />
-                        </gradient>
-                    </aapt:attr>
-                </path>
-            </group>
+            android:width="412dp"
+            android:height="890dp"
+            android:viewportWidth="412"
+            android:viewportHeight="890">
             <group android:name="_R_G">
                 <group
-                    android:name="_R_G_L_0_G_T_1"
-                    android:rotation="11"
-                    android:scaleX="0.9"
-                    android:scaleY="0.9"
-                    android:translateX="309"
-                    android:translateY="422.5">
+                    android:name="_R_G_L_3_G_T_1"
+                    android:rotation="29"
+                    android:translateX="400.931"
+                    android:translateY="449.112">
                     <group
-                        android:name="_R_G_L_0_G"
-                        android:translateX="-145"
-                        android:translateY="-208">
+                        android:name="_R_G_L_3_G"
+                        android:translateX="-51.449"
+                        android:translateY="-51.449">
                         <path
-                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:name="_R_G_L_3_G_D_0_P_0"
+                            android:fillAlpha="0"
+                            android:fillColor="#e8f0fe"
+                            android:fillType="nonZero"
+                            android:pathData=" M98.99 58.07 C95.33,84.33 71.08,102.65 44.83,98.99 C18.57,95.33 0.25,71.08 3.91,44.83 C7.57,18.57 31.82,0.25 58.07,3.91 C84.33,7.57 102.65,31.82 98.99,58.07c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_2_G_T_1"
+                    android:rotation="22"
+                    android:translateX="443.275"
+                    android:translateY="741.789">
+                    <group
+                        android:name="_R_G_L_2_G"
+                        android:translateX="-199.299"
+                        android:translateY="-310.469">
+                        <path
+                            android:name="_R_G_L_2_G_D_0_P_0"
                             android:fillAlpha="1"
                             android:fillColor="#d2e3fc"
                             android:fillType="nonZero"
-                            android:pathData=" M12.5 -47 C-7.93,-41.24 -3,-20.5 -1.5,-7 C0,6.5 2.5,22 9,39.5 C13.52,51.67 17.06,63.52 19,113 C21,164 53.5,243.5 53.5,243.5 C53.5,243.5 59,275.5 123.5,326 C188,376.5 283.5,236 290.5,199 C297.5,162 194.5,80 149,73 C103.5,66 90.5,57.5 77,50 C63.5,42.5 57,27 54.5,13.5 C52,0 43.5,-15 40,-25 C36.5,-35 32,-52.5 12.5,-47c " />
-                        <path
-                            android:name="_R_G_L_0_G_D_1_P_0"
-                            android:pathData=" M4.45 -34.66 C4.45,-34.66 10.5,-12.66 10.5,-12.66 C11.24,-9.98 13.98,-8.38 16.67,-9.04 C16.67,-9.04 29.72,-12.27 29.72,-12.27 C32.39,-12.93 34.05,-15.59 33.47,-18.28 C33.47,-18.28 32.11,-24.57 32.11,-24.57 "
-                            android:strokeWidth="4"
-                            android:strokeAlpha="1"
-                            android:strokeColor="#a0c2f9" />
-                        <path
-                            android:name="_R_G_L_0_G_D_2_P_0"
-                            android:pathData=" M18.35 21.81 C21.41,17.24 36.97,10.77 44.63,13.55 "
-                            android:strokeWidth="4"
-                            android:strokeAlpha="1"
-                            android:strokeColor="#a0c2f9" />
+                            android:pathData=" M98.53 346.74 C150.31,507.99 279.63,534.69 366.99,534.69 C454.35,534.69 342.13,310.99 342.13,207.18 C342.13,103.37 244.29,336.77 102.92,212.59 C84.64,201.99 83.91,293.37 98.53,346.74c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G_N_2_N_4_T_1"
+                    android:rotation="22"
+                    android:translateX="443.275"
+                    android:translateY="741.789">
+                    <group
+                        android:name="_R_G_L_1_G_N_2_N_4_T_0"
+                        android:translateX="-199.299"
+                        android:translateY="-310.469">
+                        <group
+                            android:name="_R_G_L_1_G_N_2_T_0"
+                            android:translateX="32.843"
+                            android:translateY="70.37599999999998">
+                            <group
+                                android:name="_R_G_L_1_G"
+                                android:pivotX="63.901"
+                                android:pivotY="99.512"
+                                android:rotation="-7"
+                                android:translateX="-28.42"
+                                android:translateY="-66.62700000000001">
+                                <path
+                                    android:name="_R_G_L_1_G_D_0_P_0"
+                                    android:fillAlpha="1"
+                                    android:fillColor="#d2e3fc"
+                                    android:fillType="nonZero"
+                                    android:pathData=" M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c " />
+                                <path
+                                    android:name="_R_G_L_1_G_D_1_P_0"
+                                    android:pathData=" M62.63 26.42 C64.51,30.16 64.56,33.23 61.29,34.92 C61.29,34.92 57.3,38.02 54.34,39.55 C52.36,40.56 31.95,52.96 29.89,52.69 C28.18,52.46 25.13,43.84 23.56,39.57 C19.67,28.95 23.51,23 30.85,17.22 "
+                                    android:strokeWidth="6"
+                                    android:strokeAlpha="1"
+                                    android:strokeColor="#a0c2f9" />
+                            </group>
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_N_4_T_1"
+                    android:rotation="22"
+                    android:translateX="443.275"
+                    android:translateY="741.789">
+                    <group
+                        android:name="_R_G_L_0_G_N_4_T_0"
+                        android:translateX="-199.299"
+                        android:translateY="-310.469">
+                        <group
+                            android:name="_R_G_L_0_G"
+                            android:translateX="32.843"
+                            android:translateY="70.37599999999998">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="#d2e3fc"
+                                android:fillType="nonZero"
+                                android:pathData=" M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 36.96,112.53 21.91,82.02 C12.91,63.77 2.48,47.19 -0.01,35.78 C-0.24,33.88 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c " />
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:pathData=" M37.4 46.68 C37.4,46.68 53.75,25.93 70.25,28.88 "
+                                android:strokeWidth="6"
+                                android:strokeAlpha="1"
+                                android:strokeColor="#a0c2f9" />
+                            <path
+                                android:name="_R_G_L_0_G_D_2_P_0"
+                                android:pathData=" M157.71 101.59 C157.71,101.59 165.04,114.63 190.3,113 "
+                                android:strokeWidth="6"
+                                android:strokeAlpha="1"
+                                android:strokeColor="#a0c2f9" />
+                        </group>
                     </group>
                 </group>
             </group>
             <group android:name="time_group" />
         </vector>
     </aapt:attr>
-    <target android:name="edge">
+    <target android:name="_R_G_L_3_G_D_0_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
@@ -102,44 +123,27 @@
                     android:propertyName="fillAlpha"
                     android:startOffset="0"
                     android:valueFrom="0"
-                    android:valueTo="0.2"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="917"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="333"
-                    android:valueFrom="0.2"
-                    android:valueTo="0.2"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="583"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1250"
-                    android:valueFrom="0.2"
                     android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
                         <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="trail">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
                 <objectAnimator
-                    android:duration="2000"
+                    android:duration="167"
                     android:propertyName="fillAlpha"
-                    android:startOffset="0"
+                    android:startOffset="333"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="500"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="500"
                     android:valueFrom="1"
                     android:valueTo="1"
                     android:valueType="floatType">
@@ -148,9 +152,9 @@
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="850"
+                    android:duration="367"
                     android:propertyName="fillAlpha"
-                    android:startOffset="2000"
+                    android:startOffset="1000"
                     android:valueFrom="1"
                     android:valueTo="0"
                     android:valueType="floatType">
@@ -161,51 +165,468 @@
             </set>
         </aapt:attr>
     </target>
-    <target android:name="trailGroup">
+    <target android:name="_R_G_L_3_G_T_1">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="83"
+                    android:duration="333"
                     android:propertyName="translateX"
-                    android:startOffset="1250"
-                    android:valueFrom="226"
-                    android:valueTo="226"
+                    android:startOffset="0"
+                    android:valueFrom="400.931"
+                    android:valueTo="400.931"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.285,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
+                    android:duration="667"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="400.931"
+                    android:valueTo="232.931"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="443.275"
+                    android:valueTo="443.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="443.275"
+                    android:valueTo="403.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateX"
+                    android:startOffset="1000"
+                    android:valueFrom="403.275"
+                    android:valueTo="403.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="translateX"
+                    android:startOffset="1600"
+                    android:valueFrom="403.275"
+                    android:valueTo="443.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateY"
+                    android:startOffset="1000"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="translateY"
+                    android:startOffset="1600"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="22"
+                    android:valueTo="22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="22"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1000"
+                    android:valueFrom="0"
+                    android:valueTo="2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.324,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="rotation"
+                    android:startOffset="1600"
+                    android:valueFrom="2"
+                    android:valueTo="22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
                     android:duration="1000"
-                    android:propertyName="translateX"
-                    android:startOffset="1333"
-                    android:valueFrom="226"
-                    android:valueTo="151"
-                    android:valueType="floatType">
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c "
+                    android:valueTo="M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c "
+                    android:valueType="pathType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.285,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="517"
-                    android:propertyName="translateX"
-                    android:startOffset="2333"
-                    android:valueFrom="151"
-                    android:valueTo="151"
-                    android:valueType="floatType">
+                    android:duration="600"
+                    android:propertyName="pathData"
+                    android:startOffset="1000"
+                    android:valueFrom="M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c "
+                    android:valueTo="M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c "
+                    android:valueType="pathType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.285,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="50"
-                    android:propertyName="translateX"
-                    android:startOffset="2850"
-                    android:valueFrom="226"
-                    android:valueTo="226"
+                    android:duration="1067"
+                    android:propertyName="pathData"
+                    android:startOffset="1600"
+                    android:valueFrom="M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c "
+                    android:valueTo="M24.09 7.19 C24.09,7.19 24.09,7.19 24.09,7.19 C37.34,0.25 53.74,5.21 61.53,18.2 C61.53,18.2 92.06,67.99 92.06,67.99 C99.55,85.1 94.55,109.83 77.93,118.03 C77.93,118.03 73.41,119.96 73.41,119.96 C54.93,128.77 33.79,118.95 27.61,98.93 C27.61,98.93 9.41,43.77 9.41,43.77 C4.59,29.86 10.87,13.76 24.09,7.19c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M62.63 26.42 C64.51,30.16 64.56,33.23 61.29,34.92 C61.29,34.92 57.3,38.02 54.34,39.55 C52.36,40.56 31.95,52.96 29.89,52.69 C28.18,52.46 25.13,43.84 23.56,39.57 C19.67,28.95 23.51,23 30.85,17.22 "
+                    android:valueTo="M62.63 26.42 C64.51,30.16 64.56,33.23 61.29,34.92 C61.29,34.92 57.3,38.02 54.34,39.55 C52.36,40.56 31.95,52.96 29.89,52.69 C28.18,52.46 25.13,43.84 23.56,39.57 C19.67,28.95 23.51,23 30.85,17.22 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M62.63 26.42 C64.51,30.16 64.56,33.23 61.29,34.92 C61.29,34.92 57.3,38.02 54.34,39.55 C52.36,40.56 31.95,52.96 29.89,52.69 C28.18,52.46 25.13,43.84 23.56,39.57 C19.67,28.95 23.51,23 30.85,17.22 "
+                    android:valueTo="M58.31 29.65 C60.19,33.38 60.72,37.56 54.12,40.99 C54.12,40.99 48.32,43.62 33.61,51.06 C31.63,52.06 29.06,53.73 26.99,53.46 C25.28,53.24 23.46,52.01 22.2,48.99 C17.92,38.66 15,31.64 18.2,26.05 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="pathData"
+                    android:startOffset="1000"
+                    android:valueFrom="M58.31 29.65 C60.19,33.38 60.72,37.56 54.12,40.99 C54.12,40.99 48.32,43.62 33.61,51.06 C31.63,52.06 29.06,53.73 26.99,53.46 C25.28,53.24 23.46,52.01 22.2,48.99 C17.92,38.66 15,31.64 18.2,26.05 "
+                    android:valueTo="M58.31 29.65 C60.19,33.38 60.72,37.56 54.12,40.99 C54.12,40.99 48.32,43.62 33.61,51.06 C31.63,52.06 29.06,53.73 26.99,53.46 C25.28,53.24 23.46,52.01 22.2,48.99 C17.92,38.66 15,31.64 18.2,26.05 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="pathData"
+                    android:startOffset="1600"
+                    android:valueFrom="M58.31 29.65 C60.19,33.38 60.72,37.56 54.12,40.99 C54.12,40.99 48.32,43.62 33.61,51.06 C31.63,52.06 29.06,53.73 26.99,53.46 C25.28,53.24 23.46,52.01 22.2,48.99 C17.92,38.66 15,31.64 18.2,26.05 "
+                    android:valueTo="M62.63 26.42 C64.51,30.16 64.56,33.23 61.29,34.92 C61.29,34.92 57.3,38.02 54.34,39.55 C52.36,40.56 31.95,52.96 29.89,52.69 C28.18,52.46 25.13,43.84 23.56,39.57 C19.67,28.95 23.51,23 30.85,17.22 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-7"
+                    android:valueTo="-7"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.285,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.315,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-7"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.315,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1000"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="rotation"
+                    android:startOffset="1600"
+                    android:valueFrom="0"
+                    android:valueTo="-7"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_2_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="443.275"
+                    android:valueTo="443.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="443.275"
+                    android:valueTo="403.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateX"
+                    android:startOffset="1000"
+                    android:valueFrom="403.275"
+                    android:valueTo="403.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="translateX"
+                    android:startOffset="1600"
+                    android:valueFrom="403.275"
+                    android:valueTo="443.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_2_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateY"
+                    android:startOffset="1000"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="translateY"
+                    android:startOffset="1600"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_2_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="22"
+                    android:valueTo="22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="22"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1000"
+                    android:valueFrom="0"
+                    android:valueTo="2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.324,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="rotation"
+                    android:startOffset="1600"
+                    android:valueFrom="2"
+                    android:valueTo="22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -215,25 +636,47 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="1833"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="1250"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 36.96,112.53 21.91,82.02 C12.91,63.77 2.48,47.19 -0.01,35.78 C-0.24,33.88 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueTo="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 36.96,112.53 21.91,82.02 C12.91,63.77 2.48,47.19 -0.01,35.78 C-0.24,33.88 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueType="pathType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="167"
-                    android:propertyName="fillAlpha"
-                    android:startOffset="3083"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                    android:duration="667"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 36.96,112.53 21.91,82.02 C12.91,63.77 2.48,47.19 -0.01,35.78 C-0.24,33.88 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueTo="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 39,110.89 30.58,91.46 C22.47,72.8 10.93,51.25 2.97,40.51 C2.75,38.61 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueType="pathType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="pathData"
+                    android:startOffset="1000"
+                    android:valueFrom="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 39,110.89 30.58,91.46 C22.47,72.8 10.93,51.25 2.97,40.51 C2.75,38.61 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueTo="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 39,110.89 30.58,91.46 C22.47,72.8 10.93,51.25 2.97,40.51 C2.75,38.61 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="pathData"
+                    android:startOffset="1600"
+                    android:valueFrom="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 39,110.89 30.58,91.46 C22.47,72.8 10.93,51.25 2.97,40.51 C2.75,38.61 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueTo="M46.18 2.25 C46.18,2.25 63.07,0.46 63.07,0.46 C71.25,13.11 74.51,28.4 84.08,41.9 C107.62,73.73 129.25,98.04 175.54,101.59 C278.31,96.7 179.52,210.03 162.67,266.4 C162.67,266.4 58.91,211.6 58.91,211.6 C56.64,197.88 36.96,112.53 21.91,82.02 C12.91,63.77 2.48,47.19 -0.01,35.78 C-0.24,33.88 0.25,27.89 12.82,15.25 C21.14,6.07 34.31,0.25 46.18,2.25c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.269,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -243,109 +686,197 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="1833"
-                    android:propertyName="strokeAlpha"
-                    android:startOffset="1250"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M37.4 46.68 C37.4,46.68 53.75,25.93 70.25,28.88 "
+                    android:valueTo="M37.4 46.68 C37.4,46.68 53.75,25.93 70.25,28.88 "
+                    android:valueType="pathType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="100"
-                    android:propertyName="strokeAlpha"
-                    android:startOffset="3083"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                    android:duration="667"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M37.4 46.68 C37.4,46.68 53.75,25.93 70.25,28.88 "
+                    android:valueTo="M29.25 53.2 C29.25,53.2 40.54,27.94 70.07,31.11 "
+                    android:valueType="pathType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="pathData"
+                    android:startOffset="1000"
+                    android:valueFrom="M29.25 53.2 C29.25,53.2 40.54,27.94 70.07,31.11 "
+                    android:valueTo="M29.25 53.2 C29.25,53.2 40.54,27.94 70.07,31.11 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="pathData"
+                    android:startOffset="1600"
+                    android:valueFrom="M29.25 53.2 C29.25,53.2 40.54,27.94 70.07,31.11 "
+                    android:valueTo="M37.4 46.68 C37.4,46.68 53.75,25.93 70.25,28.88 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.269,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_0_G_D_2_P_0">
+    <target android:name="_R_G_L_0_G_N_4_T_1">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="1833"
-                    android:propertyName="strokeAlpha"
-                    android:startOffset="1250"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="strokeAlpha"
-                    android:startOffset="3083"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_T_1">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
+                    android:duration="333"
                     android:propertyName="translateX"
-                    android:startOffset="1250"
-                    android:valueFrom="309"
-                    android:valueTo="309"
+                    android:startOffset="0"
+                    android:valueFrom="443.275"
+                    android:valueTo="443.275"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.285,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="1417"
+                    android:duration="667"
                     android:propertyName="translateX"
-                    android:startOffset="1333"
-                    android:valueFrom="309"
-                    android:valueTo="251"
+                    android:startOffset="333"
+                    android:valueFrom="443.275"
+                    android:valueTo="403.275"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.285,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateX"
+                    android:startOffset="1000"
+                    android:valueFrom="403.275"
+                    android:valueTo="403.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="translateX"
+                    android:startOffset="1600"
+                    android:valueFrom="403.275"
+                    android:valueTo="443.275"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_0_G_T_1">
+    <target android:name="_R_G_L_0_G_N_4_T_1">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="83"
-                    android:propertyName="rotation"
-                    android:startOffset="1250"
-                    android:valueFrom="11"
-                    android:valueTo="11"
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.277,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
                 <objectAnimator
-                    android:duration="1417"
+                    android:duration="667"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateY"
+                    android:startOffset="1000"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="translateY"
+                    android:startOffset="1600"
+                    android:valueFrom="741.789"
+                    android:valueTo="741.789"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_N_4_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
                     android:propertyName="rotation"
-                    android:startOffset="1333"
-                    android:valueFrom="11"
+                    android:startOffset="0"
+                    android:valueFrom="22"
+                    android:valueTo="22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="667"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="22"
                     android:valueTo="0"
                     android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.277,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1000"
+                    android:valueFrom="0"
+                    android:valueTo="2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.324,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1067"
+                    android:propertyName="rotation"
+                    android:startOffset="1600"
+                    android:valueFrom="2"
+                    android:valueTo="22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.349,0 0.21,1 1.0,1.0" />
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -355,9 +886,9 @@
         <aapt:attr name="android:animation">
             <set android:ordering="together">
                 <objectAnimator
-                    android:duration="2183"
+                    android:duration="2667"
                     android:propertyName="translateX"
-                    android:startOffset="1250"
+                    android:startOffset="0"
                     android:valueFrom="0"
                     android:valueTo="1"
                     android:valueType="floatType" />
diff --git a/quickstep/res/drawable/home_gesture.xml b/quickstep/res/drawable/home_gesture.xml
index c253b7e..4de29d0 100644
--- a/quickstep/res/drawable/home_gesture.xml
+++ b/quickstep/res/drawable/home_gesture.xml
@@ -1,46 +1,935 @@
-<!--
-    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.
--->
-<!-- Dummy translating rectangle until we have a proper animation. -->
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt" >
-  <aapt:attr name="android:drawable">
-    <vector
-        android:height="64dp"
-        android:width="64dp"
-        android:viewportHeight="600"
-        android:viewportWidth="600" >
-      <group
-          android:name="translationGroup"
-          android:pivotX="300.0"
-          android:pivotY="300.0"
-          android:rotation="180.0" >
-        <path
-            android:fillColor="#eeeeee"
-            android:pathData="M300,70 l 0,-70 70,0  0,140 -70,0 z" />
-      </group>
-    </vector>
-  </aapt:attr>
-
-  <target android:name="translationGroup">
-    <aapt:attr name="android:animation">
-      <objectAnimator
-          android:duration="3000"
-          android:propertyName="translateY"
-          android:valueFrom="0"
-          android:valueTo="-100" />
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="412dp"
+            android:height="890dp"
+            android:viewportWidth="412"
+            android:viewportHeight="890">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_3_G_T_1"
+                    android:translateX="206"
+                    android:translateY="880.283">
+                    <group
+                        android:name="_R_G_L_3_G"
+                        android:translateX="-48.25"
+                        android:translateY="-51.643">
+                        <path
+                            android:name="_R_G_L_3_G_D_0_P_0"
+                            android:fillAlpha="0"
+                            android:fillColor="#e8f0fe"
+                            android:fillType="nonZero"
+                            android:pathData=" M96.25 48.25 C96.25,74.76 74.76,96.25 48.25,96.25 C21.74,96.25 0.25,74.76 0.25,48.25 C0.25,21.74 21.74,0.25 48.25,0.25 C74.76,0.25 96.25,21.74 96.25,48.25c " />
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_2_G_N_4_N_3_T_1"
+                    android:rotation="-60"
+                    android:translateX="513.995"
+                    android:translateY="909.041">
+                    <group
+                        android:name="_R_G_L_2_G_N_4_N_3_T_0"
+                        android:translateX="-125.282"
+                        android:translateY="-222.031">
+                        <group
+                            android:name="_R_G_L_2_G_N_4_T_0"
+                            android:translateX="-27.223000000000006"
+                            android:translateY="-19.78200000000001">
+                            <group
+                                android:name="_R_G_L_2_G"
+                                android:pivotX="49.356"
+                                android:pivotY="100.997"
+                                android:rotation="-4"
+                                android:translateX="-12.881"
+                                android:translateY="-68.965">
+                                <path
+                                    android:name="_R_G_L_2_G_D_0_P_0"
+                                    android:fillAlpha="1"
+                                    android:fillColor="#d2e3fc"
+                                    android:fillType="nonZero"
+                                    android:pathData=" M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 95.13,76.48 95.13,76.48 C98.47,94.41 92.03,116.16 52.12,115.41 C52.12,115.41 31.32,103.09 31.32,103.09 C23.59,101.22 5.39,82.22 3.78,59.83 C3.78,59.83 3.05,36.42 3.05,36.42 C5.71,17.29 15.16,6.55 29.51,3.44c " />
+                                <path
+                                    android:name="_R_G_L_2_G_D_1_P_0"
+                                    android:pathData=" M58.16 28.36 C59.49,32.32 59.42,36.53 52.4,38.99 C52.4,38.99 46.29,40.77 30.67,46.05 C28.57,46.76 25.79,48.06 23.78,47.5 C22.12,47.04 20.49,45.56 19.68,42.39 C16.89,31.56 15,24.2 18.96,19.11 "
+                                    android:strokeWidth="6"
+                                    android:strokeAlpha="1"
+                                    android:strokeColor="#a0c2f9" />
+                            </group>
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_1_G_N_3_T_1"
+                    android:rotation="-60"
+                    android:translateX="513.995"
+                    android:translateY="909.041">
+                    <group
+                        android:name="_R_G_L_1_G_N_3_T_0"
+                        android:translateX="-125.282"
+                        android:translateY="-222.031">
+                        <group
+                            android:name="_R_G_L_1_G"
+                            android:translateX="-27.223000000000006"
+                            android:translateY="-19.78200000000001">
+                            <path
+                                android:name="_R_G_L_1_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="#d2e3fc"
+                                android:fillType="nonZero"
+                                android:pathData=" M62.05 -4.24 C62.05,-4.24 76.43,1.82 76.43,1.82 C91.76,16.39 83.61,36.19 86.21,52.29 C92.02,88.24 84.86,126.82 117.41,193.95 C87.71,242.97 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 17.54,107.98 13.81,87.39 C10.28,67.94 10.58,45.16 10,31.95 C10.04,30.08 8.74,12.88 15.64,8.72 C26.15,2.4 40.27,-9.2 62.05,-4.24c " />
+                            <path
+                                android:name="_R_G_L_1_G_D_1_P_0"
+                                android:pathData=" M31.4 21.98 C31.4,21.98 41.87,1.7 68.18,3.97 "
+                                android:strokeWidth="6"
+                                android:strokeAlpha="1"
+                                android:strokeColor="#a0c2f9" />
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_T_1"
+                    android:rotation="-60"
+                    android:translateX="513.995"
+                    android:translateY="909.041">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="-125.282"
+                        android:translateY="-222.031">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#d2e3fc"
+                            android:fillType="nonZero"
+                            android:pathData=" M5.87 164.71 C0.43,267.95 56.57,389.47 139.31,416.77 C222.04,444.07 215.7,295.51 248.09,197.33 C280.49,99.14 111.3,154.92 55.02,40.63 C41.48,24.52 8.77,109.58 5.87,164.71c " />
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
     </aapt:attr>
-  </target>
+    <target android:name="_R_G_L_3_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="133"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="333"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="450"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="467"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="300"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="917"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="880.283"
+                    android:valueTo="880.283"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.247,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="883"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="880.283"
+                    android:valueTo="486.283"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.247,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 95.13,76.48 95.13,76.48 C98.47,94.41 92.03,116.16 52.12,115.41 C52.12,115.41 31.32,103.09 31.32,103.09 C23.59,101.22 5.39,82.22 3.78,59.83 C3.78,59.83 3.05,36.42 3.05,36.42 C5.71,17.29 15.16,6.55 29.51,3.44c "
+                    android:valueTo="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 95.13,76.48 95.13,76.48 C98.47,94.41 92.03,116.16 52.12,115.41 C52.12,115.41 31.32,103.09 31.32,103.09 C23.59,101.22 5.39,82.22 3.78,59.83 C3.78,59.83 3.05,36.42 3.05,36.42 C5.71,17.29 15.16,6.55 29.51,3.44c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 95.13,76.48 95.13,76.48 C98.47,94.41 92.03,116.16 52.12,115.41 C52.12,115.41 31.32,103.09 31.32,103.09 C23.59,101.22 5.39,82.22 3.78,59.83 C3.78,59.83 3.05,36.42 3.05,36.42 C5.71,17.29 15.16,6.55 29.51,3.44c "
+                    android:valueTo="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 81.12,76.53 81.12,76.53 C84.46,94.46 74.07,117.8 56.14,121.15 C56.14,121.15 51.31,122.05 51.31,122.05 C31.42,125.74 12.82,111.19 11.57,91 C11.57,91 6.61,34.54 6.61,34.54 C5.34,19.87 15.16,6.55 29.51,3.44c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="pathData"
+                    android:startOffset="917"
+                    android:valueFrom="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 81.12,76.53 81.12,76.53 C84.46,94.46 74.07,117.8 56.14,121.15 C56.14,121.15 51.31,122.05 51.31,122.05 C31.42,125.74 12.82,111.19 11.57,91 C11.57,91 6.61,34.54 6.61,34.54 C5.34,19.87 15.16,6.55 29.51,3.44c "
+                    android:valueTo="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 81.12,76.53 81.12,76.53 C84.46,94.46 74.07,117.8 56.14,121.15 C56.14,121.15 51.31,122.05 51.31,122.05 C31.42,125.74 12.82,111.19 11.57,91 C11.57,91 6.61,34.54 6.61,34.54 C5.34,19.87 15.16,6.55 29.51,3.44c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="pathData"
+                    android:startOffset="1483"
+                    android:valueFrom="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 81.12,76.53 81.12,76.53 C84.46,94.46 74.07,117.8 56.14,121.15 C56.14,121.15 51.31,122.05 51.31,122.05 C31.42,125.74 12.82,111.19 11.57,91 C11.57,91 6.61,34.54 6.61,34.54 C5.34,19.87 15.16,6.55 29.51,3.44c "
+                    android:valueTo="M29.51 3.44 C29.51,3.44 29.51,3.44 29.51,3.44 C44.17,0.25 58.86,8.71 63.48,22.99 C63.48,22.99 95.13,76.48 95.13,76.48 C98.47,94.41 92.03,116.16 52.12,115.41 C52.12,115.41 31.32,103.09 31.32,103.09 C23.59,101.22 5.39,82.22 3.78,59.83 C3.78,59.83 3.05,36.42 3.05,36.42 C5.71,17.29 15.16,6.55 29.51,3.44c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-4"
+                    android:valueTo="-4"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="200"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-4"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="2300"
+                    android:propertyName="rotation"
+                    android:startOffset="533"
+                    android:valueFrom="0"
+                    android:valueTo="-4"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_4_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="513.995"
+                    android:valueTo="513.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.711,0 0.772,0.166 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="513.995"
+                    android:valueTo="469.109"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.711,0 0.772,0.166 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="433"
+                    android:propertyName="translateX"
+                    android:startOffset="483"
+                    android:valueFrom="469.109"
+                    android:valueTo="343.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.094,0.355 0.269,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="translateX"
+                    android:startOffset="917"
+                    android:valueFrom="343.995"
+                    android:valueTo="367.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.475,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="translateX"
+                    android:startOffset="1483"
+                    android:valueFrom="367.995"
+                    android:valueTo="513.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_4_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="909.041"
+                    android:valueTo="909.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="909.041"
+                    android:valueTo="859.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="translateY"
+                    android:startOffset="917"
+                    android:valueFrom="859.041"
+                    android:valueTo="883.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.475,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="translateY"
+                    android:startOffset="1483"
+                    android:valueFrom="883.041"
+                    android:valueTo="909.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_N_4_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-60"
+                    android:valueTo="-60"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-60"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="rotation"
+                    android:startOffset="917"
+                    android:valueFrom="0"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.545,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="rotation"
+                    android:startOffset="1483"
+                    android:valueFrom="-2"
+                    android:valueTo="-60"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M62.05 -4.24 C62.05,-4.24 76.43,1.82 76.43,1.82 C91.76,16.39 83.61,36.19 86.21,52.29 C92.02,88.24 84.86,126.82 117.41,193.95 C87.71,242.97 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 17.54,107.98 13.81,87.39 C10.28,67.94 10.58,45.16 10,31.95 C10.04,30.08 8.74,12.88 15.64,8.72 C26.15,2.4 40.27,-9.2 62.05,-4.24c "
+                    android:valueTo="M62.05 -4.24 C62.05,-4.24 76.43,1.82 76.43,1.82 C91.76,16.39 83.61,36.19 86.21,52.29 C92.02,88.24 84.86,126.82 117.41,193.95 C87.71,242.97 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 17.54,107.98 13.81,87.39 C10.28,67.94 10.58,45.16 10,31.95 C10.04,30.08 8.74,12.88 15.64,8.72 C26.15,2.4 40.27,-9.2 62.05,-4.24c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M62.05 -4.24 C62.05,-4.24 76.43,1.82 76.43,1.82 C91.76,16.39 83.61,36.19 86.21,52.29 C92.02,88.24 84.86,126.82 117.41,193.95 C87.71,242.97 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 17.54,107.98 13.81,87.39 C10.28,67.94 10.58,45.16 10,31.95 C10.04,30.08 8.74,12.88 15.64,8.72 C26.15,2.4 40.27,-9.2 62.05,-4.24c "
+                    android:valueTo="M51.44 5.02 C51.44,5.02 68.24,7.56 68.24,7.56 C73.26,21.67 72.7,36.77 79.13,51.76 C94.55,87.66 106.15,89.19 144.51,111.49 C114.82,160.51 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 19.02,105.73 15.29,85.14 C11.76,65.68 5.52,42.3 0.25,30.23 C0.29,28.36 0.58,17.81 15.64,8.72 C26.15,2.39 40.15,0.25 51.44,5.02c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="pathData"
+                    android:startOffset="917"
+                    android:valueFrom="M51.44 5.02 C51.44,5.02 68.24,7.56 68.24,7.56 C73.26,21.67 72.7,36.77 79.13,51.76 C94.55,87.66 106.15,89.19 144.51,111.49 C114.82,160.51 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 19.02,105.73 15.29,85.14 C11.76,65.68 5.52,42.3 0.25,30.23 C0.29,28.36 0.58,17.81 15.64,8.72 C26.15,2.39 40.15,0.25 51.44,5.02c "
+                    android:valueTo="M51.44 5.02 C51.44,5.02 68.24,7.56 68.24,7.56 C73.26,21.67 72.7,36.77 79.13,51.76 C94.55,87.66 106.15,89.19 144.51,111.49 C114.82,160.51 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 19.02,105.73 15.29,85.14 C11.76,65.68 5.52,42.3 0.25,30.23 C0.29,28.36 0.58,17.81 15.64,8.72 C26.15,2.39 40.15,0.25 51.44,5.02c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="pathData"
+                    android:startOffset="1483"
+                    android:valueFrom="M51.44 5.02 C51.44,5.02 68.24,7.56 68.24,7.56 C73.26,21.67 72.7,36.77 79.13,51.76 C94.55,87.66 106.15,89.19 144.51,111.49 C114.82,160.51 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 19.02,105.73 15.29,85.14 C11.76,65.68 5.52,42.3 0.25,30.23 C0.29,28.36 0.58,17.81 15.64,8.72 C26.15,2.39 40.15,0.25 51.44,5.02c "
+                    android:valueTo="M62.05 -4.24 C62.05,-4.24 76.43,1.82 76.43,1.82 C91.76,16.39 83.61,36.19 86.21,52.29 C92.02,88.24 84.86,126.82 117.41,193.95 C87.71,242.97 78.03,245.45 48.32,294.5 C48.32,294.5 18.2,137.31 18.2,137.31 C19.12,123.7 17.54,107.98 13.81,87.39 C10.28,67.94 10.58,45.16 10,31.95 C10.04,30.08 8.74,12.88 15.64,8.72 C26.15,2.4 40.27,-9.2 62.05,-4.24c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M31.4 21.98 C31.4,21.98 41.87,1.7 68.18,3.97 "
+                    android:valueTo="M31.4 21.98 C31.4,21.98 41.87,1.7 68.18,3.97 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M31.4 21.98 C31.4,21.98 41.87,1.7 68.18,3.97 "
+                    android:valueTo="M17.9 26.98 C17.9,26.98 29.87,13.7 56.18,15.97 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="pathData"
+                    android:startOffset="917"
+                    android:valueFrom="M17.9 26.98 C17.9,26.98 29.87,13.7 56.18,15.97 "
+                    android:valueTo="M17.9 26.98 C17.9,26.98 29.87,13.7 56.18,15.97 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="pathData"
+                    android:startOffset="1483"
+                    android:valueFrom="M17.9 26.98 C17.9,26.98 29.87,13.7 56.18,15.97 "
+                    android:valueTo="M31.4 21.98 C31.4,21.98 41.87,1.7 68.18,3.97 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="513.995"
+                    android:valueTo="513.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.711,0 0.772,0.166 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="513.995"
+                    android:valueTo="469.109"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.711,0 0.772,0.166 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="433"
+                    android:propertyName="translateX"
+                    android:startOffset="483"
+                    android:valueFrom="469.109"
+                    android:valueTo="343.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.094,0.355 0.269,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="translateX"
+                    android:startOffset="917"
+                    android:valueFrom="343.995"
+                    android:valueTo="367.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.475,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="translateX"
+                    android:startOffset="1483"
+                    android:valueFrom="367.995"
+                    android:valueTo="513.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="909.041"
+                    android:valueTo="909.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="909.041"
+                    android:valueTo="859.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="translateY"
+                    android:startOffset="917"
+                    android:valueFrom="859.041"
+                    android:valueTo="883.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.475,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="translateY"
+                    android:startOffset="1483"
+                    android:valueFrom="883.041"
+                    android:valueTo="909.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-60"
+                    android:valueTo="-60"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-60"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="rotation"
+                    android:startOffset="917"
+                    android:valueFrom="0"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.545,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="rotation"
+                    android:startOffset="1483"
+                    android:valueFrom="-2"
+                    android:valueTo="-60"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M5.87 164.71 C0.43,267.95 56.57,389.47 139.31,416.77 C222.04,444.07 215.7,295.51 248.09,197.33 C280.49,99.14 111.3,154.92 55.02,40.63 C41.48,24.52 8.77,109.58 5.87,164.71c "
+                    android:valueTo="M5.87 164.71 C0.43,267.95 56.57,389.47 139.31,416.77 C222.04,444.07 215.7,295.51 248.09,197.33 C280.49,99.14 111.3,154.92 55.02,40.63 C41.48,24.52 8.77,109.58 5.87,164.71c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M5.87 164.71 C0.43,267.95 56.57,389.47 139.31,416.77 C222.04,444.07 215.7,295.51 248.09,197.33 C280.49,99.14 111.3,154.92 55.02,40.63 C41.48,24.52 8.77,109.58 5.87,164.71c "
+                    android:valueTo="M-9.23 115.79 C-30.45,216.96 -36.73,359.16 139.26,416.71 C222.07,443.78 220.51,278.25 244.3,175.03 C268.14,75.53 116.98,150.23 54.13,40.23 C40.58,24.12 5.18,47.09 -9.23,115.79c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="433"
+                    android:propertyName="pathData"
+                    android:startOffset="483"
+                    android:valueFrom="M-9.23 115.79 C-30.45,216.96 -36.73,359.16 139.26,416.71 C222.07,443.78 220.51,278.25 244.3,175.03 C268.14,75.53 116.98,150.23 54.13,40.23 C40.58,24.12 5.18,47.09 -9.23,115.79c "
+                    android:valueTo="M-8.56 118.7 C-9.54,222.08 56.39,389.22 139.13,416.51 C221.86,443.81 234.66,227.56 233.16,109.56 C231.84,6.17 133.65,136.44 51.49,39.06 C37.94,22.95 -7.96,56.24 -8.56,118.7c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="917"
+                    android:propertyName="pathData"
+                    android:startOffset="917"
+                    android:valueFrom="M-8.56 118.7 C-9.54,222.08 56.39,389.22 139.13,416.51 C221.86,443.81 234.66,227.56 233.16,109.56 C231.84,6.17 133.65,136.44 51.49,39.06 C37.94,22.95 -7.96,56.24 -8.56,118.7c "
+                    android:valueTo="M-8.56 118.7 C-9.54,222.08 56.39,389.22 139.13,416.51 C221.86,443.81 238.66,196.56 237.16,78.56 C235.84,-24.83 133.65,136.44 51.49,39.06 C37.94,22.95 -7.96,56.24 -8.56,118.7c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.212,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="pathData"
+                    android:startOffset="1833"
+                    android:valueFrom="M-8.56 118.7 C-9.54,222.08 56.39,389.22 139.13,416.51 C221.86,443.81 238.66,196.56 237.16,78.56 C235.84,-24.83 133.65,136.44 51.49,39.06 C37.94,22.95 -7.96,56.24 -8.56,118.7c "
+                    android:valueTo="M5.87 164.71 C0.43,267.95 56.57,389.47 139.31,416.77 C222.04,444.07 215.7,295.51 248.09,197.33 C280.49,99.14 111.3,154.92 55.02,40.63 C41.48,24.52 8.77,109.58 5.87,164.71c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="513.995"
+                    android:valueTo="513.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.711,0 0.772,0.166 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="150"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="513.995"
+                    android:valueTo="469.109"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.711,0 0.772,0.166 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="433"
+                    android:propertyName="translateX"
+                    android:startOffset="483"
+                    android:valueFrom="469.109"
+                    android:valueTo="343.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.094,0.355 0.269,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="translateX"
+                    android:startOffset="917"
+                    android:valueFrom="343.995"
+                    android:valueTo="367.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.475,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="translateX"
+                    android:startOffset="1483"
+                    android:valueFrom="367.995"
+                    android:valueTo="513.995"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="909.041"
+                    android:valueTo="909.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="909.041"
+                    android:valueTo="859.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="translateY"
+                    android:startOffset="917"
+                    android:valueFrom="859.041"
+                    android:valueTo="883.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.475,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="translateY"
+                    android:startOffset="1483"
+                    android:valueFrom="883.041"
+                    android:valueTo="909.041"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-60"
+                    android:valueTo="-60"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="583"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-60"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="567"
+                    android:propertyName="rotation"
+                    android:startOffset="917"
+                    android:valueFrom="0"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.545,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1350"
+                    android:propertyName="rotation"
+                    android:startOffset="1483"
+                    android:valueFrom="-2"
+                    android:valueTo="-60"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.513,0 0.219,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="2850"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
 </animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/overview_gesture.xml b/quickstep/res/drawable/overview_gesture.xml
new file mode 100644
index 0000000..68c48d4
--- /dev/null
+++ b/quickstep/res/drawable/overview_gesture.xml
@@ -0,0 +1,1173 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="412dp"
+            android:height="890dp"
+            android:viewportWidth="412"
+            android:viewportHeight="890">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_3_G_N_4_N_3_N_2_T_0"
+                    android:translateX="297.398"
+                    android:translateY="721.169">
+                    <group
+                        android:name="_R_G_L_3_G_N_4_N_3_T_1"
+                        android:rotation="-45"
+                        android:translateX="110.176"
+                        android:translateY="177.218">
+                        <group
+                            android:name="_R_G_L_3_G_N_4_N_3_T_0"
+                            android:translateX="-132.239"
+                            android:translateY="-133.055">
+                            <group
+                                android:name="_R_G_L_3_G_N_4_T_0"
+                                android:pivotX="71.634"
+                                android:pivotY="92.684"
+                                android:rotation="-2"
+                                android:translateX="-36.948"
+                                android:translateY="-58.704">
+                                <group
+                                    android:name="_R_G_L_3_G"
+                                    android:pivotX="48.25"
+                                    android:pivotY="48.25"
+                                    android:rotation="-47"
+                                    android:translateX="-20.55"
+                                    android:translateY="-21.306">
+                                    <path
+                                        android:name="_R_G_L_3_G_D_0_P_0"
+                                        android:fillAlpha="0"
+                                        android:fillColor="#e8f0fe"
+                                        android:fillType="nonZero"
+                                        android:pathData=" M96.25 48.25 C96.25,74.76 74.76,96.25 48.25,96.25 C21.74,96.25 0.25,74.76 0.25,48.25 C0.25,21.74 21.74,0.25 48.25,0.25 C74.76,0.25 96.25,21.74 96.25,48.25c " />
+                                </group>
+                            </group>
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_2_G"
+                    android:pivotX="48.25"
+                    android:pivotY="48.25"
+                    android:rotation="-47"
+                    android:translateX="152.837"
+                    android:translateY="698.322">
+                    <path
+                        android:name="_R_G_L_2_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#e8f0fe"
+                        android:fillType="nonZero"
+                        android:pathData=" M96.25 48.25 C96.25,74.76 74.76,96.25 48.25,96.25 C21.74,96.25 0.25,74.76 0.25,48.25 C0.25,21.74 21.74,0.25 48.25,0.25 C74.76,0.25 96.25,21.74 96.25,48.25c " />
+                </group>
+                <group
+                    android:name="_R_G_L_1_G_N_3_N_2_T_0"
+                    android:translateX="297.398"
+                    android:translateY="721.169">
+                    <group
+                        android:name="_R_G_L_1_G_N_3_T_1"
+                        android:rotation="-45"
+                        android:translateX="110.176"
+                        android:translateY="177.218">
+                        <group
+                            android:name="_R_G_L_1_G_N_3_T_0"
+                            android:translateX="-132.239"
+                            android:translateY="-133.055">
+                            <group
+                                android:name="_R_G_L_1_G"
+                                android:pivotX="71.634"
+                                android:pivotY="92.684"
+                                android:rotation="-2"
+                                android:translateX="-36.948"
+                                android:translateY="-58.704">
+                                <path
+                                    android:name="_R_G_L_1_G_D_0_P_0"
+                                    android:fillAlpha="1"
+                                    android:fillColor="#d2e3fc"
+                                    android:fillType="nonZero"
+                                    android:pathData=" M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 96.98,58.55 96.98,58.55 C113.17,83.98 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c " />
+                                <path
+                                    android:name="_R_G_L_1_G_D_1_P_0"
+                                    android:pathData=" M50.61 24.81 C53.83,30.61 58.53,37.85 51.26,44.87 C51.26,44.87 38.64,50.25 34.66,52.09 C32.65,53.03 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                                    android:strokeWidth="6"
+                                    android:strokeAlpha="1"
+                                    android:strokeColor="#a0c2f9"/>
+                            </group>
+                        </group>
+                    </group>
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_N_2_T_0"
+                    android:translateX="297.398"
+                    android:translateY="721.169">
+                    <group
+                        android:name="_R_G_L_0_G_T_1"
+                        android:rotation="-45"
+                        android:translateX="110.176"
+                        android:translateY="177.218">
+                        <group
+                            android:name="_R_G_L_0_G"
+                            android:translateX="-132.239"
+                            android:translateY="-133.055">
+                            <path
+                                android:name="_R_G_L_0_G_D_0_P_0"
+                                android:fillAlpha="1"
+                                android:fillColor="#d2e3fc"
+                                android:fillType="nonZero"
+                                android:pathData=" M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 136.7,91.72 185.58,117.71 C221.01,256.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c " />
+                            <path
+                                android:name="_R_G_L_0_G_D_1_P_0"
+                                android:pathData=" M26.74 49.32 C26.74,49.32 34.17,35.86 51.22,27.28 "
+                                android:strokeWidth="6"
+                                android:strokeAlpha="1"
+                                android:strokeColor="#a0c2f9"/>
+                        </group>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_3_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="217"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="333"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_N_4_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-2"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-2"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1100"
+                    android:propertyName="rotation"
+                    android:startOffset="1167"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="rotation"
+                    android:startOffset="2267"
+                    android:valueFrom="0"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_N_4_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="110.176"
+                    android:valueTo="110.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="110.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="933"
+                    android:propertyName="translateX"
+                    android:startOffset="1167"
+                    android:valueFrom="45.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="translateX"
+                    android:startOffset="2100"
+                    android:valueFrom="45.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="translateX"
+                    android:startOffset="2267"
+                    android:valueFrom="45.176"
+                    android:valueTo="110.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_N_4_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="177.218"
+                    android:valueTo="177.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="177.218"
+                    android:valueTo="190.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateY"
+                    android:startOffset="1167"
+                    android:valueFrom="190.218"
+                    android:valueTo="190.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="1767"
+                    android:valueFrom="190.218"
+                    android:valueTo="201.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="translateY"
+                    android:startOffset="2100"
+                    android:valueFrom="201.218"
+                    android:valueTo="201.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="translateY"
+                    android:startOffset="2267"
+                    android:valueFrom="201.218"
+                    android:valueTo="177.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_N_4_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-45"
+                    android:valueTo="-45"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.618,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-45"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.618,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1167"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="1767"
+                    android:valueFrom="0"
+                    android:valueTo="-1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="rotation"
+                    android:startOffset="2100"
+                    android:valueFrom="-1"
+                    android:valueTo="-1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="rotation"
+                    android:startOffset="2267"
+                    android:valueFrom="-1"
+                    android:valueTo="-45"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_3_G_N_4_N_3_N_2_T_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleY"
+                    android:startOffset="1167"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1767"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="1"
+                    android:valueTo="1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="1767"
+                    android:valueFrom="1"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="0"
+                    android:propertyName="scaleX"
+                    android:startOffset="1167"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 96.98,58.55 96.98,58.55 C113.17,83.98 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueTo="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 96.98,58.55 96.98,58.55 C113.17,83.98 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 96.98,58.55 96.98,58.55 C113.17,83.98 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueTo="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 94.24,55.98 94.24,55.98 C109.11,75.87 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1100"
+                    android:propertyName="pathData"
+                    android:startOffset="1167"
+                    android:valueFrom="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 94.24,55.98 94.24,55.98 C109.11,75.87 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueTo="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 94.24,55.98 94.24,55.98 C109.11,75.87 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="pathData"
+                    android:startOffset="2267"
+                    android:valueFrom="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 94.24,55.98 94.24,55.98 C109.11,75.87 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueTo="M20.63 8.95 C20.63,8.95 20.63,8.95 20.63,8.95 C32.8,0.25 49.72,2.9 59.23,14.69 C59.23,14.69 96.98,58.55 96.98,58.55 C113.17,83.98 104.58,100.89 89.25,111.3 C89.25,111.3 85.05,113.83 85.05,113.83 C67.95,125.12 45.66,118.31 36.77,99.32 C36.77,99.32 11.14,47.2 11.14,47.2 C4.45,34.1 8.44,17.28 20.63,8.95c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M50.61 24.81 C53.83,30.61 58.53,37.85 51.26,44.87 C51.26,44.87 38.64,50.25 34.66,52.09 C32.65,53.03 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueTo="M50.61 24.81 C53.83,30.61 58.53,37.85 51.26,44.87 C51.26,44.87 38.64,50.25 34.66,52.09 C32.65,53.03 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M50.61 24.81 C53.83,30.61 58.53,37.85 51.26,44.87 C51.26,44.87 38.64,50.25 34.66,52.09 C32.65,53.03 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueTo="M57.62 26.47 C60,29.91 61.1,33.98 55.03,38.28 C55.03,38.28 49.65,41.69 36.11,51.08 C34.29,52.35 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1100"
+                    android:propertyName="pathData"
+                    android:startOffset="1167"
+                    android:valueFrom="M57.62 26.47 C60,29.91 61.1,33.98 55.03,38.28 C55.03,38.28 49.65,41.69 36.11,51.08 C34.29,52.35 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueTo="M57.62 26.47 C60,29.91 61.1,33.98 55.03,38.28 C55.03,38.28 49.65,41.69 36.11,51.08 C34.29,52.35 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="pathData"
+                    android:startOffset="2267"
+                    android:valueFrom="M57.62 26.47 C60,29.91 61.1,33.98 55.03,38.28 C55.03,38.28 49.65,41.69 36.11,51.08 C34.29,52.35 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueTo="M50.61 24.81 C53.83,30.61 58.53,37.85 51.26,44.87 C51.26,44.87 38.64,50.25 34.66,52.09 C32.65,53.03 31.97,54.36 29.89,54.38 C28.17,54.4 26.19,53.43 24.53,50.61 C18.85,40.97 15,34.43 17.4,28.44 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-2"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-2"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1100"
+                    android:propertyName="rotation"
+                    android:startOffset="1167"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="rotation"
+                    android:startOffset="2267"
+                    android:valueFrom="0"
+                    android:valueTo="-2"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="110.176"
+                    android:valueTo="110.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="110.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="933"
+                    android:propertyName="translateX"
+                    android:startOffset="1167"
+                    android:valueFrom="45.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="translateX"
+                    android:startOffset="2100"
+                    android:valueFrom="45.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="translateX"
+                    android:startOffset="2267"
+                    android:valueFrom="45.176"
+                    android:valueTo="110.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="177.218"
+                    android:valueTo="177.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="177.218"
+                    android:valueTo="190.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateY"
+                    android:startOffset="1167"
+                    android:valueFrom="190.218"
+                    android:valueTo="190.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="1767"
+                    android:valueFrom="190.218"
+                    android:valueTo="201.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="translateY"
+                    android:startOffset="2100"
+                    android:valueFrom="201.218"
+                    android:valueTo="201.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="translateY"
+                    android:startOffset="2267"
+                    android:valueFrom="201.218"
+                    android:valueTo="177.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_N_3_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-45"
+                    android:valueTo="-45"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.618,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-45"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.618,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1167"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="1767"
+                    android:valueFrom="0"
+                    android:valueTo="-1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="rotation"
+                    android:startOffset="2100"
+                    android:valueFrom="-1"
+                    android:valueTo="-1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="rotation"
+                    android:startOffset="2267"
+                    android:valueFrom="-1"
+                    android:valueTo="-45"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 136.7,91.72 185.58,117.71 C221.01,256.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueTo="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 136.7,91.72 185.58,117.71 C221.01,256.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.425,0 0.463,0.469 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="700"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 136.7,91.72 185.58,117.71 C221.01,256.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueTo="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 148.67,90.67 218.58,75.71 C254.01,214.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.425,0 0.463,0.469 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="733"
+                    android:propertyName="pathData"
+                    android:startOffset="1033"
+                    android:valueFrom="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 148.67,90.67 218.58,75.71 C254.01,214.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueTo="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 148.67,90.67 218.58,75.71 C254.01,214.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.275,0.272 0.661,0.665 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="500"
+                    android:propertyName="pathData"
+                    android:startOffset="1767"
+                    android:valueFrom="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 148.67,90.67 218.58,75.71 C254.01,214.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueTo="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 148.67,90.67 218.58,75.71 C254.01,214.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.165 0.661,0.665 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="733"
+                    android:propertyName="pathData"
+                    android:startOffset="2267"
+                    android:valueFrom="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 148.67,90.67 218.58,75.71 C254.01,214.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueTo="M42.21 4.34 C42.21,4.34 58.68,0.25 58.68,0.25 C68.53,11.64 74.12,25.6 85.21,38.39 C115.84,72.27 136.7,91.72 185.58,117.71 C221.01,256.74 312.66,319.67 166.82,292.86 C166.82,292.86 61.19,143.45 61.19,143.45 C57.04,130.17 50.08,112.95 39.06,94.86 C28.46,77.49 14.05,57.75 4.69,48.2 C4.2,46.35 0.25,36.08 10.96,21.83 C17.93,11.59 30.17,4.01 42.21,4.34c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.449,0.445 0.528,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M26.74 49.32 C26.74,49.32 34.17,35.86 51.22,27.28 "
+                    android:valueTo="M26.74 49.32 C26.74,49.32 34.17,35.86 51.22,27.28 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.55,0 0.375,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="pathData"
+                    android:startOffset="333"
+                    android:valueFrom="M26.74 49.32 C26.74,49.32 34.17,35.86 51.22,27.28 "
+                    android:valueTo="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.55,0 0.375,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="pathData"
+                    android:startOffset="1167"
+                    android:valueFrom="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueTo="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="1767"
+                    android:valueFrom="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueTo="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="pathData"
+                    android:startOffset="2100"
+                    android:valueFrom="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueTo="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="pathData"
+                    android:startOffset="2267"
+                    android:valueFrom="M32.48 57.18 C32.48,57.18 40.18,30.6 69.87,29.66 "
+                    android:valueTo="M26.74 49.32 C26.74,49.32 34.17,35.86 51.22,27.28 "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="110.176"
+                    android:valueTo="110.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="translateX"
+                    android:startOffset="333"
+                    android:valueFrom="110.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="933"
+                    android:propertyName="translateX"
+                    android:startOffset="1167"
+                    android:valueFrom="45.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="translateX"
+                    android:startOffset="2100"
+                    android:valueFrom="45.176"
+                    android:valueTo="45.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="translateX"
+                    android:startOffset="2267"
+                    android:valueFrom="45.176"
+                    android:valueTo="110.176"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="0"
+                    android:valueFrom="177.218"
+                    android:valueTo="177.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="translateY"
+                    android:startOffset="333"
+                    android:valueFrom="177.218"
+                    android:valueTo="190.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.658,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="translateY"
+                    android:startOffset="1167"
+                    android:valueFrom="190.218"
+                    android:valueTo="190.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.401,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateY"
+                    android:startOffset="1767"
+                    android:valueFrom="190.218"
+                    android:valueTo="201.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="translateY"
+                    android:startOffset="2100"
+                    android:valueFrom="201.218"
+                    android:valueTo="201.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="translateY"
+                    android:startOffset="2267"
+                    android:valueFrom="201.218"
+                    android:valueTo="177.218"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="-45"
+                    android:valueTo="-45"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.618,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="833"
+                    android:propertyName="rotation"
+                    android:startOffset="333"
+                    android:valueFrom="-45"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.618,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="600"
+                    android:propertyName="rotation"
+                    android:startOffset="1167"
+                    android:valueFrom="0"
+                    android:valueTo="0"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.348,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="rotation"
+                    android:startOffset="1767"
+                    android:valueFrom="0"
+                    android:valueTo="-1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="rotation"
+                    android:startOffset="2100"
+                    android:valueFrom="-1"
+                    android:valueTo="-1"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.539,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1233"
+                    android:propertyName="rotation"
+                    android:startOffset="2267"
+                    android:valueFrom="-1"
+                    android:valueTo="-45"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.286,0 0.489,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="3500"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index e163991..1ecec25 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -35,6 +35,7 @@
         </Space>
         <Button
             android:id="@+id/action_screenshot"
+            android:theme="@style/ThemeControlHighlightWorkspaceColor"
             style="@style/OverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -48,6 +49,7 @@
 
         <Button
             android:id="@+id/action_share"
+            android:theme="@style/ThemeControlHighlightWorkspaceColor"
             style="@style/OverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index e5606a3..c1cf68e 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -20,7 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Splitscreen"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"Anpinnen"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 2b7f80d..39ba152 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -45,5 +45,5 @@
     <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Se han añadido sugerencias de aplicaciones a espacios vacíos"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación sugerida: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
-    <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura de pantalla"</string>
+    <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b06dc6b..6c521fc 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -17,14 +17,16 @@
 <resources>
 
     <dimen name="task_thumbnail_top_margin">24dp</dimen>
-    <dimen name="task_thumbnail_bottom_margin_with_actions">44dp</dimen>
     <dimen name="task_thumbnail_half_top_margin">12dp</dimen>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
     <!-- For screens without rounded corners -->
     <dimen name="task_corner_radius_small">2dp</dimen>
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
-    <dimen name="overview_actions_height">110dp</dimen>
+    <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>
 
     <dimen name="recents_page_spacing">10dp</dimen>
@@ -61,7 +63,7 @@
     <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="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 b474a32..8368817 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -141,4 +141,6 @@
     <string name="action_share">Share</string>
     <!-- Label for a button that causes a screen shot of the current app to be taken. [CHAR_LIMIT=40] -->
     <string name="action_screenshot">Screenshot</string>
+    <!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
+    <string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 3926988..d3c4f4d 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -69,10 +69,18 @@
         <item name="android:textColor">@color/gesture_tutorial_primary_color</item>
     </style>
 
+    <!--
+      Can be applied to views to color things like ripples and list highlights the workspace text
+      color.
+    -->
+    <style name="ThemeControlHighlightWorkspaceColor">
+        <item name="android:colorControlHighlight">?attr/workspaceTextColor</item>
+    </style>
+
     <style name="OverviewActionButton"
         parent="@android:style/Widget.DeviceDefault.Button.Borderless">
-        <item name="android:textColor">?attr/workspaceTextColor</item>
-        <item name="android:drawableTint">?attr/workspaceTextColor</item>
+        <item name="android:textColor">@color/overview_button</item>
+        <item name="android:drawableTint">@color/overview_button</item>
         <item name="android:tint">?attr/workspaceTextColor</item>
         <item name="android:drawablePadding">4dp</item>
         <item name="android:textAllCaps">false</item>
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
new file mode 100644
index 0000000..93b64e6
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import static com.android.launcher3.util.LauncherUIHelper.doLayout;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.util.ReflectionHelpers;
+
+
+@RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
+public class RecentsActivityTest {
+
+    @Test
+    public void testRecentsActivityCreates() {
+        ActivityController<RecentsActivity> controller =
+                Robolectric.buildActivity(RecentsActivity.class);
+
+        RecentsActivity launcher = controller.setup().get();
+        doLayout(launcher);
+
+        // TODO: Ensure that LauncherAppState is not created
+    }
+
+    @Test
+    public void testRecets_showCurrentTask() {
+        ActivityController<RecentsActivity> controller =
+                Robolectric.buildActivity(RecentsActivity.class);
+
+        RecentsActivity activity = controller.setup().get();
+        doLayout(activity);
+
+        FallbackRecentsView frv = activity.getOverviewPanel();
+        frv.showCurrentTask(22);
+        doLayout(activity);
+
+        ThumbnailData thumbnailData = new ThumbnailData();
+        ReflectionHelpers.setField(thumbnailData, "thumbnail",
+                Bitmap.createBitmap(300, 500, Config.ARGB_8888));
+        frv.switchToScreenshot(thumbnailData, () -> { });
+        ShadowLooper.idleMainLooper();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index af63a25..629a74b 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
+import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -29,7 +30,6 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 
-import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
@@ -37,6 +37,7 @@
 import com.android.launcher3.proxy.StartActivityParams;
 import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.UiThreadHelper;
@@ -91,6 +92,9 @@
     @Override
     public void onNavigationModeChanged(Mode newMode) {
         getDragLayer().recreateControllers();
+        if (mActionsView != null && isOverviewActionsEnabled()) {
+            mActionsView.updateVerticalMarginForNavModeChange(newMode);
+        }
     }
 
     @Override
@@ -149,6 +153,7 @@
 
     @Override
     protected void onDeferredResumed() {
+        super.onDeferredResumed();
         if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
             // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
             onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
@@ -166,13 +171,18 @@
         mActionsView = findViewById(R.id.overview_actions_view);
         ((RecentsView) getOverviewPanel()).init(mActionsView);
 
-        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
+        if (isOverviewActionsEnabled()) {
             // Overview is above all other launcher elements, including qsb, so move it to the top.
             getOverviewPanel().bringToFront();
             mActionsView.bringToFront();
+            mActionsView.updateVerticalMarginForNavModeChange(SysUINavigationMode.getMode(this));
         }
     }
 
+    private boolean isOverviewActionsEnabled() {
+        return FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this);
+    }
+
     public <T extends OverviewActionsView> T getActionsView() {
         return (T) mActionsView;
     }
@@ -185,7 +195,7 @@
     }
 
     @Override
-    protected StateHandler[] createStateHandlers() {
+    protected StateHandler<LauncherState>[] createStateHandlers() {
         return new StateHandler[] {
                 getAllAppsController(),
                 getWorkspace(),
@@ -199,9 +209,8 @@
     }
 
     @Override
-    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
-            LauncherStateManager stateManager) {
-        return new QuickstepOnboardingPrefs(this, sharedPrefs, stateManager);
+    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+        return new QuickstepOnboardingPrefs(this, sharedPrefs);
     }
 
     @Override
@@ -251,13 +260,10 @@
         super.onActivityFlagsChanged(changeBits);
     }
 
-    /**
-     * Sets the back button visibility based on the current state/window focus.
-     */
-    private void onLauncherStateOrFocusChanged() {
+    public boolean shouldBackButtonBeHidden(LauncherState toState) {
         Mode mode = SysUINavigationMode.getMode(this);
         boolean shouldBackButtonBeHidden = mode.hasGestures
-                && getStateManager().getState().hideBackButton
+                && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
                 && hasWindowFocus()
                 && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
         if (shouldBackButtonBeHidden) {
@@ -265,6 +271,14 @@
             shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
                     TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
         }
+        return shouldBackButtonBeHidden;
+    }
+
+    /**
+     * Sets the back button visibility based on the current state/window focus.
+     */
+    private void onLauncherStateOrFocusChanged() {
+        boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
         UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
                 shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
         if (getDragLayer() != null) {
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 2cb23f1..fc60434 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
+import android.util.Log;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.UiThread;
@@ -37,6 +38,8 @@
 public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
         WrappedAnimationRunnerImpl {
 
+    private static final String TAG = "LauncherAnimationRunner";
+
     private final Handler mHandler;
     private final boolean mStartAtFrontOfQueue;
     private AnimationResult mAnimationResult;
@@ -151,7 +154,16 @@
 
                 // Because t=0 has the app icon in its original spot, we can skip the
                 // first frame and have the same movement one frame earlier.
-                mAnimator.setCurrentPlayTime(getSingleFrameMs(context));
+                int singleFrameMs = getSingleFrameMs(context);
+                long playTime = singleFrameMs;
+                // b/153821199 Add logs to debug crash but ensure release builds do not crash.
+                if (Utilities.IS_DEBUG_DEVICE) {
+                    Log.e(TAG, "Total duration=[" + mAnimator.getTotalDuration()
+                            + "], singleFrameMs=[" + singleFrameMs + "], mAnimator=" + mAnimator);
+                } else {
+                    playTime = Math.min(singleFrameMs, mAnimator.getTotalDuration());
+                }
+                mAnimator.setCurrentPlayTime(playTime);
             }
         }
     }
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 1cb0aa4..e718598 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -493,7 +493,7 @@
                 : APP_LAUNCH_ALPHA_DOWN_DURATION;
 
         RectF targetBounds = new RectF(windowTargetBounds);
-        RectF currentBounds = new RectF();
+        RectF iconBounds = new RectF();
         RectF temp = new RectF();
         Point tmpPos = new Point();
 
@@ -531,7 +531,7 @@
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
             FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
             FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
-            FloatProp mIconScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
+            FloatProp mScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
                     EXAGGERATED_EASE);
             FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
                     alphaDuration, LINEAR);
@@ -542,40 +542,48 @@
 
             @Override
             public void onUpdate(float percent) {
-                // Calculate app icon size.
-                float iconWidth = bounds.width() * mIconScale.value;
-                float iconHeight = bounds.height() * mIconScale.value;
+                // Calculate the size.
+                float width = bounds.width() * mScale.value;
+                float height = bounds.height() * mScale.value;
 
-                // Animate the window crop so that it starts off as a square.
-                final int windowWidth;
-                final int windowHeight;
+                // Animate the crop so that it starts off as a square.
+                final int cropWidth;
+                final int cropHeight;
                 if (mDeviceProfile.isVerticalBarLayout()) {
-                    windowWidth = (int) mCroppedSize.value;
-                    windowHeight = windowTargetBounds.height();
+                    cropWidth = (int) mCroppedSize.value;
+                    cropHeight = windowTargetBounds.height();
                 } else {
-                    windowWidth = windowTargetBounds.width();
-                    windowHeight = (int) mCroppedSize.value;
+                    cropWidth = windowTargetBounds.width();
+                    cropHeight = (int) mCroppedSize.value;
                 }
-                crop.set(0, 0, windowWidth, windowHeight);
+                crop.set(0, 0, cropWidth, cropHeight);
 
-                // Scale the app window to match the icon size.
-                float scaleX = iconWidth / windowWidth;
-                float scaleY = iconHeight / windowHeight;
+                // Scale the size to match the crop.
+                float scaleX = width / cropWidth;
+                float scaleY = height / cropHeight;
                 float scale = Math.min(1f, Math.max(scaleX, scaleY));
 
-                float scaledWindowWidth = windowWidth * scale;
-                float scaledWindowHeight = windowHeight * scale;
+                float scaledCropWidth = cropWidth * scale;
+                float scaledCropHeight = cropHeight * scale;
+                float offsetX  = (scaledCropWidth - width) / 2;
+                float offsetY = (scaledCropHeight - height) / 2;
 
-                float offsetX = (scaledWindowWidth - iconWidth) / 2;
-                float offsetY = (scaledWindowHeight - iconHeight) / 2;
-
-                // Calculate the window position
+                // Calculate the window position.
                 temp.set(bounds);
                 temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
                 temp.offset(mDx.value, mDy.value);
-                Utilities.scaleRectFAboutCenter(temp, mIconScale.value);
-                float transX0 = temp.left - offsetX;
-                float transY0 = temp.top - offsetY;
+                Utilities.scaleRectFAboutCenter(temp, mScale.value);
+                float windowTransX0 = temp.left - offsetX;
+                float windowTransY0 = temp.top - offsetY;
+
+                // Calculate the icon position.
+                iconBounds.set(bounds);
+                iconBounds.offset(mDx.value, mDy.value);
+                Utilities.scaleRectFAboutCenter(iconBounds, mScale.value);
+                iconBounds.left -= offsetX;
+                iconBounds.top -= offsetY;
+                iconBounds.right += offsetX;
+                iconBounds.bottom += offsetY;
 
                 float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
                 float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
@@ -584,28 +592,23 @@
                     RemoteAnimationTargetCompat target = appTargets[i];
                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
 
-                    tmpPos.set(target.position.x, target.position.y);
-                    if (target.localBounds != null) {
-                        final Rect localBounds = target.localBounds;
-                        tmpPos.set(target.localBounds.left, target.localBounds.top);
-                    }
-
                     if (target.mode == MODE_OPENING) {
                         matrix.setScale(scale, scale);
-                        matrix.postTranslate(transX0, transY0);
-                        matrix.mapRect(currentBounds, targetBounds);
-                        if (mDeviceProfile.isVerticalBarLayout()) {
-                            currentBounds.right -= croppedWidth;
-                        } else {
-                            currentBounds.bottom -= croppedHeight;
-                        }
-                        floatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
+                        matrix.postTranslate(windowTransX0, windowTransY0);
+
+                        floatingView.update(iconBounds, mIconAlpha.value, percent, 0f,
                                 mWindowRadius.value * scale, true /* isOpening */);
                         builder.withMatrix(matrix)
                                 .withWindowCrop(crop)
                                 .withAlpha(1f - mIconAlpha.value)
                                 .withCornerRadius(mWindowRadius.value);
                     } else {
+                        tmpPos.set(target.position.x, target.position.y);
+                        if (target.localBounds != null) {
+                            final Rect localBounds = target.localBounds;
+                            tmpPos.set(target.localBounds.left, target.localBounds.top);
+                        }
+
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
                                 .withWindowCrop(target.screenSpaceBounds)
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
index 983702a..13501a4 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
@@ -21,8 +21,8 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.AnimatedFloat;
@@ -32,7 +32,7 @@
 /**
  * State handler for animating back button alpha
  */
-public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
+public class BackButtonAlphaHandler implements StateHandler<LauncherState> {
 
     private final BaseQuickstepLauncher mLauncher;
     private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
@@ -59,7 +59,8 @@
         }
 
         mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha();
-        animation.setFloat(mBackAlpha, VALUE, toState.hideBackButton ? 0 : 1, LINEAR);
+        animation.setFloat(mBackAlpha, VALUE,
+                mLauncher.shouldBackButtonBeHidden(toState) ? 0 : 1, LINEAR);
     }
 
     private void updateBackAlpha() {
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 5f5d6dc..8292a92 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -26,10 +26,10 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SurfaceControlCompat;
@@ -39,7 +39,7 @@
 /**
  * Controls blur and wallpaper zoom, for the Launcher surface only.
  */
-public class DepthController implements LauncherStateManager.StateHandler {
+public class DepthController implements StateHandler<LauncherState> {
 
     public static final FloatProperty<DepthController> DEPTH =
             new FloatProperty<DepthController>("depth") {
@@ -193,8 +193,17 @@
         if (windowToken != null) {
             mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
         }
+        final int blur;
+        if (mLauncher.isInState(LauncherState.ALL_APPS) && mDepth == 1) {
+            // All apps has a solid background. We don't need to draw blurs after it's fully
+            // visible. This will take us out of GPU composition, saving battery and increasing
+            // performance.
+            blur = 0;
+        } else {
+            blur = (int) (mDepth * mMaxBlurRadius);
+        }
         new TransactionCompat()
-                .setBackgroundBlurRadius(mSurface, (int) (mDepth * mMaxBlurRadius))
+                .setBackgroundBlurRadius(mSurface, blur)
                 .apply();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index ac50d6d..ec3a490 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -36,9 +36,9 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.quickstep.views.RecentsView;
 
@@ -49,7 +49,7 @@
  * @param <T> the recents view
  */
 public abstract class BaseRecentsViewStateController<T extends RecentsView>
-        implements StateHandler {
+        implements StateHandler<LauncherState> {
     protected final T mRecentsView;
     protected final BaseQuickstepLauncher mLauncher;
 
@@ -107,7 +107,7 @@
         setter.setFloat(
                 mRecentsView, getTaskModalnessProperty(),
                 toState.getOverviewModalness(),
-                config.getInterpolator(ANIM_OVERVIEW_MODAL, AGGRESSIVE_EASE_IN_OUT));
+                config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
     }
 
     abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 81d4224..e7cd393 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -21,7 +21,6 @@
 
 import android.content.Context;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.allapps.AllAppsContainerView;
@@ -33,7 +32,7 @@
  */
 public class AllAppsState extends LauncherState {
 
-    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+    private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS;
 
     private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
         @Override
@@ -47,23 +46,11 @@
     }
 
     @Override
-    public int getTransitionDuration(Launcher launcher) {
+    public int getTransitionDuration(Context context) {
         return 320;
     }
 
     @Override
-    public void onStateEnabled(Launcher launcher) {
-        AbstractFloatingView.closeAllOpenViews(launcher);
-        dispatchWindowStateChanged(launcher);
-    }
-
-    @Override
-    public void onStateDisabled(Launcher launcher) {
-        super.onStateDisabled(launcher);
-        AbstractFloatingView.closeAllOpenViews(launcher);
-    }
-
-    @Override
     public String getDescription(Launcher launcher) {
         AllAppsContainerView appsView = launcher.getAppsView();
         return appsView.getDescription();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index a7a03e5..43328b6 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -18,14 +18,11 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Build;
-import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
@@ -33,7 +30,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -52,24 +48,13 @@
 
     int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
 
-    /**
-     * @return The progress of the swipe where we start resisting the user, where 0 is fullscreen
-     * and 1 is recents. These values should probably be greater than 1 to let the user swipe past
-     * recents before we start resisting them.
-     */
-    default Pair<Float, Float> getSwipeUpPullbackStartAndMaxProgress() {
-        return new Pair<>(1.4f, 1.8f);
-    }
-
     void onSwipeUpToRecentsComplete();
 
     default void onSwipeUpToHomeComplete() { }
     void onAssistantVisibilityChanged(float visibility);
 
-    @NonNull HomeAnimationFactory prepareHomeUI();
-
-    AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
-            Consumer<AnimatorPlaybackController> callback);
+    AnimationFactory prepareRecentsUI(
+            boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
 
     ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
 
@@ -105,7 +90,7 @@
 
     Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
 
-    boolean shouldMinimizeSplitScreen();
+    boolean allowMinimizeSplitScreen();
 
     default boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
         return true;
@@ -152,35 +137,4 @@
          */
         default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
     }
-
-    interface HomeAnimationFactory {
-
-        /** Return the floating view that will animate in sync with the closing window. */
-        default @Nullable View getFloatingView() {
-            return null;
-        }
-
-        @NonNull RectF getWindowTargetRect();
-
-        @NonNull AnimatorPlaybackController createActivityAnimationToHome();
-
-        default void playAtomicAnimation(float velocity) {
-            // No-op
-        }
-
-        static RectF getDefaultWindowTargetRect(PagedOrientationHandler orientationHandler,
-            DeviceProfile dp) {
-            final int halfIconSize = dp.iconSizePx / 2;
-            float primaryDimension = orientationHandler
-                .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
-            float secondaryDimension = orientationHandler
-                .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
-            final float targetX =  primaryDimension / 2f;
-            final float targetY = secondaryDimension - dp.hotseatBarSizePx;
-            // Fallback to animate to center of screen.
-            return new RectF(targetX - halfIconSize, targetY - halfIconSize,
-                    targetX + halfIconSize, targetY + halfIconSize);
-        }
-
-    }
 }
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/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 544f420..9b515ae 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -110,10 +110,6 @@
     public static final int STATE_RECENTS_SCROLLING_FINISHED =
             getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
 
-    // Called when the new task appeared from quick switching.
-    public static final int STATE_TASK_APPEARED_DURING_SWITCH =
-            getFlagForIndex("STATE_TASK_APPEARED_DURING_SWITCH");
-
     // Needed to interact with the current activity
     private final Intent mHomeIntent;
     private final Intent mOverviewIntent;
@@ -123,9 +119,8 @@
 
     private ActivityManager.RunningTaskInfo mRunningTask;
     private GestureEndTarget mEndTarget;
-    private RemoteAnimationTargetCompat mAnimationTarget;
-    // TODO: This can be removed once we stop finishing the animation when starting a new task
-    private int mFinishingRecentsAnimationTaskId = -1;
+    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private int mLastStartedTaskId = -1;
 
     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
         mHomeIntent = componentObserver.getHomeIntent();
@@ -143,7 +138,8 @@
         mGestureId = other.mGestureId;
         mRunningTask = other.mRunningTask;
         mEndTarget = other.mEndTarget;
-        mFinishingRecentsAnimationTaskId = other.mFinishingRecentsAnimationTaskId;
+        mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
+        mLastStartedTaskId = other.mLastStartedTaskId;
     }
 
     public GestureState() {
@@ -226,20 +222,41 @@
     }
 
     /**
+     * Updates the last task that appeared during this gesture.
+     */
+    public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
+        mLastAppearedTaskTarget = lastAppearedTaskTarget;
+    }
+
+    /**
+     * @return The id of the task that appeared during this gesture.
+     */
+    public int getLastAppearedTaskId() {
+        return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
+    }
+
+    /**
+     * Updates the last task that we started via startActivityFromRecents() during this gesture.
+     */
+    public void updateLastStartedTaskId(int lastStartedTaskId) {
+        mLastStartedTaskId = lastStartedTaskId;
+    }
+
+    /**
+     * @return The id of the task that was most recently started during this gesture, or -1 if
+     * no task has been started yet (i.e. we haven't settled on a new task).
+     */
+    public int getLastStartedTaskId() {
+        return mLastStartedTaskId;
+    }
+
+    /**
      * @return the end target for this gesture (if known).
      */
     public GestureEndTarget getEndTarget() {
         return mEndTarget;
     }
 
-    public void setAnimationTarget(RemoteAnimationTargetCompat target) {
-        mAnimationTarget = target;
-    }
-
-    public RemoteAnimationTargetCompat getAnimationTarget() {
-        return mAnimationTarget;
-    }
-
     /**
      * Sets the end target of this gesture and immediately notifies the state changes.
      */
@@ -260,29 +277,8 @@
     }
 
     /**
-     * @return the id for the task that was about to be launched following the finish of the recents
-     * animation.  Only defined between when the finish-recents call was made and the launch
-     * activity call is made.
-     */
-    public int getFinishingRecentsAnimationTaskId() {
-        return mFinishingRecentsAnimationTaskId;
-    }
-
-    /**
-     * Sets the id for the task will be launched after the recents animation is finished. Once the
-     * animation has finished then the id will be reset to -1.
-     */
-    public void setFinishingRecentsAnimationTaskId(int taskId) {
-        mFinishingRecentsAnimationTaskId = taskId;
-        mStateCallback.runOnceAtState(STATE_RECENTS_ANIMATION_FINISHED, () -> {
-            mFinishingRecentsAnimationTaskId = -1;
-        });
-    }
-
-    /**
      * @return whether the current gesture is still running a recents animation to a state in the
      *         Launcher or Recents activity.
-     * Updates the running task for the gesture to be the given {@param runningTask}.
      */
     public boolean isRunningAnimationToLauncher() {
         return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
@@ -314,18 +310,13 @@
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
     }
 
-    @Override
-    public void onTaskAppeared(RemoteAnimationTargetCompat app) {
-        mAnimationTarget = app;
-        mStateCallback.setState(STATE_TASK_APPEARED_DURING_SWITCH);
-    }
-
     public void dump(PrintWriter pw) {
         pw.println("GestureState:");
         pw.println("  gestureID=" + mGestureId);
         pw.println("  runningTask=" + mRunningTask);
         pw.println("  endTarget=" + mEndTarget);
-        pw.println("  finishingRecentsAnimationTaskId=" + mFinishingRecentsAnimationTaskId);
+        pw.println("  lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
+        pw.println("  lastStartedTaskId=" + mLastStartedTaskId);
         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
     }
 }
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 2a9f32d..879fd1d 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -55,6 +55,8 @@
     private static final boolean DEBUG = false;
     private static final int MAX_ORIENTATIONS = 4;
 
+    private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
+
     private final Matrix mTmpMatrix = new Matrix();
     private final float[] mTmpPoint = new float[2];
 
@@ -69,9 +71,10 @@
     private QuickStepContractInfo mContractInfo;
 
     /**
-     * Represents if we're currently in a swipe "session" of sorts. If value is -1, then user
-     * has not tapped on an active nav region. Otherwise it will be the rotation of the display
-     * when the user first interacted with the active nav bar region.
+     * Represents if we're currently in a swipe "session" of sorts. If value is
+     * QUICKSTEP_ROTATION_UNINITIALIZED, then user has not tapped on an active nav region.
+     * Otherwise it will be the rotation of the display when the user first interacted with the
+     * active nav bar region.
      * The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is
      * called - usually from a timeout or if user starts interacting w/ the foreground app.
      *
@@ -79,7 +82,7 @@
      * the rect is purely used for tracking touch interactions and usually this "session" will
      * outlast the touch interaction.
      */
-    private int mQuickStepStartingRotation = -1;
+    private int mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
 
     /** For testability */
     interface QuickStepContractInfo {
@@ -116,7 +119,7 @@
      */
     void createOrAddTouchRegion(DefaultDisplay.Info info) {
         mCurrentDisplayRotation = info.rotation;
-        if (mQuickStepStartingRotation > -1
+        if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
                 && mCurrentDisplayRotation == mQuickStepStartingRotation) {
             // User already was swiping and the current screen is same rotation as the starting one
             // Remove active nav bars in other rotations except for the one we started out in
@@ -146,7 +149,7 @@
         mEnableMultipleRegions = enableMultipleRegions &&
                 mMode != SysUINavigationMode.Mode.TWO_BUTTONS;
         if (!enableMultipleRegions) {
-            mQuickStepStartingRotation = -1;
+            mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
             resetSwipeRegions(info);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 7d568a4..a21c714 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -19,13 +19,11 @@
 
 import android.graphics.Rect;
 import android.util.ArraySet;
-import android.util.Log;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -41,15 +39,15 @@
         com.android.systemui.shared.system.RecentsAnimationListener {
 
     private final Set<RecentsAnimationListener> mListeners = new ArraySet<>();
-    private final boolean mShouldMinimizeSplitScreen;
+    private final boolean mAllowMinimizeSplitScreen;
 
     // TODO(141886704): Remove these references when they are no longer needed
     private RecentsAnimationController mController;
 
     private boolean mCancelled;
 
-    public RecentsAnimationCallbacks(boolean shouldMinimizeSplitScreen) {
-        mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+    public RecentsAnimationCallbacks(boolean allowMinimizeSplitScreen) {
+        mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
     }
 
     @UiThread
@@ -94,7 +92,7 @@
         RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
                 wallpaperTargets, homeContentInsets, minimizedHomeBounds);
         mController = new RecentsAnimationController(animationController,
-                mShouldMinimizeSplitScreen, this::onAnimationFinished);
+                mAllowMinimizeSplitScreen, this::onAnimationFinished);
 
         if (mCancelled) {
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
@@ -161,6 +159,6 @@
         /**
          * Callback made when a task started from the recents is ready for an app transition.
          */
-        default void onTaskAppeared(RemoteAnimationTargetCompat app) {}
+        default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {}
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 5ece2d7..76a81eb 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -49,21 +49,22 @@
 
     private final RecentsAnimationControllerCompat mController;
     private final Consumer<RecentsAnimationController> mOnFinishedListener;
-    private final boolean mShouldMinimizeSplitScreen;
+    private final boolean mAllowMinimizeSplitScreen;
 
     private InputConsumerController mInputConsumerController;
     private Supplier<InputConsumer> mInputProxySupplier;
     private InputConsumer mInputConsumer;
-    private boolean mWindowThresholdCrossed = false;
+    private boolean mUseLauncherSysBarFlags = false;
+    private boolean mSplitScreenMinimized = false;
     private boolean mTouchInProgress;
     private boolean mFinishPending;
 
     public RecentsAnimationController(RecentsAnimationControllerCompat controller,
-            boolean shouldMinimizeSplitScreen,
+            boolean allowMinimizeSplitScreen,
             Consumer<RecentsAnimationController> onFinishedListener) {
         mController = controller;
         mOnFinishedListener = onFinishedListener;
-        mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+        mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
     }
 
     /**
@@ -76,16 +77,31 @@
 
     /**
      * Indicates that the gesture has crossed the window boundary threshold and system UI can be
-     * update the represent the window behind
+     * update the system bar flags accordingly.
      */
-    public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
-        if (mWindowThresholdCrossed != windowThresholdCrossed) {
-            mWindowThresholdCrossed = windowThresholdCrossed;
+    public void setUseLauncherSystemBarFlags(boolean useLauncherSysBarFlags) {
+        if (mUseLauncherSysBarFlags != useLauncherSysBarFlags) {
+            mUseLauncherSysBarFlags = useLauncherSysBarFlags;
             UI_HELPER_EXECUTOR.execute(() -> {
-                mController.setAnimationTargetsBehindSystemBars(!windowThresholdCrossed);
+                mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
+            });
+        }
+    }
+
+    /**
+     * Indicates that the gesture has crossed the window boundary threshold and we should minimize
+     * if we are in splitscreen.
+     */
+    public void setSplitScreenMinimized(boolean splitScreenMinimized) {
+        if (!mAllowMinimizeSplitScreen) {
+            return;
+        }
+        if (mSplitScreenMinimized != splitScreenMinimized) {
+            mSplitScreenMinimized = splitScreenMinimized;
+            UI_HELPER_EXECUTOR.execute(() -> {
                 SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
-                if (p != null && mShouldMinimizeSplitScreen) {
-                    p.setSplitScreenMinimized(windowThresholdCrossed);
+                if (p != null) {
+                    p.setSplitScreenMinimized(splitScreenMinimized);
                 }
             });
         }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index a6ce2b5..8ac15e8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,6 +17,7 @@
 
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
@@ -216,6 +217,7 @@
         mDefaultDisplay.removeChangeListener(this);
         if (newMode.hasGestures) {
             mDefaultDisplay.addChangeListener(this);
+            onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
         }
 
         if (newMode == NO_BUTTON) {
@@ -511,14 +513,12 @@
 
     void enableMultipleRegions(boolean enable) {
         mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
-        if (enable) {
-            UI_HELPER_EXECUTOR.execute(() -> {
-                int quickStepStartingRotation =
-                        mOrientationTouchTransformer.getQuickStepStartingRotation();
-                SystemUiProxy.INSTANCE.get(mContext)
-                        .onQuickSwitchToNewTask(quickStepStartingRotation);
-            });
-        }
+        UI_HELPER_EXECUTOR.execute(() -> {
+            int quickStepStartingRotation =
+                    mOrientationTouchTransformer.getQuickStepStartingRotation();
+            SystemUiProxy.INSTANCE.get(mContext)
+                    .onQuickSwitchToNewTask(quickStepStartingRotation);
+        });
     }
 
     public int getCurrentActiveRotation() {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index bbca568..cad51f4 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
+import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
 
 import android.content.Intent;
 import android.util.Log;
@@ -26,9 +27,9 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
 
@@ -37,6 +38,7 @@
     private RecentsAnimationTargets mTargets;
     // Temporary until we can hook into gesture state events
     private GestureState mLastGestureState;
+    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
 
     /**
      * Preloads the recents animation.
@@ -67,13 +69,21 @@
 
         final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
         mLastGestureState = gestureState;
-        mCallbacks = new RecentsAnimationCallbacks(activityInterface.shouldMinimizeSplitScreen());
+        mCallbacks = new RecentsAnimationCallbacks(activityInterface.allowMinimizeSplitScreen());
         mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
             @Override
             public void onRecentsAnimationStart(RecentsAnimationController controller,
                     RecentsAnimationTargets targets) {
+                if (mCallbacks == null) {
+                    // It's possible for the recents animation to have finished and be cleaned up
+                    // by the time we process the start callback, and in that case, just we can skip
+                    // handling this call entirely
+                    return;
+                }
                 mController = controller;
                 mTargets = targets;
+                mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId());
+                mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
             }
 
             @Override
@@ -91,6 +101,20 @@
             public void onRecentsAnimationFinished(RecentsAnimationController controller) {
                 cleanUpRecentsAnimation(null /* canceledThumbnail */);
             }
+
+            @Override
+            public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+                if (mController != null) {
+                    if (mLastAppearedTaskTarget == null
+                            || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
+                        if (mLastAppearedTaskTarget != null) {
+                            mController.removeTaskTarget(mLastAppearedTaskTarget);
+                        }
+                        mLastAppearedTaskTarget = appearedTaskTarget;
+                        mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
+                    }
+                }
+            }
         });
         mCallbacks.addListener(gestureState);
         mCallbacks.addListener(listener);
@@ -107,6 +131,9 @@
         mCallbacks.removeListener(mLastGestureState);
         mLastGestureState = gestureState;
         mCallbacks.addListener(gestureState);
+        gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED
+                | STATE_RECENTS_ANIMATION_STARTED);
+        gestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
         return mCallbacks;
     }
 
@@ -166,6 +193,7 @@
         mCallbacks = null;
         mTargets = null;
         mLastGestureState = null;
+        mLastAppearedTaskTarget = null;
     }
 
     public void dump() {
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index fe95e83..f3cefb9 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -75,9 +75,6 @@
 
     @Override
     Integer getActionTextButtonStringId() {
-        if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_text_button_label;
-        }
         return null;
     }
 
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index ac2200d..a98aad1 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,17 +16,17 @@
 
 package com.android.quickstep.logging;
 
-import static android.stats.launcher.nano.Launcher.ALLAPPS;
-import static android.stats.launcher.nano.Launcher.BACKGROUND;
-import static android.stats.launcher.nano.Launcher.HOME;
-import static android.stats.launcher.nano.Launcher.OVERVIEW;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
+import static com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.WIDGET;
 
 import android.content.Context;
+import android.util.Log;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
@@ -34,11 +34,13 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.LogConfig;
+import com.android.systemui.shared.system.SysUiStatsLog;
 
 import java.util.ArrayList;
 
 /**
- * This method calls the StatsLog hidden method until they are made available public.
+ * This class calls StatsLog compile time generated methods.
  *
  * To see if the logs are properly sent to statsd, execute following command.
  * $ adb root && adb shell statsd
@@ -47,34 +49,69 @@
  */
 public class StatsLogCompatManager extends StatsLogManager {
 
-    private static final int SUPPORTED_TARGET_DEPTH = 2;
     private static final String TAG = "StatsLog";
-    private static final boolean DEBUG = false;
+    private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
+
     private static Context sContext;
 
+    private static final int DEFAULT_WIDGET_SPAN_XY = 1;
+    private static final int DEFAULT_WORKSPACE_GRID_XY = -1;
+    private static final int DEFAULT_PAGE_INDEX = -2;
+    private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
+
     public StatsLogCompatManager(Context context) {
         sContext = context;
     }
 
-    @Override
-    public void log(LauncherEvent eventId, LauncherAtom.ItemInfo item) {
-        // Call StatsLog method
+    /**
+     * Logs an event and accompanying {@link ItemInfo}
+     */
+    public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) {
+        log(event, DEFAULT_INSTANCE_ID, itemInfo);
     }
 
+    /**
+     * Logs an event and accompanying {@link LauncherAtom.ItemInfo}
+     */
     @Override
-    public void verify() {
-        if (!(StatsLogUtils.LAUNCHER_STATE_ALLAPPS == ALLAPPS
-                && StatsLogUtils.LAUNCHER_STATE_BACKGROUND == BACKGROUND
-                && StatsLogUtils.LAUNCHER_STATE_OVERVIEW == OVERVIEW
-                && StatsLogUtils.LAUNCHER_STATE_HOME == HOME)) {
-            throw new IllegalStateException(
-                    "StatsLogUtil constants doesn't match enums in launcher.proto");
+    public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) {
+        if (IS_VERBOSE) {
+            Log.d(TAG, String.format("\n%s\n%s", event.name(), itemInfo));
         }
+        if (!Utilities.ATLEAST_R) {
+            return;
+        }
+        SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT,
+                SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
+                SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME /* TODO */,
+                SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND /* TODO */,
+                null /* launcher extensions, deprecated */,
+                false /* quickstep_enabled, deprecated */,
+                event.getId() /* event_id */,
+                itemInfo.getItemCase().getNumber() /* target_id */,
+                instanceId.getId() /* instance_id TODO */,
+                0 /* uid TODO */,
+                getPackageName(itemInfo) /* package_name */,
+                getComponentName(itemInfo) /* component_name */,
+                getGridX(itemInfo, false) /* grid_x */,
+                getGridY(itemInfo, false) /* grid_y */,
+                getPageId(itemInfo, false) /* page_id */,
+                getGridX(itemInfo, true) /* grid_x_parent */,
+                getGridY(itemInfo, true) /* grid_y_parent */,
+                getPageId(itemInfo, true) /* page_id_parent */,
+                getHierarchy(itemInfo) /* hierarchy */,
+                itemInfo.getIsWork() /* is_work_profile */,
+                itemInfo.getRank() /* rank */,
+                0 /* fromState */,
+                0 /* toState */,
+                null /* edittext */,
+                0 /* cardinality */);
     }
 
     /**
      * Logs the workspace layout information on the model thread.
      */
+    @Override
     public void logSnapshot() {
         LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
                 new SnapshotWorker());
@@ -88,19 +125,161 @@
             ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
 
             for (ItemInfo info : workspaceItems) {
-                LauncherAtom.ItemInfo atomInfo = info.buildProto(null, null);
-                // call StatsLog method
+                LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
+                writeSnapshot(atomInfo);
             }
             for (FolderInfo fInfo : folders) {
                 for (ItemInfo info : fInfo.contents) {
-                    LauncherAtom.ItemInfo atomInfo = info.buildProto(null, fInfo);
-                    // call StatsLog method
+                    LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
+                    writeSnapshot(atomInfo);
                 }
             }
             for (ItemInfo info : appWidgets) {
-                LauncherAtom.ItemInfo atomInfo = info.buildProto(null, null);
-                // call StatsLog method
+                LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
+                writeSnapshot(atomInfo);
             }
         }
     }
+    private static void writeSnapshot(LauncherAtom.ItemInfo itemInfo) {
+        if (IS_VERBOSE) {
+            Log.d(TAG, "\nwriteSnapshot:" + itemInfo);
+        }
+        if (!Utilities.ATLEAST_R) {
+            return;
+        }
+        SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
+                0 /* event_id */,
+                itemInfo.getItemCase().getNumber() /* target_id */,
+                0 /* instance_id */,
+                0 /* uid */,
+                getPackageName(itemInfo) /* package_name */,
+                getComponentName(itemInfo) /* component_name */,
+                getGridX(itemInfo, false) /* grid_x */,
+                getGridY(itemInfo, false) /* grid_y */,
+                getPageId(itemInfo, false) /* page_id */,
+                getGridX(itemInfo, true) /* grid_x_parent */,
+                getGridY(itemInfo, true) /* grid_y_parent */,
+                getPageId(itemInfo, true) /* page_id_parent */,
+                getHierarchy(itemInfo) /* hierarchy */,
+                itemInfo.getIsWork() /* is_work_profile */,
+                0 /* origin TODO */,
+                0 /* cardinality */,
+                getSpanX(itemInfo),
+                getSpanY(itemInfo));
+    }
+
+    private static int getSpanX(LauncherAtom.ItemInfo atomInfo) {
+        if (atomInfo.getItemCase() != WIDGET) {
+            return DEFAULT_WIDGET_SPAN_XY;
+        }
+        return atomInfo.getWidget().getSpanX();
+    }
+
+    private static int getSpanY(LauncherAtom.ItemInfo atomInfo) {
+        if (atomInfo.getItemCase() != WIDGET) {
+            return DEFAULT_WIDGET_SPAN_XY;
+        }
+        return atomInfo.getWidget().getSpanY();
+    }
+
+    private static String getPackageName(LauncherAtom.ItemInfo atomInfo) {
+        switch (atomInfo.getItemCase()) {
+            case APPLICATION:
+                return atomInfo.getApplication().getPackageName();
+            case SHORTCUT:
+                return atomInfo.getShortcut().getShortcutName();
+            case WIDGET:
+                return atomInfo.getWidget().getPackageName();
+            case TASK:
+                return atomInfo.getTask().getPackageName();
+            default:
+                return null;
+        }
+    }
+
+    private static String getComponentName(LauncherAtom.ItemInfo atomInfo) {
+        switch (atomInfo.getItemCase()) {
+            case APPLICATION:
+                return atomInfo.getApplication().getComponentName();
+            case SHORTCUT:
+                return atomInfo.getShortcut().getShortcutName();
+            case WIDGET:
+                return atomInfo.getWidget().getComponentName();
+            case TASK:
+                return atomInfo.getTask().getComponentName();
+            default:
+                return null;
+        }
+    }
+
+    private static int getGridX(LauncherAtom.ItemInfo info, boolean parent) {
+        switch (info.getContainerInfo().getContainerCase()) {
+            case WORKSPACE:
+                if (parent) {
+                    return DEFAULT_WORKSPACE_GRID_XY;
+                } else {
+                    return info.getContainerInfo().getWorkspace().getGridX();
+                }
+            case FOLDER:
+                if (parent) {
+                    switch (info.getContainerInfo().getFolder().getParentContainerCase()) {
+                        case WORKSPACE:
+                            return info.getContainerInfo().getFolder().getWorkspace().getGridX();
+                        default:
+                            return DEFAULT_WORKSPACE_GRID_XY;
+                    }
+                } else {
+                    return info.getContainerInfo().getFolder().getGridX();
+                }
+            default:
+                return DEFAULT_WORKSPACE_GRID_XY;
+        }
+    }
+
+    private static int getGridY(LauncherAtom.ItemInfo info, boolean parent) {
+        switch (info.getContainerInfo().getContainerCase()) {
+            case WORKSPACE:
+                if (parent) {
+                    return DEFAULT_WORKSPACE_GRID_XY;
+                } else {
+                    return info.getContainerInfo().getWorkspace().getGridY();
+                }
+            case FOLDER:
+                if (parent) {
+                    switch (info.getContainerInfo().getFolder().getParentContainerCase()) {
+                        case WORKSPACE:
+                            return info.getContainerInfo().getFolder().getWorkspace().getGridY();
+                        default:
+                            return DEFAULT_WORKSPACE_GRID_XY;
+                    }
+                } else {
+                    return info.getContainerInfo().getFolder().getGridY();
+                }
+            default:
+                return DEFAULT_WORKSPACE_GRID_XY;
+        }
+    }
+
+    private static int getPageId(LauncherAtom.ItemInfo info, boolean parent) {
+        switch (info.getContainerInfo().getContainerCase()) {
+            case HOTSEAT:
+                return info.getContainerInfo().getHotseat().getIndex();
+            case WORKSPACE:
+                return info.getContainerInfo().getWorkspace().getPageIndex();
+            default:
+                return DEFAULT_PAGE_INDEX;
+        }
+    }
+
+    /**
+     *
+     */
+    private static int getHierarchy(LauncherAtom.ItemInfo info) {
+        // TODO
+        if (info.getContainerInfo().getContainerCase() == FOLDER) {
+            return info.getContainerInfo().getFolder().getParentContainerCase().getNumber() + 100;
+        } else {
+            return info.getContainerInfo().getContainerCase().getNumber();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 4edf2fb..fa53be2 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -17,31 +17,19 @@
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Rect;
-
-import androidx.annotation.AnyThread;
-import androidx.annotation.IntDef;
+import android.view.View;
+import android.view.ViewGroup;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.quickstep.SysUINavigationMode;
 
-import java.lang.annotation.Retention;
-
 public class LayoutUtils {
 
-    private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
-    private static final int MULTI_WINDOW_STRATEGY_DEVICE_PROFILE = 2;
-
-    @Retention(SOURCE)
-    @IntDef({MULTI_WINDOW_STRATEGY_HALF_SCREEN, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE})
-    private @interface MultiWindowStrategy {}
-
     /**
      * The height for the swipe up motion
      */
@@ -53,112 +41,11 @@
         return swipeHeight;
     }
 
-    public static void calculateLauncherTaskSize(Context context, DeviceProfile dp, Rect outRect) {
-        float extraSpace;
-        if (dp.isVerticalBarLayout()) {
-            extraSpace = 0;
-        } else {
-            Resources res = context.getResources();
-
-            if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
-                //TODO: this needs to account for the swipe gesture height and accessibility
-                // UI when shown.
-                extraSpace = res.getDimensionPixelSize(R.dimen.overview_actions_height);
-            } else {
-                extraSpace = getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
-                        + res.getDimensionPixelSize(
-                                R.dimen.dynamic_grid_hotseat_extra_vertical_size)
-                        + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
-            }
-        }
-        calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect);
-    }
-
-    public static void calculateFallbackTaskSize(Context context, DeviceProfile dp, Rect outRect) {
-        float extraSpace;
-        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
-            extraSpace = context.getResources()
-                    .getDimensionPixelSize(R.dimen.overview_actions_height);
-        } else {
-            extraSpace = 0;
-        }
-        calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE, outRect);
-    }
-
-    @AnyThread
-    public static void calculateTaskSize(Context context, DeviceProfile dp,
-            float extraVerticalSpace, @MultiWindowStrategy int multiWindowStrategy, Rect outRect) {
-        float taskWidth, taskHeight, paddingHorz;
-        Resources res = context.getResources();
-        Rect insets = dp.getInsets();
-        final boolean overviewActionsEnabled = ENABLE_OVERVIEW_ACTIONS.get();
-
-        if (dp.isMultiWindowMode) {
-            if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
-                DeviceProfile fullDp = dp.getFullScreenProfile();
-                // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
-                // account for system insets
-                taskWidth = fullDp.availableWidthPx;
-                taskHeight = fullDp.availableHeightPx;
-                float halfDividerSize = res.getDimension(R.dimen.multi_window_task_divider_size)
-                        / 2;
-
-                if (fullDp.isLandscape) {
-                    taskWidth = taskWidth / 2 - halfDividerSize;
-                } else {
-                    taskHeight = taskHeight / 2 - halfDividerSize;
-                }
-            } else {
-                // multiWindowStrategy == MULTI_WINDOW_STRATEGY_DEVICE_PROFILE
-                taskWidth = dp.widthPx;
-                taskHeight = dp.heightPx;
-            }
-            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 if (overviewActionsEnabled && removeShelfFromOverview(context)) {
-                paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
-            } else {
-                paddingResId = R.dimen.portrait_task_card_horz_space;
-            }
-            paddingHorz = res.getDimension(paddingResId);
-        }
-
-        float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float paddingVert = overviewActionsEnabled && removeShelfFromOverview(context)
-                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
-
-        // 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;
-
-        float availableHeight = launcherVisibleHeight
-                - topIconMargin - extraVerticalSpace - paddingVert;
-        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 + Math.max(topIconMargin,
-                (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
-        outRect.set(Math.round(x), Math.round(y),
-                Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
-    }
-
     public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
         // Track the bottom of the window.
         if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
             Rect taskSize = new Rect();
-            calculateLauncherTaskSize(context, dp, taskSize);
+            LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, taskSize);
             return (dp.heightPx - taskSize.height()) / 2;
         }
         int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
@@ -169,6 +56,7 @@
 
     /**
      * Gets the scale that should be applied to the TaskView so that it matches the target
+     * TODO: Remove this method
      */
     public static float getTaskScale(RecentsOrientedState orientedState,
             float srcWidth, float srcHeight, float targetWidth, float targetHeight) {
@@ -180,4 +68,21 @@
             return srcHeight / targetHeight;
         }
     }
+
+    /**
+     * Recursively sets view and all children enabled/disabled.
+     * @param viewGroup Top most parent view to change.
+     * @param enabled True = enable, False = disable.
+     */
+    public static void setViewEnabled(ViewGroup viewGroup, boolean enabled) {
+        viewGroup.setEnabled(enabled);
+        for (int i = 0; i < viewGroup.getChildCount(); i++) {
+            View child = viewGroup.getChildAt(i);
+            if (child instanceof ViewGroup) {
+                setViewEnabled((ViewGroup) child, enabled);
+            } else {
+                child.setEnabled(enabled);
+            }
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 7d52571..a5d4568 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -19,11 +19,13 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Alarm;
 import com.android.launcher3.R;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
@@ -84,6 +86,9 @@
         mSpeedSlow = res.getDimension(R.dimen.motion_pause_detector_speed_slow);
         mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
         mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "creating alarm");
+        }
         mForcePauseTimeout = new Alarm();
         mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
         mMakePauseHarderToTrigger = makePauseHarderToTrigger;
@@ -120,6 +125,9 @@
      * @param pointerIndex Index for the pointer being tracked in the motion event
      */
     public void addPosition(MotionEvent ev, int pointerIndex) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "setting alarm");
+        }
         mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
                 ? HARDER_TRIGGER_TIMEOUT
                 : FORCE_PAUSE_TIMEOUT);
@@ -167,6 +175,9 @@
     }
 
     private void updatePaused(boolean isPaused) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "updatePaused: " + isPaused);
+        }
         if (mDisallowPause) {
             isPaused = false;
         }
@@ -188,6 +199,9 @@
         setOnMotionPauseListener(null);
         mIsPaused = mHasEverBeenPaused = false;
         mSlowStartTime = 0;
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "canceling alarm");
+        }
         mForcePauseTimeout.cancelAlarm();
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index aa6d56a..2d8bba2 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -25,8 +25,8 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateListener;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.quickstep.SysUINavigationMode;
 
@@ -35,23 +35,23 @@
  */
 public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
 
-    public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs,
-            LauncherStateManager stateManager) {
-        super(launcher, sharedPrefs, stateManager);
+    public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs) {
+        super(launcher, sharedPrefs);
 
+        StateManager<LauncherState> stateManager = launcher.getStateManager();
         if (!getBoolean(HOME_BOUNCE_SEEN)) {
-            mStateManager.addStateListener(new StateListener() {
+            stateManager.addStateListener(new StateListener<LauncherState>() {
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
                     boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
                             .get(mLauncher).getMode().hasGestures;
-                    LauncherState prevState = mStateManager.getLastState();
+                    LauncherState prevState = stateManager.getLastState();
 
                     if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
                             && finalState == ALL_APPS && prevState == NORMAL) ||
                             hasReachedMaxCount(HOME_BOUNCE_COUNT))) {
                         mSharedPrefs.edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
-                        mStateManager.removeStateListener(this);
+                        stateManager.removeStateListener(this);
                     }
                 }
             });
@@ -65,27 +65,27 @@
             mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, shelfBounceSeen).apply();
         }
         if (!shelfBounceSeen) {
-            mStateManager.addStateListener(new StateListener() {
+            stateManager.addStateListener(new StateListener<LauncherState>() {
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
-                    LauncherState prevState = mStateManager.getLastState();
+                    LauncherState prevState = stateManager.getLastState();
 
                     if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
                             hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
                         mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
-                        mStateManager.removeStateListener(this);
+                        stateManager.removeStateListener(this);
                     }
                 }
             });
         }
 
         if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
-            mStateManager.addStateListener(new StateListener() {
+            stateManager.addStateListener(new StateListener<LauncherState>() {
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
                     if (finalState == ALL_APPS) {
                         if (incrementEventCount(ALL_APPS_COUNT)) {
-                            mStateManager.removeStateListener(this);
+                            stateManager.removeStateListener(this);
                             mLauncher.getScrimView().updateDragHandleVisibility();
                         }
                     }
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 74daeca..fffbb34 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -17,11 +17,13 @@
 package com.android.quickstep.util;
 
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
@@ -35,7 +37,6 @@
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Handler;
 import android.provider.Settings;
 import android.util.Log;
@@ -44,6 +45,7 @@
 import android.view.Surface;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
@@ -64,7 +66,7 @@
 public final class RecentsOrientedState implements SharedPreferences.OnSharedPreferenceChangeListener {
 
     private static final String TAG = "RecentsOrientedState";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
 
@@ -82,7 +84,7 @@
 
     private @SurfaceRotation int mTouchRotation = ROTATION_0;
     private @SurfaceRotation int mDisplayRotation = ROTATION_0;
-    private @SurfaceRotation int mLauncherRotation = Surface.ROTATION_0;
+    private @SurfaceRotation int mLauncherRotation = ROTATION_0;
 
     // Launcher activity supports multiple orientation, but fallback activity does not
     private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
@@ -94,31 +96,33 @@
     private static final int FLAG_HOME_ROTATION_ALLOWED_IN_PREFS = 1 << 3;
     // If the user has enabled system rotation
     private static final int FLAG_SYSTEM_ROTATION_ALLOWED = 1 << 4;
+    // Multiple orientation is not supported in multiwindow mode
+    private static final int FLAG_MULTIWINDOW_ROTATION_ALLOWED = 1 << 5;
     // Whether to rotation sensor is supported on the device
-    private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 5;
+    private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 6;
     // Whether to enable rotation watcher when multi-rotation is supported
-    private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 6;
+    private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 7;
+    // Enable home rotation for UI tests, ignoring home rotation value from prefs
+    private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 8;
 
     private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
             FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
             | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY
             | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
 
-    private static final int MASK_ACTIVITY_ROTATING =
-            FLAG_HOME_ROTATION_ALLOWED_IN_PREFS | FLAG_SYSTEM_ROTATION_ALLOWED;
-
-    // State for which rotation watcher will be enabled.
-    // We skip it when home rotation is enabled as in that case, activity itself rotates
+    // State for which rotation watcher will be enabled. We skip it when home rotation or
+    // multi-window is enabled as in that case, activity itself rotates.
     private static final int VALUE_ROTATION_WATCHER_ENABLED =
             MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
                     | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
 
+    private final Context mContext;
     private final ContentResolver mContentResolver;
     private final SharedPreferences mSharedPrefs;
     private final OrientationEventListener mOrientationListener;
+    private final WindowSizeStrategy mSizeStrategy;
 
     private final Matrix mTmpMatrix = new Matrix();
-    private final Matrix mTmpInverseMatrix = new Matrix();
 
     private int mFlags;
     private int mPreviousRotation = ROTATION_0;
@@ -128,14 +132,16 @@
      *                              is enabled
      * @see #setRotationWatcherEnabled(boolean)
      */
-    public RecentsOrientedState(Context context, boolean rotationSupportedByActivity,
+    public RecentsOrientedState(Context context, WindowSizeStrategy sizeStrategy,
             IntConsumer rotationChangeListener) {
+        mContext = context;
         mContentResolver = context.getContentResolver();
         mSharedPrefs = Utilities.getPrefs(context);
+        mSizeStrategy = sizeStrategy;
         mOrientationListener = new OrientationEventListener(context) {
             @Override
             public void onOrientationChanged(int degrees) {
-                int newRotation = getRotationForUserDegreesRotated(degrees);
+                int newRotation = getRotationForUserDegreesRotated(degrees, mPreviousRotation);
                 if (newRotation != mPreviousRotation) {
                     mPreviousRotation = newRotation;
                     rotationChangeListener.accept(newRotation);
@@ -143,7 +149,8 @@
             }
         };
 
-        mFlags = rotationSupportedByActivity ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
+        mFlags = sizeStrategy.rotationSupportedByActivity
+                ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
 
         Resources res = context.getResources();
         int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
@@ -157,6 +164,17 @@
         if (mOrientationListener.canDetectOrientation()) {
             mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
         }
+
+        // initialize external flags
+        updateAutoRotateSetting();
+        updateHomeRotationSetting();
+    }
+
+    /**
+     * Sets if the host is in multi-window mode
+     */
+    public void setMultiWindowMode(boolean isMultiWindow) {
+        setFlag(FLAG_MULTIWINDOW_ROTATION_ALLOWED, isMultiWindow);
     }
 
     /**
@@ -168,13 +186,15 @@
      *         false otherwise
      */
     public boolean update(
-            @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation,
-            @SurfaceRotation int launcherRotation) {
+            @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
         if (!isMultipleOrientationSupportedByDevice()) {
             return false;
         }
-        if (mDisplayRotation == displayRotation && mTouchRotation == touchRotation
-                && launcherRotation == mLauncherRotation) {
+
+        int launcherRotation = inferLauncherRotation(displayRotation);
+        if (mDisplayRotation == displayRotation
+                && mTouchRotation == touchRotation
+                && mLauncherRotation == launcherRotation) {
             return false;
         }
 
@@ -182,11 +202,10 @@
         mDisplayRotation = displayRotation;
         mTouchRotation = touchRotation;
 
-        if (canLauncherRotate() || mLauncherRotation == mTouchRotation) {
-            // TODO(b/153476489) Need to determine when launcher is rotated
+        if (mLauncherRotation == mTouchRotation) {
             mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
             if (DEBUG) {
-                Log.d(TAG, "Set Orientation Handler: " + mOrientationHandler);
+                Log.d(TAG, "current RecentsOrientedState: " + this);
             }
             return true;
         }
@@ -199,11 +218,20 @@
             mOrientationHandler = PagedOrientationHandler.PORTRAIT;
         }
         if (DEBUG) {
-            Log.d(TAG, "Set Orientation Handler: " + mOrientationHandler);
+            Log.d(TAG, "current RecentsOrientedState: " + this);
         }
         return true;
     }
 
+    @SurfaceRotation
+    private int inferLauncherRotation(@SurfaceRotation int displayRotation) {
+        if (!isMultipleOrientationSupportedByDevice() || isHomeRotationAllowed()) {
+            return displayRotation;
+        } else {
+            return ROTATION_0;
+        }
+    }
+
     private void setFlag(int mask, boolean enabled) {
         boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
                 && mFlags == VALUE_ROTATION_WATCHER_ENABLED;
@@ -228,7 +256,9 @@
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
-        updateHomeRotationSetting();
+        if (ALLOW_ROTATION_PREFERENCE_KEY.equals(s)) {
+            updateHomeRotationSetting();
+        }
     }
 
     private void updateAutoRotateSetting() {
@@ -242,23 +272,24 @@
     }
 
     /**
-     * Initializes aany system values and registers corresponding change listeners. It must be
-     * paired with {@link #destroy()} call
+     * Initializes any system values and registers corresponding change listeners. It must be
+     * paired with {@link #destroyListeners()} call
      */
-    public void init() {
+    public void initListeners() {
         if (isMultipleOrientationSupportedByDevice()) {
             mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
             mContentResolver.registerContentObserver(
                     Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
                     false, mSystemAutoRotateObserver);
         }
-        initWithoutListeners();
+        updateAutoRotateSetting();
+        updateHomeRotationSetting();
     }
 
     /**
      * Unregisters any previously registered listeners.
      */
-    public void destroy() {
+    public void destroyListeners() {
         if (isMultipleOrientationSupportedByDevice()) {
             mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
             mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
@@ -266,13 +297,8 @@
         setRotationWatcherEnabled(false);
     }
 
-    /**
-     * Initializes the OrientationState without attaching any listeners. This can be used when
-     * the object is short lived.
-     */
-    public void initWithoutListeners() {
-        updateAutoRotateSetting();
-        updateHomeRotationSetting();
+    public void forceAllowRotationForTesting(boolean forceAllow) {
+        setFlag(FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING, forceAllow);
     }
 
     @SurfaceRotation
@@ -296,11 +322,13 @@
     }
 
     public boolean isHomeRotationAllowed() {
-        return (mFlags & FLAG_HOME_ROTATION_ALLOWED_IN_PREFS) != 0;
+        return (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS | FLAG_MULTIWINDOW_ROTATION_ALLOWED))
+                != 0 ||
+                (mFlags & FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING) != 0;
     }
 
     public boolean canLauncherRotate() {
-        return (mFlags & MASK_ACTIVITY_ROTATING) == MASK_ACTIVITY_ROTATING;
+        return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isHomeRotationAllowed();
     }
 
     /**
@@ -331,11 +359,26 @@
         Rect insets = dp.getInsets();
         float fullWidth = dp.widthPx - insets.left - insets.right;
         float fullHeight = dp.heightPx - insets.top - insets.bottom;
-        final float scale = LayoutUtils.getTaskScale(this,
-                fullWidth, fullHeight, taskView.width(), taskView.height());
+
+        if (dp.isMultiWindowMode) {
+            mSizeStrategy.getMultiWindowSize(mContext, dp, outPivot);
+        } else {
+            outPivot.set(fullWidth, fullHeight);
+        }
+        float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height());
+        // We also scale the preview as part of fullScreenParams, so account for that as well.
+        if (fullWidth > 0) {
+            scale = scale * dp.widthPx / fullWidth;
+        }
 
         if (scale == 1) {
             outPivot.set(fullWidth / 2, fullHeight / 2);
+        } else if (dp.isMultiWindowMode) {
+            float denominator = 1 / (scale - 1);
+            // Ensure that the task aligns to right bottom for the root view
+            float y = (scale * taskView.bottom - fullHeight) * denominator;
+            float x = (scale * taskView.right - fullWidth) * denominator;
+            outPivot.set(x, y);
         } else {
             float factor = scale / (scale - 1);
             outPivot.set(taskView.left * factor, taskView.top * factor);
@@ -374,35 +417,57 @@
         */
     }
 
-    public void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
-        mapRectFromRotation(mDisplayRotation, src, screenWidth, screenHeight);
-    }
-
-    public void mapRectFromRotation(int rotation, RectF src, int screenWidth, int screenHeight) {
-        mTmpMatrix.reset();
-        postDisplayRotation(rotation, screenWidth, screenHeight, mTmpMatrix);
-        mTmpMatrix.mapRect(src);
-    }
-
-    public void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
-        mTmpMatrix.reset();
-        postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix);
-        mTmpMatrix.invert(mTmpInverseMatrix);
-        mTmpInverseMatrix.mapRect(src);
-    }
-
     @SurfaceRotation
-    public static int getRotationForUserDegreesRotated(float degrees) {
-        int threshold = 70;
-        if (degrees >= (360 - threshold) || degrees < (threshold)) {
-            return ROTATION_0;
-        } else if (degrees < (90 + threshold)) {
-            return ROTATION_270;
-        } else if (degrees < 180 + threshold) {
-            return ROTATION_180;
-        } else {
-            return ROTATION_90;
+    public static int getRotationForUserDegreesRotated(float degrees, int currentRotation) {
+        if (degrees == ORIENTATION_UNKNOWN) {
+            return currentRotation;
         }
+
+        int threshold = 70;
+        switch (currentRotation) {
+            case ROTATION_0:
+                if (degrees > 180 && degrees < (360 - threshold)) {
+                    return ROTATION_90;
+                }
+                if (degrees < 180 && degrees > threshold) {
+                    return ROTATION_270;
+                }
+                break;
+            case ROTATION_270:
+                if (degrees < (90 - threshold)) {
+                    return ROTATION_0;
+                }
+                if (degrees > (90 + threshold) && degrees < 180) {
+                    return ROTATION_180;
+                }
+                // flip from seascape to landscape
+                if (degrees > (180 + threshold) && degrees < 360) {
+                    return ROTATION_90;
+                }
+                break;
+            case ROTATION_180:
+                if (degrees < (180 - threshold)) {
+                    return ROTATION_270;
+                }
+                if (degrees > (180 + threshold)) {
+                    return ROTATION_90;
+                }
+                break;
+            case ROTATION_90:
+                if (degrees < (270 - threshold) && degrees > 90) {
+                    return ROTATION_180;
+                }
+                if (degrees > (270 + threshold) && degrees < 360) {
+                    return ROTATION_0;
+                }
+                // flip from landscape to seascape
+                if (degrees > threshold && degrees < 180) {
+                    return ROTATION_270;
+                }
+                break;
+        }
+
+        return currentRotation;
     }
 
     public boolean isDisplayPhoneNatural() {
@@ -440,4 +505,21 @@
         return Settings.Global.getInt(
                 context.getContentResolver(), FIXED_ROTATION_TRANSFORM_SETTING_NAME, 1) == 1;
     }
+
+    @NonNull
+    @Override
+    public String toString() {
+        boolean systemRotationOn = (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0;
+        return "["
+                + "this=" + extractObjectNameAndAddress(super.toString())
+                + " mOrientationHandler=" +
+                    extractObjectNameAndAddress(mOrientationHandler.toString())
+                + " mDisplayRotation=" + mDisplayRotation
+                + " mTouchRotation=" + mTouchRotation
+                + " mLauncherRotation=" + mLauncherRotation
+                + " mHomeRotation=" + isHomeRotationAllowed()
+                + " mSystemRotation=" + systemRotationOn
+                + " mFlags=" + mFlags
+                + "]";
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 4cd0206..04308c8 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -25,12 +25,10 @@
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 public abstract class RemoteAnimationProvider {
 
     LauncherAnimationRunner mAnimationRunner;
-    static final int Z_BOOST_BASE = 800570000;
 
     public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets);
@@ -53,24 +51,6 @@
     }
 
     /**
-     * Prepares the given {@param targets} for a remote animation, and should be called with the
-     * transaction from the first frame of animation.
-     *
-     * @param boostModeTargets The mode indicating which targets to boost in z-order above other
-     *                         targets.
-     */
-    static void prepareTargetsForFirstFrame(RemoteAnimationTargetCompat[] targets,
-            TransactionCompat t, int boostModeTargets) {
-        for (RemoteAnimationTargetCompat target : targets) {
-            t.show(target.leash);
-        }
-    }
-
-    public static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
-        return target.prefixOrderIndex;
-    }
-
-    /**
      * @return the target with the lowest opaque layer for a certain app animation, or null.
      */
     public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
@@ -80,7 +60,7 @@
         for (int i = appTargets.length - 1; i >= 0; i--) {
             RemoteAnimationTargetCompat target = appTargets[i];
             if (target.mode == mode && !target.isTranslucent) {
-                int layer = getLayer(target, mode);
+                int layer = target.prefixOrderIndex;
                 if (layer < lowestLayer) {
                     lowestLayer = layer;
                     lowestLayerIndex = i;
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index fa2d338..958ee24 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.quickstep.util.RemoteAnimationProvider.prepareTargetsForFirstFrame;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.ValueAnimator;
@@ -42,7 +41,9 @@
     public void onAnimationUpdate(ValueAnimator valueAnimator) {
         TransactionCompat t = new TransactionCompat();
         if (mFirstFrame) {
-            prepareTargetsForFirstFrame(mTarget.unfilteredApps, t, MODE_CLOSING);
+            for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
+                t.show(target.leash);
+            }
             mFirstFrame = false;
         }
 
diff --git a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
new file mode 100644
index 0000000..1557dfc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
@@ -0,0 +1,181 @@
+/*
+ * 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.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.quickstep.SysUINavigationMode.Mode;
+
+/**
+ * Utility class to wrap different layout behavior for Launcher and RecentsView
+ * TODO: Merge is with {@link com.android.quickstep.BaseActivityInterface} once we remove the
+ * state dependent members from {@link com.android.quickstep.LauncherActivityInterface}
+ */
+public abstract class WindowSizeStrategy {
+
+    private final PointF mTempPoint = new PointF();
+    public final boolean rotationSupportedByActivity;
+
+    private WindowSizeStrategy(boolean rotationSupportedByActivity) {
+        this.rotationSupportedByActivity = rotationSupportedByActivity;
+    }
+
+    /**
+     * Sets the expected window size in multi-window mode
+     */
+    public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
+
+    /**
+     * Calculates the taskView size for the provided device configuration
+     */
+    public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+        calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
+    }
+
+    abstract float getExtraSpace(Context context, DeviceProfile dp);
+
+    private void calculateTaskSize(
+            Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
+        float taskWidth, taskHeight, paddingHorz;
+        Resources res = context.getResources();
+        Rect insets = dp.getInsets();
+        final boolean showLargeTaskSize = showOverviewActions(context);
+
+        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 if (showLargeTaskSize) {
+                paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
+            } else {
+                paddingResId = R.dimen.portrait_task_card_horz_space;
+            }
+            paddingHorz = res.getDimension(paddingResId);
+        }
+
+        float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+        float paddingVert = showLargeTaskSize
+                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
+
+        // 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;
+
+        float availableHeight = launcherVisibleHeight
+                - topIconMargin - extraVerticalSpace - paddingVert;
+        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 + Math.max(topIconMargin,
+                (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+        outRect.set(Math.round(x), Math.round(y),
+                Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+    }
+
+
+    public static final WindowSizeStrategy LAUNCHER_ACTIVITY_SIZE_STRATEGY =
+            new WindowSizeStrategy(true) {
+
+        @Override
+        public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
+            DeviceProfile fullDp = dp.getFullScreenProfile();
+            // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+            // account for system insets
+            out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
+            float halfDividerSize = context.getResources()
+                    .getDimension(R.dimen.multi_window_task_divider_size) / 2;
+
+            if (fullDp.isLandscape) {
+                out.x = out.x / 2 - halfDividerSize;
+            } else {
+                out.y = out.y / 2 - halfDividerSize;
+            }
+        }
+
+        @Override
+        float getExtraSpace(Context context, DeviceProfile dp) {
+            if (dp.isVerticalBarLayout()) {
+                return  0;
+            } else {
+                Resources res = context.getResources();
+                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 actionsHeight = actionsBottomMargin
+                            + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+                    return actionsHeight;
+                } else {
+                    return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
+                            + res.getDimensionPixelSize(
+                                    R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+                            + res.getDimensionPixelSize(
+                                    R.dimen.dynamic_grid_hotseat_bottom_padding);
+                }
+            }
+        }
+    };
+
+    public static final WindowSizeStrategy FALLBACK_RECENTS_SIZE_STRATEGY =
+            new WindowSizeStrategy(false) {
+        @Override
+        public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
+            out.set(dp.widthPx, dp.heightPx);
+        }
+
+        @Override
+        float getExtraSpace(Context context, DeviceProfile dp) {
+            return showOverviewActions(context)
+                    ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
+                    : 0;
+        }
+    };
+
+    static boolean showOverviewActions(Context context) {
+        return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 75fcfe2..bf093fd 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -42,6 +42,7 @@
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.views.RecentsView;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,6 +54,18 @@
     public void setUp() throws Exception {
         super.setUp();
         TaplTestsLauncher3.initialize(this);
+        executeOnLauncher(launcher -> {
+            RecentsView recentsView = launcher.getOverviewPanel();
+            recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(true);
+        });
+    }
+
+    @After
+    public void tearDown() {
+        executeOnLauncher(launcher -> {
+            RecentsView recentsView = launcher.getOverviewPanel();
+            recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false);
+        });
     }
 
     private void startTestApps() throws Exception {
diff --git a/res/color/overview_button.xml b/res/color/overview_button.xml
new file mode 100644
index 0000000..6ac36bf
--- /dev/null
+++ b/res/color/overview_button.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:alpha="1"
+        android:color="?attr/workspaceTextColor"
+        android:state_enabled="true" />
+    <item
+        android:alpha="?android:disabledAlpha"
+        android:color="?attr/workspaceTextColor"
+        android:state_enabled="false" />
+</selector>
\ No newline at end of file
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
index 5506b94..c3c7010 100644
--- a/res/layout/work_profile_edu.xml
+++ b/res/layout/work_profile_edu.xml
@@ -23,13 +23,13 @@
         android:layout_width="match_parent"
         android:layout_height="32dp"
         android:background="@drawable/bottom_sheet_top_border"
-        android:backgroundTint="?android:attr/colorAccent" />
+        android:backgroundTint="?attr/eduHalfSheetBGColor" />
 
     <LinearLayout
         android:id="@+id/view_wrapper"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?android:attr/colorAccent"
+        android:background="?attr/eduHalfSheetBGColor"
         android:orientation="vertical"
         android:paddingLeft="@dimen/bottom_sheet_edu_padding"
         android:paddingRight="@dimen/bottom_sheet_edu_padding">
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 07739c5..b8a8818 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -133,14 +133,14 @@
     <string name="notification_dismissed" msgid="6002233469409822874">"Obaveštenje je odbačeno"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil za Work"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Lični podaci su odvojeni i sakriveni od aplikacija za posao"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"IT administrator vidi aplikacije za posao i podatke"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"IT administrator vidi poslovne aplikacije i podatke"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalje"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Važi"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil za Work je pauziran"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Aplikacije za posao ne mogu da vam šalju obaveštenja, koriste bateriju ni pristupaju lokaciji"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil za Work je pauziran. Aplikacije za posao ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte aplikacije za posao i obaveštenja"</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"Poslovni profil je pauziran"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju ni pristupaju lokaciji"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obaveštenja"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 53d8794..1a118ad 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -134,12 +134,12 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"অফিসের অ্যাপের থেকে ব্যক্তিগত ডেটা আলাদা করে লুকিয়ে রাখা হয়"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"অফিসের অ্যাপ এবং ডেটা আপনার আইটি অ্যাডমিন দেখতে পাবেন"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"আপনার আইটি অ্যাডমিন অফিস অ্যাপ এবং ডেটা দেখতে পাবেন"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"পরের"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুঝেছি"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"অফিস প্রোফাইল বন্ধ করা আছে"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"অফিসের অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, আপনার ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারে না"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"অফিস অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, আপনার ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারে না"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"অফিসের প্রোফাইল পজ করা আছে। অফিসের অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"অফিসের অ্যাপ এবং বিজ্ঞপ্তি বন্ধ করুন"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"অফিস অ্যাপ এবং বিজ্ঞপ্তি বন্ধ করুন"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index e899d49..43e7d03 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -139,7 +139,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Sljedeće"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumijem"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Radni profil je pauziran"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Poslovne aplikacije ne mogu vam slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Radni profil je pauziran. Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obavještenja"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index ca3dfe1..be5945f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -133,12 +133,12 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personenbezogene Daten sind separat und für geschäftliche Apps nicht sichtbar"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personenbezogene Daten sind für geschäftlichen Apps nicht sichtbar oder zugänglich"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Geschäftliche Apps und Daten können von deinem IT-Administrator eingesehen werden"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Weiter"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Arbeitsprofil pausiert"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Geschäftliche Apps können dir Benachrichtigungen senden, deinen Akku verbrauchen oder auf deinen Standort zugreifen"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht nutzen und nicht auf deinen Standort zugreifen"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Das Arbeitsprofil ist pausiert. Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht beanspruchen und nicht auf deinen Standort zugreifen."</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Geschäftliche Apps und Benachrichtigungen pausieren"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Fehler: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index fc7603a..e3dc671 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -133,13 +133,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Προσωπικές"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Εργασίας"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Προφίλ εργασίας"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Τα προσωπικά δεδομένα βρίσκονται σε ξεχωριστή θέση και δεν είναι ορατά από τις εφαρμογές εργασιών"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Οι εφαρμογές εργασιών και τα δεδομένα τους είναι ορατά στον διαχειριστή IT"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Τα προσωπικά δεδομένα βρίσκονται σε ξεχωριστή θέση και δεν είναι ορατά από τις εφαρμογές εργασίας"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Οι εφαρμογές εργασίας και τα δεδομένα τους είναι ορατά στον διαχειριστή IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Επόμενο"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Το κατάλαβα"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Το προφίλ εργασίας έχει τεθεί σε παύση"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Οι εφαρμογές εργασιών δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Οι εφαρμογές εργασίας δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Το προφίλ εργασίας έχει τεθεί σε παύση. Οι εφαρμογές εργασιών δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας."</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Παύση εφαρμογών εργασιών και ειδοποιήσεων"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Παύση εφαρμογών εργασίας και ειδοποιήσεων"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 1eca60f..6a2d292 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -138,7 +138,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps cant send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 1eca60f..6a2d292 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -138,7 +138,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps cant send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 1eca60f..6a2d292 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -138,7 +138,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps cant send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 1eca60f..6a2d292 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -138,7 +138,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps cant send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index db60954..56ae3bd 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -133,13 +133,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Los datos personales están separados y ocultos de las aplicaciones de trabajo, que no pueden acceder a ellos"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Los datos personales están separados y ocultos de las aplicaciones de trabajo"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Tu administrador de TI puede ver tus aplicaciones y datos de trabajo"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Siguiente"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Listo"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de trabajo está en pausa"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Las aplicaciones de trabajo no pueden enviarte notificaciones, gastar tu batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Las aplicaciones de trabajo no pueden enviarte notificaciones, gastar batería ni acceder a tu ubicación"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de trabajo está en pausa. Las aplicaciones de trabajo no pueden enviarte notificaciones, consumir tu batería ni acceder a tu ubicación"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Pausar notificaciones y aplicaciones de trabajo"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Pausa apps y notificaciones de trabajo"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 27ac2fb..cee265d 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -138,7 +138,7 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Järgmine"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selge"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Tööprofiil on peatatud"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurdepääseda"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Tööprofiil on peatatud. Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Peatage töörakendused ja märguanded"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nurjus: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 87b329c..af40f5c 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -131,9 +131,9 @@
     <string name="accessibility_close" msgid="2277148124685870734">"بستن"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"اعلان رد شد"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصی"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"محل کار"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"کاری"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"نمایه کاری"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"داده‌های شخصی مجزا هستند و از دسترس برنامه‌های کاری پنهان هستند"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"داده‌های شخصی از برنامه‌های کاری جدا است و از آن پنهان است"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"برنامه‌های کاری و داده‌ها برای سرپرست فناوری اطلاعات نمایان هستند"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"بعدی"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"متوجه‌ام"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index d72f3bb..c8b2876 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -80,7 +80,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'écran d\'accueil"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 8dbf0fd..0aa2f1b 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -20,7 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
+    <string name="work_folder_name" msgid="3753320833950115786">"ઑફિસ"</string>
     <string name="activity_not_found" msgid="8071924732094499514">"ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"સુરક્ષિત મોડમાં ડાઉનલોડ કરેલ ઍપ્લિકેશન અક્ષમ કરી"</string>
@@ -131,13 +131,13 @@
     <string name="accessibility_close" msgid="2277148124685870734">"બંધ કરો"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"સૂચના છોડી દીધી"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"મનગમતી ઍપ"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"કાર્યાલયની પ્રોફાઇલ"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"ઑફિસની ઍપ"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"ઑફિસની પ્રોફાઇલ"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"વ્યક્તિગત ડેટા ઑફિસ માટેની ઍપથી અલગ અને છુપાવીને રાખેલો છે"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ઑફિસ માટેની ઍપ અને ડેટા તમારા IT વ્યવસ્થાપકને દેખાય છે"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"આગળ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"સમજાઈ ગયું"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"કાર્યાલયની પ્રોફાઇલ થોભાવી છે"</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"ઑફિસની પ્રોફાઇલ થોભાવી છે"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"કાર્યાલયની પ્રોફાઇલ થોભાવી છે. ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ઑફિસ માટેની ઍપ અને નોટિફિકેશન થોભાવો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index cef3b49..246f236 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -133,8 +133,8 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"काम से जुड़े ऐप"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"वर्क प्रोफ़ाइल"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"निजी डेटा को अलग और ऑफ़िस के काम से जुड़े ऐप्लिकेशन से छिपा कर रखा जाता है"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन और डेटा आपके आईटी एडमिन को दिखते हैं"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"निजी डेटा को अलग रखा जाता है. साथ ही, ऑफ़िस के काम से जुड़े ऐप्लिकेशन से छिपा कर रखा जाता है"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन और डेटा, आपके आईटी एडमिन को दिखते हैं"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"आगे बढ़ें"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ठीक है"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"वर्क प्रोफ़ाइल रोक दी गई है"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index fe9a359..46e9cba 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -137,9 +137,9 @@
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"A munkahelyi alkalmazásokat és adatokat látja a rendszergazda"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Tovább"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Értem"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"A munkaprofil szüneteltetve van"</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"A munkaprofil használata szünetel"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"A munkaprofil szüneteltetve van. A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"A munkaprofil használata szünetel. A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Munkahelyi alkalmazások és értesítések szüneteltetése"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Sikertelen: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 33294e1..923f504 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -133,13 +133,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Անձնական"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Անձնական հավելվածները առանձնացված են և թաքցված աշխատանքային հավելվածներից։"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Անձնական հավելվածները թաքցված են և առանձնացված աշխատանքային հավելվածներից։"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Աշխատանքային հավելվածներն ու դրանց տվյալները տեսանելի են ձեր ադմինիստրատորին"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Առաջ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Եղավ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Աշխատանքային պրոֆիլը դադարեցված է"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Աշխատանքային հավելվածները չեն կարող օգտագործել ձեր մարտկոցը, գտնվելու վայրի մասին տվյալները և ուղարկել ձեզ ծանուցումներ։"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող օգտագործել ձեր մարտկոցը, գտնվելու վայրի մասին տվյալները և ուղարկել ձեզ ծանուցումներ։"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել և օգտագործել ձեր մարտկոցն ու տեղադրության տվյալները։"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել և օգտագործել ձեր մարտկոցն ու տեղադրության տվյալները։"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Դադարեցնել աշխատանքային հավելվածներն ու ծանուցումները"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Չհաջողվեց կատարել գործողությունը (<xliff:g id="WHAT">%1$s</xliff:g>)"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 1d83d64..8999506 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -133,7 +133,7 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personali"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"I dati personali sono separati e sono nascosti alle app di lavoro"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"I dati personali sono separati e non sono visibili nelle app di lavoro"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"I dati e le app di lavoro sono visibili all\'amministratore IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Avanti"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index b5c3c0b..87bafc8 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -133,7 +133,7 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人用"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"仕事用"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"個人データは仕事用アプリとは別個に保存され、一緒に表示されません"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"個人データは仕事用アプリとは別に保存され、一緒に表示されません"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"仕事用アプリと仕事用データは IT 管理者に公開されます"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"次へ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 01a6b25..47ef01d 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -138,8 +138,8 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Кийинки"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түшүндүм"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Жумуш профили тындырылган"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Жумуш колдонмолору билдирмелерди жөнөтүп, батареяңызды колдонуп же кайда жүргөнүңүздү көрө албайт"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Жумуш профили тындырылган. Жумуш колдонмолору билдирмелерди жөнөтүп, батареяңызды колдонуп же кайда жүргөнүңүздү көрө албайт"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Жумуш профили тындырылган. Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Жумуш колдонмолорун жана билдирмелерди тындыруу"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index f69248c..4667ede 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -53,11 +53,11 @@
     <string name="app_info_drop_target_label" msgid="692894985365717661">"अनुप्रयोगको जानकारी"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
+    <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा अनुप्रयोगलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा एपलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
     <string name="permlab_write_settings" msgid="3574213698004620587">"गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा अनुप्रयोगलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा एपलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले फोन कलहरू गर्न अनुमति छैन"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"समस्या लोडिङ गर्ने विजेट"</string>
     <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
@@ -139,8 +139,7 @@
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको ब्याट्री प्रयोग गर्न वा तपाईंको स्थानसम्बन्धी जानकारीमाथि पहुँच राख्न सक्दैनन्"</string>
-    <!-- no translation found for work_apps_paused_content_description (7553586952985486433) -->
-    <skip />
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ। कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको यन्त्रको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"कामसम्बन्धी एप र सूचनाहरू अस्थायी रूपमा रोक्का गर्नुहोस्"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index b2cf426..87a1948 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -20,7 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="649227358658669779">"ଲଞ୍ଚର୍3"</string>
-    <string name="work_folder_name" msgid="3753320833950115786">"କାମ"</string>
+    <string name="work_folder_name" msgid="3753320833950115786">"ୱାର୍କ"</string>
     <string name="activity_not_found" msgid="8071924732094499514">"ଆପ୍‌ ଇନଷ୍ଟଲ୍‌ ହୋଇନାହିଁ"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"ଆପ୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ନିରାପଦ ମୋଡରେ ଡାଉନଲୋଡ୍‌ ହେଇଥିବା ଆପ୍‌ ଅକ୍ଷମ କରାଗଲା"</string>
@@ -131,7 +131,7 @@
     <string name="accessibility_close" msgid="2277148124685870734">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"ବିଜ୍ଞପ୍ତି ଖାରଜ କରାଗଲା"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"ବ୍ୟକ୍ତିଗତ"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"କାମ"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"ୱାର୍କ"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ବ୍ୟକ୍ତିଗତ ଡାଟା କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଠାରୁ ପୃଥକ୍ ଓ ଲୁକ୍କାୟିତ ଅଟେ"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଓ ଡାଟା ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଏ"</string>
@@ -139,7 +139,7 @@
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ବିରତ କରାଯାଇଛି"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ ନାହିଁ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ବିରତ କରାଯାଇଛି। କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ବିରତ କରାଯାଇଛି। କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରନ୍ତୁ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index ff4acfa..bfaef66 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -133,10 +133,10 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Zamknij"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"Powiadomienie odrzucone"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobiste"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Służbowe"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil służbowy"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"Do pracy"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil do pracy"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Dane osobowe znajdują się w innym miejscu i są niewidoczne dla aplikacji do pracy"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Dane z profilu do pracy są widoczne dla Twojego administratora IT"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Dane i aplikacje z profilu do pracy są widoczne dla Twojego administratora IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalej"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Wstrzymano profil do pracy"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b8b4b3d..c6dcc6f 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -21,8 +21,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
     <string name="work_folder_name" msgid="3753320833950115786">"Trabalho"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"A aplicação não está instalada."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"A aplicação não está disponível"</string>
+    <string name="activity_not_found" msgid="8071924732094499514">"A app não está instalada."</string>
+    <string name="activity_not_available" msgid="7456344436509528827">"A app não está disponível"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicação transferida desativada no Modo de segurança"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no Modo de segurança"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
@@ -36,7 +36,7 @@
     <string name="place_automatically" msgid="8064208734425456485">"Adicionar automaticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar aplicações"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"A carregar aplicações…"</string>
-    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhuma aplicação correspondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhuma app correspondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais aplicações"</string>
     <string name="label_application" msgid="8531721983832654978">"Aplicação"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
@@ -50,23 +50,23 @@
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ecrã principal"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicação"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da app"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador."</string>
+    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma app adicionar atalhos sem a intervenção do utilizador."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ler definições e atalhos do Ecrã Principal"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite à aplicação ler as definições e os atalhos no Ecrã Principal."</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite à app ler as definições e os atalhos no Ecrã Principal."</string>
     <string name="permlab_write_settings" msgid="3574213698004620587">"escrever definições e atalhos do Ecrã principal"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite à aplicação alterar as definições e os atalhos no Ecrã Principal."</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite à app alterar as definições e os atalhos no Ecrã Principal."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"O <xliff:g id="APP_NAME">%1$s</xliff:g> não tem autorização para efetuar chamadas telefónicas"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma aplicação de sistema e não pode ser desinstalada."</string>
+    <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma app de sistema e não pode ser desinstalada."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edite o nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
     <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
-      <item quantity="other">A aplicação <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
-      <item quantity="one">A aplicação <xliff:g id="APP_NAME_0">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação</item>
+      <item quantity="other">A app <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
+      <item quantity="one">A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
@@ -96,8 +96,8 @@
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
-    <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicação não está instalada"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente."</string>
+    <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta app não está instalada"</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A app deste ícone não está instalada. Pode removê-lo ou pesquisar a app e instalá-la manualmente."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"A transferir o <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"A aguardar a instalação do <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 656ef2b..bf0b837 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -131,7 +131,7 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"Notificação dispensada"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoais"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Comerciais"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Os dados pessoais ficam separados e ocultos dos apps de trabalho"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Os dados de apps de trabalho ficam visíveis para seu administrador de TI"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 12ccaf6..084bbdb 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -136,7 +136,7 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Рабочие"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Рабочий профиль"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Личные данные скрыты от рабочих приложений и недоступны им"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Рабочие приложения и данные видны системному администратору"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Рабочие приложения и данные видны системному администратору."</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Далее"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ОК"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Рабочий профиль приостановлен"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 4bbc9b3..12e58fc 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -135,13 +135,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobné"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovné"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovný profil"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobné údaje sú od pracovných aplikácií oddelené a nie je ich vidieť"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Pracovné aplikácie a údaje vidí správca IT"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobné údaje sú oddelené a sú pred pracovnými aplikáciami skryté"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Pracovné aplikácie a údaje môže vidieť váš správca IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Ďalej"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Dobre"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovný profil je pozastavený"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Pracovný profil je pozastavený. Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Pozastavenie pracovných aplikácií a upozornení"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Pozastavte pracovné aplikácie a upozornenia"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index eaa6bce..c41f12b 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -133,14 +133,14 @@
     <string name="notification_dismissed" msgid="6002233469409822874">"Обавештење је одбачено"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Личне"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Пословне"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Профил за Work"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Пословни профил"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Лични подаци су одвојени и сакривени од апликација за посао"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ИТ администратор види апликације за посао и податке"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ИТ администратор види пословне апликације и податке"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Даље"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Важи"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"Профил за Work је паузиран"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Апликације за посао не могу да вам шаљу обавештења, користе батерију ни приступају локацији"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Профил за Work је паузиран. Апликације за посао не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Паузирајте апликације за посао и обавештења"</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"Пословни профил је паузиран"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Пословне апликације не могу да вам шаљу обавештења, користе батерију ни приступају локацији"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Пословни профил је паузиран. Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Паузирајте пословне апликације и обавештења"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Није успело: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6a6bb76..77c6aad 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -136,7 +136,7 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Wasifu wa kazini"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Data ya binafsi ni tofauti na haionyeshwi kwenye programu za kazini"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Programu na data ya kazini huonyeshwa kwa msimamizi wako wa TEHAMA"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Programu na data ya kazini huonekana kwa msimamizi wako wa TEHAMA"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Endelea"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Nimeelewa"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Wasifu wa kazini umesimamishwa"</string>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index eb9af97..09bdaaf 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,4 +1,3 @@
 <resources>
-    <bool name="is_tablet">true</bool>
     <bool name="allow_rotation">true</bool>
 </resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index 94cffcb..1f401c4 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -1,6 +1,5 @@
 <resources>
     <bool name="config_largeHeap">true</bool>
-    <bool name="is_large_tablet">true</bool>
 
 <!-- All Apps & Widgets -->
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 08f987e..eb8823d 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -46,7 +46,7 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Ilovalar ro‘yxati"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Shaxsiy ilovalar ro‘yxati"</string>
-    <string name="all_apps_button_work_label" msgid="7270707118948892488">"Ishchi ilovalar ro‘yxati"</string>
+    <string name="all_apps_button_work_label" msgid="7270707118948892488">"Ishga oid ilovalar ro‘yxati"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Bosh sahifa"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
@@ -131,8 +131,8 @@
     <string name="accessibility_close" msgid="2277148124685870734">"Yopish"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"Bildirishnoma yopildi"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Shaxsiy"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Ishchi"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ishchi profil"</string>
+    <string name="all_apps_work_tab" msgid="4884822796154055118">"Ish"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ish profili"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Shaxsiy maʼlumotlar ishga oid ilovalardan alohida va berkitilgan"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Ishga oid ilovalar va maʼlumotlarni AT administratoringiz koʻra oladi"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Keyingisi"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 314195e..e9b6db1 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -134,11 +134,11 @@
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Cơ quan"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Hồ sơ công việc"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Dữ liệu cá nhân được lưu trữ riêng biệt và ẩn khỏi các ứng dụng công việc"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Quản trị viên CNTT của bạn có thể xem ứng dụng công việc và dữ liệu"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Quản trị viên CNTT của bạn có thể xem dữ liệu và các ứng dụng công việc"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Tiếp theo"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Hồ sơ công việc của bạn đã bị tạm dừng"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"Ứng dụng công việc không thể gửi thông báo cho bạn, sử dụng pin hoặc xem dữ liệu vị trí"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"Các ứng dụng công việc không thể gửi thông báo cho bạn, sử dụng pin hoặc truy cập thông tin vị trí của bạn"</string>
     <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Hồ sơ công việc của bạn đã bị tạm dừng. Các ứng dụng công việc không thể: gửi thông báo cho bạn, sử dụng pin hoặc xem dữ liệu vị trí"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Tạm dừng các ứng dụng và thông báo liên quan tới công việc"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Không thực hiện được thao tác: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index eb448e5..c302f71 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -133,13 +133,13 @@
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"个人"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作资料"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"个人数据与工作应用相互独立,两者不会同时显示"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"工作应用与个人数据相互独立,它们无法获取此类数据"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"您的 IT 管理员可以查看工作应用和工作数据"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"继续"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
-    <string name="work_apps_paused_title" msgid="2389865654362803723">"工作资料已暂停使用"</string>
-    <string name="work_apps_paused_body" msgid="5388070126389079077">"工作应用无法向您发送通知、使用电池电量,也无法获取您的位置信息"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作资料已暂停使用。工作应用无法向您发送通知、不能使用电池电量,也无法获取您的位置信息"</string>
+    <string name="work_apps_paused_title" msgid="2389865654362803723">"工作资料已被暂停"</string>
+    <string name="work_apps_paused_body" msgid="5388070126389079077">"现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作资料已被暂停。现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"暂停工作应用和工作通知"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失败:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index d4cb6d7..2875006 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -35,6 +35,7 @@
     <attr name="widgetsTheme" format="reference" />
     <attr name="loadingIconColor" format="color" />
     <attr name="iconOnlyShortcutColor" format="color"/>
+    <attr name="eduHalfSheetBGColor" format="color"/>
 
     <attr name="folderDotColor" format="color" />
     <attr name="folderFillColor" format="color" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 41bb909..603dc91 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,8 +1,6 @@
 <resources>
     <!-- Miscellaneous -->
     <bool name="config_largeHeap">false</bool>
-    <bool name="is_tablet">false</bool>
-    <bool name="is_large_tablet">false</bool>
     <bool name="allow_rotation">false</bool>
 
     <integer name="extracted_color_gradient_alpha">153</integer>
@@ -132,7 +130,7 @@
     <item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">800</item>
 
     <item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.8</item>
-    <item name="horizontal_spring_stiffness" type="dimen" format="float">400</item>
+    <item name="horizontal_spring_stiffness" type="dimen" format="float">250</item>
 
     <item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
     <item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5f4bd8e..2efa66f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -103,9 +103,9 @@
     <!-- Label for install drop target. [CHAR_LIMIT=20] -->
     <string name="install_drop_target_label">Install</string>
     <!-- Label for install dismiss prediction. -->
-    <string translatable="false" name="dismiss_prediction_label">Don\'t suggest app</string>
+    <string name="dismiss_prediction_label">Don\'t suggest app</string>
     <!-- Label for pinning predicted app. -->
-    <string name="pin_prediction" translatable="false">Pin Prediction</string>
+    <string name="pin_prediction">Pin Prediction</string>
 
 
     <!-- Permissions: -->
@@ -342,7 +342,7 @@
     <!--- heading shown when user opens work apps tab while work apps are paused -->
     <string name="work_apps_paused_title">Work profile is paused</string>
     <!--- body shown when user opens work apps tab while work apps are paused -->
-    <string name="work_apps_paused_body">Work apps can\’t send you notifications, use your battery, or access your location</string>
+    <string name="work_apps_paused_body">Work apps can\'t send you notifications, use your battery, or access your location</string>
     <!-- content description for paused work apps list -->
     <string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a2c0f23..26b7205 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -53,6 +53,7 @@
         <item name="loadingIconColor">#CCFFFFFF</item>
         <item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
         <item name="workProfileOverlayTextColor">#FF212121</item>
+        <item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
 
         <item name="android:windowTranslucentStatus">false</item>
         <item name="android:windowTranslucentNavigation">false</item>
@@ -90,7 +91,7 @@
         <item name="android:textColorHint">#A0FFFFFF</item>
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
         <item name="android:colorPrimary">#FF212121</item>
-        <item name="allAppsScrimColor">#FF212121</item>
+        <item name="allAppsScrimColor">#FF000000</item>
         <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
@@ -106,6 +107,7 @@
         <item name="loadingIconColor">#99FFFFFF</item>
         <item name="iconOnlyShortcutColor">#B3FFFFFF</item>
         <item name="workProfileOverlayTextColor">@android:color/white</item>
+        <item name="eduHalfSheetBGColor">#DD000000</item>
     </style>
 
     <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
@@ -229,9 +231,7 @@
 
     <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
     <style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
-    <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
-        <item name="android:textStyle">bold</item>
-    </style>
+    <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
     <style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
 
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index e4f201c..239d8a3 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
 
 import android.app.ActivityOptions;
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Process;
@@ -31,6 +32,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.ActionMode;
+import android.view.Display;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Toast;
@@ -181,9 +183,8 @@
                         sourceContainer);
             }
             getUserEventDispatcher().logAppLaunch(v, intent, user);
-
-            getStatsLogManager().log(APP_LAUNCH_TAP, item == null ? null
-                    : item.buildProto(null, null));
+            getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item == null ? null
+                    : item.buildProto(null));
             return true;
         } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
@@ -270,4 +271,16 @@
     }
 
     protected abstract void reapplyUi();
+
+    protected Rect getMultiWindowDisplaySize() {
+        if (Utilities.ATLEAST_R) {
+            return new Rect(getWindowManager().getCurrentWindowMetrics().getBounds());
+        }
+        // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
+        // the app window size
+        Display display = getWindowManager().getDefaultDisplay();
+        Point mwSize = new Point();
+        display.getSize(mwSize);
+        return new Rect(0, 0, mwSize.x, mwSize.y);
+    }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d7d4a27..79ed2b8 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -722,11 +722,27 @@
     }
 
     @Override
-    public void getVisualDragBounds(Rect bounds) {
+    public void getWorkspaceVisualDragBounds(Rect bounds) {
         DeviceProfile grid = mActivity.getDeviceProfile();
         BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
     }
 
+    private int getIconSizeForDisplay(int display) {
+        DeviceProfile grid = mActivity.getDeviceProfile();
+        switch (display) {
+            case DISPLAY_ALL_APPS:
+                return grid.allAppsIconSizePx;
+            case DISPLAY_WORKSPACE:
+            case DISPLAY_FOLDER:
+            default:
+                return grid.iconSizePx;
+        }
+    }
+
+    public void getSourceVisualDragBounds(Rect bounds) {
+        BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));
+    }
+
     @Override
     public void prepareDrawDragView() {
         if (getIcon() instanceof FastBitmapDrawable) {
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/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a5f98c0..51b21aa 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -32,6 +32,10 @@
 
 public class DeviceProfile {
 
+    private static final float TABLET_MIN_DPS = 600;
+    private static final float LARGE_TABLET_MIN_DPS = 720;
+
+
     public final InvariantDeviceProfile inv;
     private final DefaultDisplay.Info mInfo;
 
@@ -45,6 +49,8 @@
     public final boolean isLandscape;
     public final boolean isMultiWindowMode;
 
+    public final int windowX;
+    public final int windowY;
     public final int widthPx;
     public final int heightPx;
     public final int availableWidthPx;
@@ -133,13 +139,16 @@
     public DotRenderer mDotRendererWorkSpace;
     public DotRenderer mDotRendererAllApps;
 
-    public DeviceProfile(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info,
+    DeviceProfile(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info,
             Point minSize, Point maxSize, int width, int height, boolean isLandscape,
-            boolean isMultiWindowMode, boolean transposeLayoutWithOrientation) {
+            boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
+            Point windowPosition) {
 
         this.inv = inv;
         this.isLandscape = isLandscape;
         this.isMultiWindowMode = isMultiWindowMode;
+        windowX = windowPosition.x;
+        windowY = windowPosition.y;
 
         // Determine sizes.
         widthPx = width;
@@ -153,11 +162,12 @@
         }
 
         mInfo = info;
-        Resources res = context.getResources();
 
         // Constants from resources
-        isTablet = res.getBoolean(R.bool.is_tablet);
-        isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
+        float swDPs = Utilities.dpiFromPx(
+                Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
+        isTablet = swDPs >= TABLET_MIN_DPS;
+        isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS;
         isPhone = !isTablet && !isLargeTablet;
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@@ -165,10 +175,10 @@
         // Some more constants
         this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
 
-        context = getContext(context, isVerticalBarLayout()
+        context = getContext(context, info, isVerticalBarLayout()
                 ? Configuration.ORIENTATION_LANDSCAPE
                 : Configuration.ORIENTATION_PORTRAIT);
-        res = context.getResources();
+        final Resources res = context.getResources();
 
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
         desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
@@ -244,6 +254,7 @@
         return new Builder(context, inv, mInfo)
                 .setSizeRange(size, size)
                 .setSize(widthPx, heightPx)
+                .setWindowPosition(windowX, windowY)
                 .setMultiWindowMode(isMultiWindowMode);
     }
 
@@ -254,10 +265,11 @@
     /**
      * TODO: Move this to the builder as part of setMultiWindowMode
      */
-    public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+    public DeviceProfile getMultiWindowProfile(Context context, Rect windowPosition) {
         // We take the minimum sizes of this profile and it's multi-window variant to ensure that
         // the system decor is always excluded.
-        mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
+        Point mwSize = new Point(Math.min(availableWidthPx, windowPosition.width()),
+                Math.min(availableHeightPx, windowPosition.height()));
 
         // In multi-window mode, we can have widthPx = availableWidthPx
         // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
@@ -265,6 +277,7 @@
         DeviceProfile profile = toBuilder(context)
                 .setSizeRange(mwSize, mwSize)
                 .setSize(mwSize.x, mwSize.y)
+                .setWindowPosition(windowPosition.left, windowPosition.top)
                 .setMultiWindowMode(true)
                 .build();
 
@@ -286,7 +299,7 @@
     }
 
     /**
-     * Inverse of {@link #getMultiWindowProfile(Context, Point)}
+     * Inverse of {@link #getMultiWindowProfile(Context, Rect)}
      * @return device profile corresponding to the current orientation in non multi-window mode.
      */
     public DeviceProfile getFullScreenProfile() {
@@ -624,10 +637,11 @@
         }
     }
 
-    private static Context getContext(Context c, int orientation) {
-        Configuration context = new Configuration(c.getResources().getConfiguration());
-        context.orientation = orientation;
-        return c.createConfigurationContext(context);
+    private static Context getContext(Context c, DefaultDisplay.Info info, int orientation) {
+        Configuration config = new Configuration(c.getResources().getConfiguration());
+        config.orientation = orientation;
+        config.densityDpi = info.metrics.densityDpi;
+        return c.createConfigurationContext(config);
     }
 
     /**
@@ -649,6 +663,7 @@
         private InvariantDeviceProfile mInv;
         private DefaultDisplay.Info mInfo;
 
+        private final Point mWindowPosition = new Point();
         private Point mMinSize, mMaxSize;
         private int mWidth, mHeight;
 
@@ -682,6 +697,14 @@
             return this;
         }
 
+        /**
+         * Sets the window position if not full-screen
+         */
+        public Builder setWindowPosition(int x, int y) {
+            mWindowPosition.set(x, y);
+            return this;
+        }
+
         public Builder setTransposeLayoutWithOrientation(boolean transposeLayoutWithOrientation) {
             mTransposeLayoutWithOrientation = transposeLayoutWithOrientation;
             return this;
@@ -690,7 +713,7 @@
         public DeviceProfile build() {
             return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize,
                     mWidth, mHeight, mIsLandscape, mIsMultiWindowMode,
-                    mTransposeLayoutWithOrientation);
+                    mTransposeLayoutWithOrientation, mWindowPosition);
         }
     }
 
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 0a0f9ad..0b0983c 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -25,6 +25,8 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.FolderNameProvider;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
 
 /**
@@ -75,6 +77,11 @@
          * DragView represents. May be an actual View class or a virtual stand-in */
         public DraggableView originalView = null;
 
+        /** Used for matching DROP event with its corresponding DRAG event on the server side. */
+        public final InstanceId logInstanceId =
+                new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
+                    .newInstanceId();
+
         public DragObject(Context context) {
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
                 folderNameProvider = FolderNameProvider.newInstance(context);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 070a392..59476dd 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,23 +18,30 @@
 
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
+import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
+import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
+import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.NO_OFFSET;
 import static com.android.launcher3.LauncherState.NO_SCALE;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 
 import android.animation.Animator;
@@ -57,11 +64,9 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.os.Handler;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
@@ -69,7 +74,6 @@
 import android.text.method.TextKeyListener;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.Display;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
@@ -82,18 +86,19 @@
 import android.view.animation.OvershootInterpolator;
 import android.widget.Toast;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragController;
@@ -124,6 +129,10 @@
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.qsb.QsbContainerView;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
@@ -187,7 +196,7 @@
 /**
  * Default launcher application.
  */
-public class Launcher extends BaseDraggingActivity implements LauncherExterns,
+public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
         Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
     public static final String TAG = "Launcher";
 
@@ -234,7 +243,7 @@
     public static final String ON_RESUME_EVT = "Launcher.onResume";
     public static final String ON_NEW_INTENT_EVT = "Launcher.onNewIntent";
 
-    private LauncherStateManager mStateManager;
+    private StateManager<LauncherState> mStateManager;
 
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
 
@@ -318,10 +327,6 @@
 
     private RotationHelper mRotationHelper;
 
-    final Handler mHandler = new Handler();
-    private final Runnable mHandleDeferredResume = this::handleDeferredResume;
-    private boolean mDeferredResumePending;
-
     private float mCurrentAssistantVisibility = 0f;
 
     protected LauncherOverlayManager mOverlayManager;
@@ -368,9 +373,9 @@
 
         mDragController = new DragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
-        mStateManager = new LauncherStateManager(this);
+        mStateManager = new StateManager<>(this, NORMAL);
 
-        mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs, mStateManager);
+        mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
 
         mAppWidgetManager = new WidgetManagerHelper(this);
         mAppWidgetHost = new LauncherAppWidgetHost(this,
@@ -433,7 +438,7 @@
 
         mRotationHelper.initialize();
 
-        mStateManager.addStateListener(new LauncherStateManager.StateListener() {
+        mStateManager.addStateListener(new StateListener<LauncherState>() {
 
             @Override
             public void onStateTransitionComplete(LauncherState finalState) {
@@ -460,9 +465,8 @@
         return new LauncherOverlayManager() { };
     }
 
-    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
-            LauncherStateManager stateManager) {
-        return new OnboardingPrefs<>(this, sharedPrefs, stateManager);
+    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+        return new OnboardingPrefs<>(this, sharedPrefs);
     }
 
     public OnboardingPrefs getOnboardingPrefs() {
@@ -516,13 +520,9 @@
     }
 
     @Override
-    public void reapplyUi() {
-        reapplyUi(true /* cancelCurrentAnimation */);
-    }
-
     public void reapplyUi(boolean cancelCurrentAnimation) {
         getRootView().dispatchInsets();
-        getStateManager().reapplyState(cancelCurrentAnimation);
+        super.reapplyUi(cancelCurrentAnimation);
     }
 
     @Override
@@ -560,12 +560,8 @@
         // Load configuration-specific DeviceProfile
         mDeviceProfile = idp.getDeviceProfile(this);
         if (isInMultiWindowMode()) {
-            // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
-            // the app window size
-            Display display = getWindowManager().getDefaultDisplay();
-            Point mwSize = new Point();
-            display.getSize(mwSize);
-            mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
+            mDeviceProfile = mDeviceProfile.getMultiWindowProfile(
+                    this, getMultiWindowDisplaySize());
         }
 
         onDeviceProfileInitiated();
@@ -580,7 +576,8 @@
         return mFocusHandler;
     }
 
-    public LauncherStateManager getStateManager() {
+    @Override
+    public StateManager<LauncherState> getStateManager() {
         return mStateManager;
     }
 
@@ -887,11 +884,7 @@
 
     @Override
     protected void onStop() {
-        final boolean wasActive = isUserActive();
-        final LauncherState origState = getStateManager().getState();
-        final int origDragLayerChildCount = mDragLayer.getChildCount();
         super.onStop();
-
         if (mDeferOverlayCallbacks) {
             checkIfOverlayStillDeferred();
         } else {
@@ -899,30 +892,8 @@
         }
 
         logStopAndResume(Action.Command.STOP);
-
         mAppWidgetHost.setListenIfResumed(false);
-
         NotificationListener.removeNotificationsChangedListener();
-        getStateManager().moveToRestState();
-
-        // Workaround for b/78520668, explicitly trim memory once UI is hidden
-        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
-
-        if (wasActive) {
-            // The expected condition is that this activity is stopped because the device goes to
-            // sleep and the UI may have noticeable changes.
-            mDragLayer.post(() -> {
-                if ((!getStateManager().isInStableState(origState)
-                        // The drag layer may be animating (e.g. dismissing QSB).
-                        || mDragLayer.getAlpha() < 1
-                        // Maybe an ArrowPopup is closed.
-                        || mDragLayer.getChildCount() != origDragLayerChildCount)) {
-                    onUiChangedWhileSleeping();
-                }
-            });
-        }
-
-        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStop");
     }
 
     @Override
@@ -936,38 +907,29 @@
 
         mAppWidgetHost.setListenIfResumed(true);
         TraceHelper.INSTANCE.endSection(traceToken);
-        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStart");
     }
 
-    private void handleDeferredResume() {
-        if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
-            logStopAndResume(Action.Command.RESUME);
-            getUserEventDispatcher().startSession();
+    @Override
+    @CallSuper
+    protected void onDeferredResumed() {
+        logStopAndResume(Action.Command.RESUME);
+        getUserEventDispatcher().startSession();
 
-            AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
+        AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
 
-            // Process any items that were added while Launcher was away.
-            InstallShortcutReceiver.disableAndFlushInstallQueue(
-                    InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+        // Process any items that were added while Launcher was away.
+        InstallShortcutReceiver.disableAndFlushInstallQueue(
+                InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
 
-            // Refresh shortcuts if the permission changed.
-            mModel.refreshShortcutsIfRequired();
+        // Refresh shortcuts if the permission changed.
+        mModel.refreshShortcutsIfRequired();
 
-            // Set the notification listener and fetch updated notifications when we resume
-            NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+        // Set the notification listener and fetch updated notifications when we resume
+        NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
-            DiscoveryBounce.showForHomeIfNeeded(this);
-
-            onDeferredResumed();
-            addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
-
-            mDeferredResumePending = false;
-        } else {
-            mDeferredResumePending = true;
-        }
+        DiscoveryBounce.showForHomeIfNeeded(this);
     }
 
-    protected void onDeferredResumed() { }
 
     private void logStopAndResume(int command) {
         int containerType = mStateManager.getState().containerType;
@@ -988,7 +950,8 @@
         if (!mDeferOverlayCallbacks) {
             return;
         }
-        if (isStarted() && (!hasBeenResumed() || mStateManager.getState().disableInteraction)) {
+        if (isStarted() && (!hasBeenResumed()
+                || mStateManager.getState().hasFlag(FLAG_NON_INTERACTIVE))) {
             return;
         }
         mDeferOverlayCallbacks = false;
@@ -1015,21 +978,51 @@
         return mOverlayManager;
     }
 
+    @Override
     public void onStateSetStart(LauncherState state) {
-        if (mDeferredResumePending) {
-            handleDeferredResume();
-        }
+        super.onStateSetStart(state);
         if (mDeferOverlayCallbacks) {
             scheduleDeferredCheck();
         }
         addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
+
+        if (state.hasFlag(FLAG_CLOSE_POPUPS)) {
+            AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE));
+        }
+
+        if (state == SPRING_LOADED) {
+            // Prevent any Un/InstallShortcutReceivers from updating the db while we are
+            // not on homescreen
+            InstallShortcutReceiver.enableInstallQueue(FLAG_DRAG_AND_DROP);
+            getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
+
+            mWorkspace.showPageIndicatorAtCurrentScroll();
+            mWorkspace.setClipChildren(false);
+        }
+        // When multiple pages are visible, show persistent page indicator
+        mWorkspace.getPageIndicator().setShouldAutoHide(!state.hasFlag(FLAG_MULTI_PAGE));
     }
 
+    @Override
     public void onStateSetEnd(LauncherState state) {
+        super.onStateSetStart(state);
         getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
-        getWorkspace().setClipChildren(!state.disablePageClipping);
+        getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
+
         finishAutoCancelActionMode();
         removeActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
+
+        // dispatch window state changed
+        getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
+        AccessibilityManagerCompat.sendStateEventToTest(this, state.ordinal);
+
+        if (state == NORMAL) {
+            // Re-enable any Un/InstallShortcutReceiver and now process any queued items
+            InstallShortcutReceiver.disableAndFlushInstallQueue(FLAG_DRAG_AND_DROP, this);
+
+            // Clear any rotation locks when going to normal state
+            getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
+        }
     }
 
     @Override
@@ -1038,9 +1031,6 @@
                 TraceHelper.FLAG_UI_EVENT);
         super.onResume();
 
-        mHandler.removeCallbacks(mHandleDeferredResume);
-        Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
-
         if (!mOnResumeCallbacks.isEmpty()) {
             final ArrayList<OnResumeCallback> resumeCallbacks = new ArrayList<>(mOnResumeCallbacks);
             mOnResumeCallbacks.clear();
@@ -1083,10 +1073,6 @@
         }
     }
 
-    public boolean isInState(LauncherState state) {
-        return mStateManager.getState() == state;
-    }
-
     /**
      * Restores the previous state, if it exists.
      *
@@ -1100,7 +1086,7 @@
         int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal);
         LauncherState[] stateValues = LauncherState.values();
         LauncherState state = stateValues[stateOrdinal];
-        if (!state.disableRestore) {
+        if (!state.shouldDisableRestore()) {
             mStateManager.goToState(state, false /* animated */);
         }
 
@@ -1325,8 +1311,6 @@
         }
     };
 
-    protected void onUiChangedWhileSleeping() { }
-
     private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         mWorkspace.updateNotificationDots(updatedDots);
         mAppsView.getAppsStore().updateNotificationDots(updatedDots);
@@ -2196,6 +2180,9 @@
         workspace.requestLayout();
     }
 
+    @Override
+    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
+
     /**
      * Add the views for a widget to the workspace.
      */
@@ -2688,7 +2675,7 @@
         return super.onKeyUp(keyCode, event);
     }
 
-    protected StateHandler[] createStateHandlers() {
+    protected StateHandler<LauncherState>[] createStateHandlers() {
         return new StateHandler[] { getAllAppsController(), getWorkspace() };
     }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 2f38037..14e604d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.PredictionModel;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.InstallSessionTracker;
@@ -57,6 +58,7 @@
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
+    private final PredictionModel mPredictionModel;
 
     private SecureSettingsObserver mNotificationDotsObserver;
     private InstallSessionTracker mInstallSessionTracker;
@@ -127,6 +129,7 @@
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
         mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
+        mPredictionModel = new PredictionModel(mContext);
     }
 
     protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -182,6 +185,10 @@
         return mModel;
     }
 
+    public PredictionModel getPredictionModel() {
+        return mPredictionModel;
+    }
+
     public WidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 9148c2f..24e0d14 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -13,11 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.launcher3;
 
-
-import android.animation.Animator;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.graphics.Rect;
@@ -58,17 +55,6 @@
     }
 
     /**
-     * Number of animations which run on state properties.
-     */
-    public int getStateElementAnimationsCount() {
-        return 0;
-    }
-
-    public Animator createStateElementAnimation(int index, float... values) {
-        throw new RuntimeException("Unknown gesture animation " + index);
-    }
-
-    /**
      * Registers remote animations for certain system transitions.
      */
     public void registerRemoteAnimations() {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 21cd04e..48b97fa 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.annotation.TargetApi;
 import android.app.backup.BackupManager;
@@ -48,7 +47,6 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -85,6 +83,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
 public class LauncherProvider extends ContentProvider {
@@ -92,8 +91,7 @@
     private static final boolean LOGD = false;
 
     private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
-    private static final String TOKEN_RESTORE_BACKUP_TABLE = "restore_backup_table";
-    private static final long RESTORE_BACKUP_TABLE_DELAY = 60000;
+    private static final long RESTORE_BACKUP_TABLE_DELAY = TimeUnit.SECONDS.toMillis(30);
 
     /**
      * Represents the schema of the database. Changes in scheme need not be backwards compatible.
@@ -107,6 +105,8 @@
 
     protected DatabaseHelper mOpenHelper;
 
+    private long mLastRestoreTimestamp = 0L;
+
     /**
      * $ adb shell dumpsys activity provider com.android.launcher3
      */
@@ -412,11 +412,12 @@
                 return null;
             }
             case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: {
-                final Handler handler = MODEL_EXECUTOR.getHandler();
-                handler.removeCallbacksAndMessages(TOKEN_RESTORE_BACKUP_TABLE);
-                handler.postDelayed(() -> RestoreDbTask.restoreIfPossible(
-                        getContext(), mOpenHelper, new BackupManager(getContext())),
-                        TOKEN_RESTORE_BACKUP_TABLE, RESTORE_BACKUP_TABLE_DELAY);
+                final long ts = System.currentTimeMillis();
+                if (ts - mLastRestoreTimestamp > RESTORE_BACKUP_TABLE_DELAY) {
+                    mLastRestoreTimestamp = ts;
+                    RestoreDbTask.restoreIfPossible(
+                            getContext(), mOpenHelper, new BackupManager(getContext()));
+                }
                 return null;
             }
             case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index b4fbbc3..226a924 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -64,15 +64,8 @@
     private void handleSystemWindowInsets(Rect insets) {
         mConsumedInsets.setEmpty();
         boolean drawInsetBar = false;
-        if (mLauncher.isInMultiWindowMode()
-                && (insets.left > 0 || insets.right > 0 || insets.bottom > 0)) {
-            mConsumedInsets.left = insets.left;
-            mConsumedInsets.right = insets.right;
-            mConsumedInsets.bottom = insets.bottom;
-            insets.set(0, insets.top, 0, 0);
-            drawInsetBar = true;
-        } else  if ((insets.right > 0 || insets.left > 0) &&
-                getContext().getSystemService(ActivityManager.class).isLowRamDevice()) {
+        if ((insets.right > 0 || insets.left > 0)
+                && getContext().getSystemService(ActivityManager.class).isLowRamDevice()) {
             mConsumedInsets.left = insets.left;
             mConsumedInsets.right = insets.right;
             insets.set(0, insets.top, 0, insets.bottom);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index d3af5fc..87ead9e 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -152,6 +152,7 @@
         public static final int CONTAINER_HOTSEAT = -101;
         public static final int CONTAINER_PREDICTION = -102;
         public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
+        public static final int CONTAINER_ALL_APPS = -104;
 
         public static final String containerToString(int container) {
             switch (container) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index e2b867e..db2a6cd 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -15,23 +15,7 @@
  */
 package com.android.launcher3;
 
-import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
-import static android.view.View.VISIBLE;
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
-import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-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.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
@@ -43,25 +27,22 @@
 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
 import android.content.Context;
-import android.view.View;
 import android.view.animation.Interpolator;
 
-import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.HintState;
 import com.android.launcher3.states.SpringLoadedState;
-import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.states.AllAppsState;
 import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import java.util.Arrays;
 
-
 /**
  * Base state for various states used for the Launcher
  */
-public abstract class LauncherState {
-
+public abstract class LauncherState implements BaseState<LauncherState> {
 
     /**
      * Set of elements indicating various workspace elements which change visibility across states
@@ -80,16 +61,24 @@
     public static final int APPS_VIEW_ITEM_MASK =
             HOTSEAT_SEARCH_BOX | ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
 
-    protected static final int FLAG_MULTI_PAGE = 1 << 0;
-    protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1;
-    protected static final int FLAG_DISABLE_RESTORE = 1 << 2;
-    protected static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = 1 << 3;
-    protected static final int FLAG_DISABLE_PAGE_CLIPPING = 1 << 4;
-    protected static final int FLAG_PAGE_BACKGROUNDS = 1 << 5;
-    protected static final int FLAG_DISABLE_INTERACTION = 1 << 6;
-    protected static final int FLAG_OVERVIEW_UI = 1 << 7;
-    protected static final int FLAG_HIDE_BACK_BUTTON = 1 << 8;
-    protected static final int FLAG_HAS_SYS_UI_SCRIM = 1 << 9;
+    // Flag indicating workspace has multiple pages visible.
+    public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
+    // Flag indicating that workspace and its contents are not accessible
+    public static final int FLAG_WORKSPACE_INACCESSIBLE = BaseState.getFlag(1);
+
+    // Flag indicating the state allows workspace icons to be dragged.
+    public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = BaseState.getFlag(2);
+    // Flag to indicate that workspace should draw page background
+    public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = BaseState.getFlag(3);
+    // True if the back button should be hidden when in this state (assuming no floating views are
+    // open, launcher has window focus, etc).
+    public static final int FLAG_HIDE_BACK_BUTTON = BaseState.getFlag(4);
+    // Flag to indicate if the state would have scrim over sysui region: statu sbar and nav bar
+    public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(5);
+    // Flag to inticate that all popups should be closed when this state is enabled.
+    public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(6);
+    public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+
 
     public static final float NO_OFFSET = 0;
     public static final float NO_SCALE = 1;
@@ -112,7 +101,7 @@
             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
                     FLAG_HAS_SYS_UI_SCRIM) {
         @Override
-        public int getTransitionDuration(Launcher launcher) {
+        public int getTransitionDuration(Context context) {
             // Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
             return 0;
         }
@@ -144,87 +133,32 @@
     public final int containerType;
 
     /**
-     * True if the state can be persisted across activity restarts.
-     */
-    public final boolean disableRestore;
-
-    /**
-     * True if workspace has multiple pages visible.
-     */
-    public final boolean hasMultipleVisiblePages;
-
-    /**
-     * Accessibility flag for workspace and its pages.
-     * @see android.view.View#setImportantForAccessibility(int)
-     */
-    public final int workspaceAccessibilityFlag;
-
-    /**
-     * Properties related to state transition animation
-     *
-     * @see WorkspaceStateTransitionAnimation
-     */
-    public final boolean hasWorkspacePageBackground;
-
-    /**
-     * True if the state allows workspace icons to be dragged.
-     */
-    public final boolean workspaceIconsCanBeDragged;
-
-    /**
-     * True if the workspace pages should not be clipped relative to the workspace bounds
-     * for this state.
-     */
-    public final boolean disablePageClipping;
-
-    /**
-     * True if launcher can not be directly interacted in this state;
-     */
-    public final boolean disableInteraction;
-
-    /**
      * True if the state has overview panel visible.
      */
     public final boolean overviewUi;
 
-    /**
-     * True if the back button should be hidden when in this state (assuming no floating views are
-     * open, launcher has window focus, etc).
-     */
-    public final boolean hideBackButton;
-
-    public final boolean hasSysUiScrim;
+    private final int mFlags;
 
     public LauncherState(int id, int containerType, int flags) {
         this.containerType = containerType;
-
-        this.hasWorkspacePageBackground = (flags & FLAG_PAGE_BACKGROUNDS) != 0;
-        this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0;
-        this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0
-                ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-        this.disableRestore = (flags & FLAG_DISABLE_RESTORE) != 0;
-        this.workspaceIconsCanBeDragged = (flags & FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED) != 0;
-        this.disablePageClipping = (flags & FLAG_DISABLE_PAGE_CLIPPING) != 0;
-        this.disableInteraction = (flags & FLAG_DISABLE_INTERACTION) != 0;
+        this.mFlags = flags;
         this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
-        this.hideBackButton = (flags & FLAG_HIDE_BACK_BUTTON) != 0;
-        this.hasSysUiScrim = (flags & FLAG_HAS_SYS_UI_SCRIM) != 0;
-
         this.ordinal = id;
         sAllStates[id] = this;
     }
 
+    /**
+     * Returns if the state has the provided flag
+     */
+    @Override
+    public final boolean hasFlag(int mask) {
+        return (mFlags & mask) != 0;
+    }
+
     public static LauncherState[] values() {
         return Arrays.copyOf(sAllStates, sAllStates.length);
     }
 
-    /**
-     * @return How long the animation to this state should take (or from this state to NORMAL).
-     * @param launcher
-     */
-    public abstract int getTransitionDuration(Launcher launcher);
-
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         return new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
     }
@@ -252,12 +186,6 @@
         return 0;
     }
 
-    public void onStateEnabled(Launcher launcher) {
-        dispatchWindowStateChanged(launcher);
-    }
-
-    public void onStateDisabled(Launcher launcher) { }
-
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
@@ -324,82 +252,25 @@
         };
     }
 
+    @Override
     public LauncherState getHistoryForState(LauncherState previousState) {
         // No history is supported
         return NORMAL;
     }
 
-    /**
-     * Called when the start transition ends and the user settles on this particular state.
-     */
-    public void onStateTransitionEnd(Launcher launcher) {
-        if (this == NORMAL) {
-            // Clear any rotation locks when going to normal state
-            launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
-        }
+    @Override
+    public String toString() {
+        return "Ordinal-" + ordinal;
     }
 
     public void onBackPressed(Launcher launcher) {
         if (this != NORMAL) {
-            LauncherStateManager lsm = launcher.getStateManager();
+            StateManager<LauncherState> lsm = launcher.getStateManager();
             LauncherState lastState = lsm.getLastState();
             lsm.goToState(lastState);
         }
     }
 
-    /**
-     * Prepares for a non-user controlled animation from fromState to this state. Preparations
-     * include:
-     * - Setting interpolators for various animations included in the state transition.
-     * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
-     */
-    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
-            StateAnimationConfig config) {
-        if (this == NORMAL && fromState == OVERVIEW) {
-            config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
-            config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
-            config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
-            config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
-            Workspace workspace = launcher.getWorkspace();
-
-            // Start from a higher workspace scale, but only if we're invisible so we don't jump.
-            boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
-            if (isWorkspaceVisible) {
-                CellLayout currentChild = (CellLayout) workspace.getChildAt(
-                        workspace.getCurrentPage());
-                isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
-                        && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
-            }
-            if (!isWorkspaceVisible) {
-                workspace.setScaleX(0.92f);
-                workspace.setScaleY(0.92f);
-            }
-            Hotseat hotseat = launcher.getHotseat();
-            boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
-            if (!isHotseatVisible) {
-                hotseat.setScaleX(0.92f);
-                hotseat.setScaleY(0.92f);
-                if (ENABLE_OVERVIEW_ACTIONS.get()) {
-                    AllAppsContainerView qsbContainer = launcher.getAppsView();
-                    View qsb = qsbContainer.getSearchView();
-                    boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
-                    if (!qsbVisible) {
-                        qsbContainer.setScaleX(0.92f);
-                        qsbContainer.setScaleY(0.92f);
-                    }
-                }
-            }
-        } else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
-            // Keep fully visible until the very end (when overview is offscreen) to make invisible.
-            config.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
-        }
-    }
-
-    protected static void dispatchWindowStateChanged(Launcher launcher) {
-        launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
-    }
-
     public static abstract class PageAlphaProvider {
 
         public final Interpolator interpolator;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 5343424..359190c 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -257,13 +257,6 @@
         forceFinishScroller(true);
     }
 
-    /**
-     * Returns left offset of a page. This is the gap between pages and prevents overlap.
-     */
-    public int scrollOffsetLeft() {
-        return mInsets.left + getPaddingLeft();
-    }
-
     private void abortScrollerAnimation(boolean resetNextPage) {
         mScroller.abortAnimation();
         // We need to clean up the next page here to avoid computeScrollHelper from
@@ -1075,7 +1068,7 @@
     private int getSpringOverScroll(int amount) {
         if (mScroller.isSpringing()) {
             return amount < 0
-                    ? mScroller.getCurrPos()
+                    ? mScroller.getCurrPos() - mMinScroll
                     : Math.max(0, mScroller.getCurrPos() - mMaxScroll);
         } else {
             return 0;
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/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7baee95..8e33406 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -63,6 +63,8 @@
 import android.view.ViewConfiguration;
 import android.view.animation.Interpolator;
 
+import androidx.core.os.BuildCompat;
+
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.graphics.GridOptionsProvider;
@@ -104,6 +106,8 @@
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
 
+    public static final boolean ATLEAST_R = BuildCompat.isAtLeastR();
+
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
     public static final boolean ATLEAST_P =
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 70a5cc5..286b522 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -19,6 +19,9 @@
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
+import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
+import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_INACCESSIBLE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
@@ -76,6 +79,8 @@
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
@@ -84,6 +89,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.WorkspaceTouchListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -114,7 +120,7 @@
  */
 public class Workspace extends PagedView<WorkspacePageIndicator>
         implements DropTarget, DragSource, View.OnTouchListener,
-        DragController.DragListener, Insettable, LauncherStateManager.StateHandler,
+        DragController.DragListener, Insettable, StateHandler<LauncherState>,
         WorkspaceLayoutManager {
 
     /** The value that {@link #mTransitionProgress} must be greater than for
@@ -243,6 +249,8 @@
     // Handles workspace state transitions
     private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
 
+    private final StatsLogManager mStatsLogManager;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -275,6 +283,7 @@
         // Disable multitouch across the workspace/all apps/customize tray
         setMotionEventSplittingEnabled(true);
         setOnTouchListener(new WorkspaceTouchListener(mLauncher, this));
+        mStatsLogManager = StatsLogManager.newInstance(context);
     }
 
     @Override
@@ -354,7 +363,7 @@
     }
 
     @Override
-    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+    public void onDragStart(DragObject dragObject, DragOptions options) {
         if (ENFORCE_DRAG_EVENT_ORDER) {
             enforceDragParity("onDragStart", 0, 0);
         }
@@ -406,6 +415,14 @@
 
         // Always enter the spring loaded mode
         mLauncher.getStateManager().goToState(SPRING_LOADED);
+        mStatsLogManager.log(
+                LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
+                dragObject.logInstanceId,
+                dragObject.originalDragInfo.buildProto(
+                    dragObject.dragSource instanceof Folder
+                        ? ((Folder) dragObject.dragSource).mInfo
+                        : null)
+        );
     }
 
     public void deferRemoveExtraEmptyScreen() {
@@ -1208,7 +1225,7 @@
 
     /** Returns whether a drag should be allowed to be started from the current workspace state. */
     public boolean workspaceIconsCanBeDragged() {
-        return mLauncher.getStateManager().getState().workspaceIconsCanBeDragged;
+        return mLauncher.getStateManager().getState().hasFlag(FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED);
     }
 
     private void updateChildrenLayersEnabled() {
@@ -1318,7 +1335,7 @@
 
         // Invalidate the pages now, so that we have the visible pages before the
         // animation is started
-        if (toState.hasMultipleVisiblePages) {
+        if (toState.hasFlag(FLAG_MULTI_PAGE)) {
             mForceDrawAdjacentPages = true;
         }
         invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
@@ -1336,7 +1353,10 @@
 
     public void updateAccessibilityFlags() {
         // TODO: Update the accessibility flags appropriately when dragging.
-        int accessibilityFlag = mLauncher.getStateManager().getState().workspaceAccessibilityFlag;
+        int accessibilityFlag =
+                mLauncher.getStateManager().getState().hasFlag(FLAG_WORKSPACE_INACCESSIBLE)
+                        ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                        : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
         if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
             int total = getPageCount();
             for (int i = 0; i < total; i++) {
@@ -1422,6 +1442,10 @@
 
         mOutlineProvider = previewProvider;
 
+        if (draggableView == null && child instanceof DraggableView) {
+            draggableView = (DraggableView) child;
+        }
+
         // The drag bitmap follows the touch point around on the screen
         final Bitmap b = previewProvider.createDragBitmap();
         int halfPadding = previewProvider.previewPadding / 2;
@@ -1432,12 +1456,8 @@
         Point dragVisualizeOffset = null;
         Rect dragRect = new Rect();
 
-        if (draggableView == null && child instanceof DraggableView) {
-            draggableView = (DraggableView) child;
-        }
-
         if (draggableView != null) {
-            draggableView.getVisualDragBounds(dragRect);
+            draggableView.getSourceVisualDragBounds(dragRect);
             dragLayerY += dragRect.top;
             dragVisualizeOffset = new Point(- halfPadding, halfPadding);
         }
@@ -1630,7 +1650,10 @@
             Rect folderLocation = new Rect();
             float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
             target.removeView(v);
-
+            mStatsLogManager.log(
+                    LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
+                    d.logInstanceId,
+                    destInfo.buildProto(null));
             FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
                     targetCell[1]);
             destInfo.cellX = -1;
@@ -1668,6 +1691,10 @@
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
             if (fi.acceptDrop(d.dragInfo)) {
+                mStatsLogManager.log(
+                        LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+                        d.logInstanceId,
+                        fi.mInfo.buildProto(null));
                 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
 
                 // if the drag started here, we need to remove it from the workspace
@@ -1683,6 +1710,7 @@
     @Override
     public void prepareAccessibilityDrop() { }
 
+    @Override
     public void onDrop(final DragObject d, DragOptions options) {
         mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
         CellLayout dropTargetLayout = mDropToLayout;
@@ -1869,6 +1897,10 @@
 
             mLauncher.getStateManager().goToState(
                     NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
+            mStatsLogManager.log(
+                    LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+                    d.logInstanceId,
+                    d.dragInfo.buildProto(null));
         }
 
         if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@@ -2495,6 +2527,10 @@
                 resetTransitionTransform();
             }
         }
+        mStatsLogManager.log(
+                LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+                d.logInstanceId,
+                d.dragInfo.buildProto(null));
     }
 
     public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index c4c4377..06a73db 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -21,6 +21,8 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
+import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_HAS_BACKGROUNDS;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
@@ -64,7 +66,7 @@
     }
 
     /**
-     * @see com.android.launcher3.LauncherStateManager.StateHandler#setStateWithAnimation
+     * @see com.android.launcher3.statemanager.StateManager.StateHandler#setStateWithAnimation
      */
     public void setStateWithAnimation(
             LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
@@ -161,7 +163,8 @@
         WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
         propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
                 LINEAR);
-        propertySetter.setFloat(scrim, SYSUI_PROGRESS, state.hasSysUiScrim ? 1 : 0, LINEAR);
+        propertySetter.setFloat(scrim, SYSUI_PROGRESS,
+                state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR);
     }
 
     public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
@@ -173,7 +176,8 @@
             PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter,
             StateAnimationConfig config) {
         float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
-        int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
+        int drawableAlpha = state.hasFlag(FLAG_WORKSPACE_HAS_BACKGROUNDS)
+                ? Math.round(pageAlpha * 255) : 0;
 
         if (!config.onlyPlayAtomicComponent()) {
             // Don't update the scrim during the atomic animation.
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index d2dcfd2..b11312c 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -18,7 +18,6 @@
 import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
 
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -32,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -50,14 +50,12 @@
 
     private AppInfo[] mApps = EMPTY_ARRAY;
 
-    private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
+    private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>();
     private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
 
     private int mDeferUpdatesFlags = 0;
     private boolean mUpdatePending = false;
 
-    private boolean mListenerUpdateInProgress = false;
-
     public AppInfo[] getApps() {
         return mApps;
     }
@@ -102,12 +100,9 @@
             mUpdatePending = true;
             return;
         }
-        mListenerUpdateInProgress = true;
-        int count = mUpdateListeners.size();
-        for (int i = 0; i < count; i++) {
-            mUpdateListeners.get(i).onAppsUpdated();
+        for (OnUpdateListener listener : mUpdateListeners) {
+            listener.onAppsUpdated();
         }
-        mListenerUpdateInProgress = false;
     }
 
     public void addUpdateListener(OnUpdateListener listener) {
@@ -115,9 +110,6 @@
     }
 
     public void removeUpdateListener(OnUpdateListener listener) {
-        if (mListenerUpdateInProgress) {
-            Log.e("AllAppsStore", "Trying to remove listener during update", new Exception());
-        }
         mUpdateListeners.remove(listener);
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 071c03d..f057036 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -28,11 +28,11 @@
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
 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.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.Themes;
@@ -50,8 +50,8 @@
  * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
  * closer to top or closer to the page indicator.
  */
-public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener,
-        PluginListener<AllAppsSearchPlugin> {
+public class AllAppsTransitionController implements StateHandler<LauncherState>,
+        OnDeviceProfileChangeListener, PluginListener<AllAppsSearchPlugin> {
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
             new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -132,6 +132,10 @@
 
         mAppsView.setTranslationY(shiftCurrent);
 
+        if (mPlugin != null) {
+            mPlugin.setProgress(progress);
+        }
+
         // Use a light system UI (dark icons) if all apps is behind at least half of the
         // status bar.
         boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop())
@@ -200,8 +204,8 @@
             // TODO: change this from toggle event to continuous transition event.
             mPlugin.setEditText(mAppsView.getSearchUiManager().setTextSearchEnabled(true));
         } else {
-            mAppsView.getSearchUiManager().setTextSearchEnabled(false);
             mPlugin.setEditText(null);
+            mAppsView.getSearchUiManager().setTextSearchEnabled(false);
         }
 
     }
@@ -230,6 +234,8 @@
                     hasAllAppsContent, setter, headerFade, allAppsFade);
         } else {
             setter.setViewAlpha(mPluginContent, hasAllAppsContent ? 1 : 0, allAppsFade);
+            setter.setViewAlpha(mAppsView.getContentView(), 0, allAppsFade);
+            setter.setViewAlpha(mAppsView.getScrollBar(), 0, allAppsFade);
         }
         mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
 
@@ -279,7 +285,7 @@
                 R.layout.all_apps_content_layout, mAppsView, false);
         mAppsView.addView(mPluginContent);
         mPluginContent.setAlpha(0f);
-        mPlugin.setup((ViewGroup) mPluginContent);
+        mPlugin.setup((ViewGroup) mPluginContent, mLauncher);
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 0648682..5397942 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -31,9 +31,9 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.OnboardingPrefs;
 
 /**
@@ -46,7 +46,7 @@
     private final Launcher mLauncher;
     private final Animator mDiscoBounceAnimation;
 
-    private final StateListener mStateListener = new StateListener() {
+    private final StateListener<LauncherState> mStateListener = new StateListener<LauncherState>() {
         @Override
         public void onStateTransitionStart(LauncherState toState) {
             handleClose(false);
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index f6766c4..80b6a5a 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -22,7 +22,7 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.views.WorkEduView;
 
 /**
@@ -32,7 +32,7 @@
 
     private final Launcher mLauncher;
 
-    private LauncherStateManager.StateListener mWorkTabListener;
+    private StateListener<LauncherState> mWorkTabListener;
 
     public LauncherAllAppsContainerView(Context context) {
         this(context, null);
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index a95a5e1..740f7f2 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -105,6 +105,13 @@
         add(anim);
     }
 
+    public <T> void addFloat(T target, FloatProperty<T> property, float from, float to,
+            TimeInterpolator interpolator) {
+        Animator anim = ObjectAnimator.ofFloat(target, property, from, to);
+        anim.setDuration(mDuration).setInterpolator(interpolator);
+        add(anim);
+    }
+
     @Override
     public <T> void setInt(T target, IntProperty<T> property, int value,
             TimeInterpolator interpolator) {
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index bc77aab..770df03 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -17,6 +17,8 @@
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.util.FloatProperty;
@@ -195,6 +197,12 @@
         animator.setDuration(getDuration()).setInterpolator(LINEAR);
         animator.addUpdateListener(anim ->
                 property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                property.set(target, mEndValue);
+            }
+        });
         return animator;
     }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index df30f7b..78d194b 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -92,6 +92,7 @@
     public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
             "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
 
+    // Keep as DeviceFlag to allow remote disable in emergency.
     public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
             "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen");
 
@@ -117,8 +118,8 @@
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
 
-    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
-            "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
+    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
+            "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
 
     public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag(
             "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
@@ -139,8 +140,9 @@
             "ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
             + " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
 
-    public static final BooleanFlag ENABLE_SELECT_MODE = getDebugFlag(
-            "ENABLE_SELECT_MODE", true, "Show Select Mode button in Overview Actions");
+    // Keep as DeviceFlag for remote disable in emergency.
+    public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
+            "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
 
     public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
             "ENABLE_DATABASE_RESTORE", true,
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index d93fb1a..03028d3 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -186,8 +186,7 @@
 
         mDragObject.dragSource = source;
         mDragObject.dragInfo = dragInfo;
-        mDragObject.originalDragInfo = new ItemInfo();
-        mDragObject.originalDragInfo.copyFrom(dragInfo);
+        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
 
         if (dragOffset != null) {
             dragView.setDragVisualizeOffset(new Point(dragOffset));
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 970c5a0..ddf44ca 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -274,19 +274,29 @@
         scale *= childScale;
         int toX = Math.round(coord[0]);
         int toY = Math.round(coord[1]);
+
         float toScale = scale;
 
         if (child instanceof DraggableView) {
+            // This code is fairly subtle. Please verify drag and drop is pixel-perfect in a number
+            // of scenarios before modifying (from all apps, from workspace, different grid-sizes,
+            // shortcuts from in and out of Launcher etc).
             DraggableView d = (DraggableView) child;
-            d.getVisualDragBounds(dragViewBounds);
+            Rect destRect = new Rect();
+            d.getWorkspaceVisualDragBounds(destRect);
+
+            // In most cases this additional scale factor should be a no-op (1). It mainly accounts
+            // for alternate grids where the source and destination icon sizes are different
+            toScale *= ((1f * destRect.width())
+                    / (dragView.getMeasuredWidth() - dragView.getBlurSizeOutline()));
 
             // This accounts for the offset of the DragView created by scaling it about its
             // center as it animates into place.
-            float scaleShiftX = dragView.getMeasuredWidth() * (1 - scale) / 2;
-            float scaleShiftY = dragView.getMeasuredHeight() * (1 - scale) / 2;
+            float scaleShiftX = dragView.getMeasuredWidth() * (1 - toScale) / 2;
+            float scaleShiftY = dragView.getMeasuredHeight() * (1 - toScale) / 2;
 
-            toX += scale * (dragViewBounds.left - dragView.getBlurSizeOutline() / 2) - scaleShiftX;
-            toY += scale * (dragViewBounds.top - dragView.getBlurSizeOutline() / 2) - scaleShiftY;
+            toX += scale * destRect.left - toScale * dragView.getBlurSizeOutline() / 2 - scaleShiftX;
+            toY += scale * destRect.top - toScale * dragView.getBlurSizeOutline() / 2 - scaleShiftY;
         }
 
         child.setVisibility(INVISIBLE);
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 1e23bb6..de0fa1a 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -49,18 +49,18 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.Arrays;
 
-public class DragView extends View implements LauncherStateManager.StateListener {
+public class DragView extends View implements StateListener<LauncherState> {
     private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
     private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
 
diff --git a/src/com/android/launcher3/dragndrop/DraggableView.java b/src/com/android/launcher3/dragndrop/DraggableView.java
index df99902..287c781 100644
--- a/src/com/android/launcher3/dragndrop/DraggableView.java
+++ b/src/com/android/launcher3/dragndrop/DraggableView.java
@@ -53,5 +53,14 @@
      *
      * @param bounds Visual bounds in the views coordinates will be written here.
      */
-    default void getVisualDragBounds(Rect bounds) { }
+    default void getWorkspaceVisualDragBounds(Rect bounds) { }
+
+    /**
+     * Same as above, but accounts for differing icon sizes between source and destination
+     *
+     * @param bounds Visual bounds in the views coordinates will be written here.
+     */
+    default void getSourceVisualDragBounds(Rect bounds) {
+        getWorkspaceVisualDragBounds(bounds);
+    }
 }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index c287190..9a36b3e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -87,6 +87,7 @@
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.FolderInfo.FolderListener;
@@ -1346,6 +1347,10 @@
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
+        StatsLogManager.newInstance(getContext())
+                .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+                    d.logInstanceId,
+                    d.dragInfo.buildProto(mInfo));
     }
 
     // This is used so the item doesn't immediately appear in the folder when added. In one case
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index b875a0b..93208d4 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -53,6 +53,7 @@
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.FolderDotInfo;
@@ -85,7 +86,7 @@
 
     @Thunk ActivityContext mActivity;
     @Thunk Folder mFolder;
-    private FolderInfo mInfo;
+    public FolderInfo mInfo;
 
     private CheckLongPressHelper mLongPressHelper;
 
@@ -385,6 +386,14 @@
             float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
 
             float finalScale = scale * scaleRelativeToDragLayer;
+
+            // Account for potentially different icon sizes with non-default grid settings
+            if (d.dragSource instanceof AllAppsContainerView) {
+                DeviceProfile grid = mActivity.getDeviceProfile();
+                float containerScale = (1f * grid.iconSizePx / grid.allAppsIconSizePx);
+                finalScale *= containerScale;
+            }
+
             dragLayer.animateView(animateView, from, to, finalAlpha,
                     1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
                     Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
@@ -758,7 +767,7 @@
     }
 
     @Override
-    public void getVisualDragBounds(Rect bounds) {
+    public void getWorkspaceVisualDragBounds(Rect bounds) {
         getPreviewBounds(bounds);
     }
 }
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 848c04a..634d07e 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -77,7 +77,7 @@
         if (mView instanceof DraggableView) {
             DraggableView dv = (DraggableView) mView;
             dv.prepareDrawDragView();
-            dv.getVisualDragBounds(mTempRect);
+            dv.getSourceVisualDragBounds(mTempRect);
             destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
                     blurSizeOutline / 2 - mTempRect.top);
             mView.draw(destCanvas);
@@ -95,7 +95,7 @@
         // Assume scaleX == scaleY, which is always the case for workspace items.
         float scale = mView.getScaleX();
         if (mView instanceof DraggableView) {
-            ((DraggableView) mView).getVisualDragBounds(mTempRect);
+            ((DraggableView) mView).getSourceVisualDragBounds(mTempRect);
             width = mTempRect.width();
             height = mTempRect.height();
         } else {
diff --git a/src/com/android/launcher3/logging/InstanceId.java b/src/com/android/launcher3/logging/InstanceId.java
new file mode 100644
index 0000000..e720d75
--- /dev/null
+++ b/src/com/android/launcher3/logging/InstanceId.java
@@ -0,0 +1,108 @@
+/*
+ * 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.launcher3.logging;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+
+/**
+ * An opaque identifier used to disambiguate which logs refer to a particular instance of some
+ * UI element. Useful when there might be multiple instances simultaneously active.
+ * Obtain from InstanceIdSequence.  Clipped to range [0, INSTANCE_ID_MAX].
+ *
+ * Copy of frameworks/base/core/java/com/android/internal/logging/InstanceId.java.
+ */
+public final class InstanceId implements Parcelable {
+    // At most 20 bits: ~1m possibilities, ~0.5% probability of collision in 100 values
+    static final int INSTANCE_ID_MAX = 1 << 20;
+
+    private final int mId;
+    InstanceId(int id) {
+        mId = min(max(0, id), INSTANCE_ID_MAX);
+    }
+
+    private InstanceId(Parcel in) {
+        this(in.readInt());
+    }
+
+    @VisibleForTesting
+    public int getId() {
+        return mId;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return mId + "";
+    }
+
+    /**
+     * Create a fake instance ID for testing purposes.  Not for production use. See also
+     * InstanceIdSequenceFake, which is a testing replacement for InstanceIdSequence.
+     * @param id The ID you want to assign.
+     * @return new InstanceId.
+     */
+    @VisibleForTesting
+    public static InstanceId fakeInstanceId(int id) {
+        return new InstanceId(id);
+    }
+
+    @Override
+    public int hashCode() {
+        return mId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof InstanceId)) {
+            return false;
+        }
+        return mId == ((InstanceId) obj).mId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mId);
+    }
+
+    public static final Parcelable.Creator<InstanceId> CREATOR =
+            new Parcelable.Creator<InstanceId>() {
+                @Override
+                public InstanceId createFromParcel(Parcel in) {
+                    return new InstanceId(in);
+                }
+
+                @Override
+                public InstanceId[] newArray(int size) {
+                    return new InstanceId[size];
+                }
+            };
+
+}
diff --git a/src/com/android/launcher3/logging/InstanceIdSequence.java b/src/com/android/launcher3/logging/InstanceIdSequence.java
new file mode 100644
index 0000000..a4b7953
--- /dev/null
+++ b/src/com/android/launcher3/logging/InstanceIdSequence.java
@@ -0,0 +1,64 @@
+/*
+ * 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.launcher3.logging;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * Generates random InstanceIds in range [1, instanceIdMax] for passing to
+ * UiEventLogger.logWithInstanceId(). Holds a SecureRandom, which self-seeds on first use; try to
+ * give it a long lifetime. Safe for concurrent use.
+ *
+ * Copy of frameworks/base/core/java/com/android/internal/logging/InstanceIdSequence.java
+ */
+public class InstanceIdSequence {
+    protected final int mInstanceIdMax;
+    private final Random mRandom = new SecureRandom();
+
+    /**
+     * Constructs a sequence with identifiers [1, instanceIdMax].  Capped at INSTANCE_ID_MAX.
+     * @param instanceIdMax Limiting value of identifiers. Normally positive: otherwise you get
+     *                      an all-1 sequence.
+     */
+    public InstanceIdSequence(int instanceIdMax) {
+        mInstanceIdMax = min(max(1, instanceIdMax), InstanceId.INSTANCE_ID_MAX);
+    }
+
+    /**
+     * Gets the next instance from the sequence.  Safe for concurrent use.
+     * @return new InstanceId
+     */
+    public InstanceId newInstanceId() {
+        return newInstanceIdInternal(1 + mRandom.nextInt(mInstanceIdMax));
+    }
+
+    /**
+     * Factory function for instance IDs, used for testing.
+     * @param id
+     * @return new InstanceId(id)
+     */
+    @VisibleForTesting
+    protected InstanceId newInstanceIdInternal(int id) {
+        return new InstanceId(id);
+    }
+}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 0f79bd6..cd4f034 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -41,6 +41,7 @@
     private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
     private static final String UNKNOWN = "UNKNOWN";
     private static final int DEFAULT_PREDICTED_RANK = 10000;
+    private static final String DELIMITER_DOT = "\\.";
 
     public static String getFieldName(int value, Class c) {
         SparseArray<String> cache;
@@ -173,4 +174,17 @@
         targets.toArray(targetsArray);
         return newLauncherEvent(action, targetsArray);
     }
+
+    /**
+     * String conversion for only the helpful parts of {@link Object#toString()} method
+     * @param stringToExtract "foo.bar.baz.MyObject@1234"
+     * @return "MyObject@1234"
+     */
+    public static String extractObjectNameAndAddress(String stringToExtract) {
+        String[] superStringParts = stringToExtract.split(DELIMITER_DOT);
+        if (superStringParts.length == 0) {
+            return "";
+        }
+        return superStringParts[superStringParts.length - 1];
+    }
 }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2829951..9455bd3 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.logging;
 
 import android.content.Context;
+import android.util.Log;
 
 import com.android.launcher3.R;
 import com.android.launcher3.logger.LauncherAtom;
@@ -25,22 +26,61 @@
 
 /**
  * Handles the user event logging in R+.
+ * 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();
     }
 
     public enum LauncherEvent implements EventEnum {
         @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
-        APP_LAUNCH_TAP(1),
+        LAUNCHER_APP_LAUNCH_TAP(338),
+
         @LauncherUiEvent(doc = "Task launched from overview using TAP")
-        TASK_LAUNCH_TAP(2),
+        LAUNCHER_TASK_LAUNCH_TAP(339),
+
         @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
-        TASK_LAUNCH_SWIPE_DOWN(2),
+        LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
+
         @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
-        TASK_DISMISS_SWIPE_UP(3);
+        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 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;
@@ -54,23 +94,37 @@
 
     protected LogStateProvider mStateProvider;
 
+    /**
+     * Creates a new instance of {@link StatsLogManager} based on provided context.
+     */
+    public static StatsLogManager newInstance(Context context) {
+        return newInstance(context, null);
+    }
+
     public static StatsLogManager newInstance(Context context, LogStateProvider stateProvider) {
         StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
                 context.getApplicationContext(), R.string.stats_log_manager_class);
         mgr.mStateProvider = stateProvider;
-        mgr.verify();
         return mgr;
     }
 
     /**
      * Logs an event and accompanying {@link ItemInfo}
      */
-    public void log(LauncherEvent eventId, LauncherAtom.ItemInfo 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) { }
+    public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) { }
+
 
     /**
      * Logs snapshot, or impression of the current workspace.
      */
     public void logSnapshot() { }
-
-    public void verify() {}     // TODO: should move into robo tests
 }
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index 97aaf84..10d88e5 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -1,7 +1,5 @@
 package com.android.launcher3.logging;
 
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
-
 import android.view.View;
 import android.view.ViewParent;
 
@@ -13,6 +11,7 @@
 
 import java.util.ArrayList;
 
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
 
 public class StatsLogUtils {
 
@@ -31,8 +30,6 @@
 
     /**
      * Implemented by containers to provide a container source for a given child.
-     *
-     * Currently,
      */
     public interface LogContainerProvider {
 
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index c98be56..1465100 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
 import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
 
 import android.util.Log;
@@ -196,6 +197,10 @@
             // Load items on the current page.
             bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
             bindAppWidgets(currentAppWidgets, mainExecutor);
+
+            // Locate available spots for prediction using currentWorkspaceItems
+            IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons);
+            bindPredictedItems(gaps, mainExecutor);
             // In case of validFirstPage, only bind the first screen, and defer binding the
             // remaining screens after first onDraw (and an optional the fade animation whichever
             // happens later).
@@ -247,6 +252,11 @@
             }
         }
 
+        private void bindPredictedItems(IntArray ranks, final Executor executor) {
+            executeCallbacksTask(
+                    c -> c.bindPredictedItems(mBgDataModel.cachedPredictedItems, ranks), executor);
+        }
+
         protected void executeCallbacksTask(CallbackTask task, Executor executor) {
             executor.execute(() -> {
                 if (mMyBindingId != mBgDataModel.lastBindId) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index f79a9d1..2522a49 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -93,6 +93,11 @@
     public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
 
     /**
+     * List of all cached predicted items visible on home screen
+     */
+    public final ArrayList<AppInfo> cachedPredictedItems = new ArrayList<>();
+
+    /**
      * True if the launcher has permission to access deep shortcuts.
      */
     public boolean hasShortcutHostPermission;
@@ -366,5 +371,10 @@
         void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
 
         void bindAllApplications(AppInfo[] apps);
+
+        /**
+         * Binds predicted appInfos at at available prediction slots.
+         */
+        void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks);
     }
 }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4c02837..9e6282e 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -179,6 +179,7 @@
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
             loadWorkspace(allShortcuts);
+            loadCachedPredictions();
             logger.addSplit("loadWorkspace");
 
             verifyNotStopped();
@@ -849,6 +850,26 @@
         }
     }
 
+    private List<AppInfo> loadCachedPredictions() {
+        synchronized (mBgDataModel) {
+            List<ComponentKey> componentKeys =
+                    mApp.getPredictionModel().getPredictionComponentKeys();
+            List<AppInfo> results = new ArrayList<>();
+            if (componentKeys == null) return results;
+            List<LauncherActivityInfo> l;
+            mBgDataModel.cachedPredictedItems.clear();
+            for (ComponentKey key : componentKeys) {
+                l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
+                if (l.size() == 0) continue;
+                boolean quietMode = mUserManager.isQuietModeEnabled(key.user);
+                AppInfo info = new AppInfo(l.get(0), key.user, quietMode);
+                mBgDataModel.cachedPredictedItems.add(info);
+                mIconCache.getTitleAndIcon(info, false);
+            }
+            return results;
+        }
+    }
+
     private List<LauncherActivityInfo> loadAllApps() {
         final List<UserHandle> profiles = mUserCache.getUserProfiles();
         List<LauncherActivityInfo> allActivityList = new ArrayList<>();
@@ -880,6 +901,14 @@
                         PackageInstallInfo.fromInstallingState(info));
             }
         }
+        for (AppInfo item : mBgDataModel.cachedPredictedItems) {
+            List<LauncherActivityInfo> l = mLauncherApps.getActivityList(
+                    item.componentName.getPackageName(), item.user);
+            for (LauncherActivityInfo info : l) {
+                boolean quietMode = mUserManager.isQuietModeEnabled(item.user);
+                mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info);
+            }
+        }
 
         mBgAllAppsList.getAndResetChangeFlag();
         return allActivityList;
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index ef7e828..4efeba5 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -19,11 +19,14 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
+import java.util.stream.IntStream;
 
 /**
  * Utils class for {@link com.android.launcher3.LauncherModel}.
@@ -109,4 +112,17 @@
             }
         });
     }
+
+    /**
+     * Iterates though current workspace items and returns available hotseat ranks for prediction.
+     */
+    public static IntArray getMissingHotseatRanks(List<ItemInfo> items, int len) {
+        IntSet seen = new IntSet();
+        items.stream().filter(
+                info -> info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)
+                .forEach(i -> seen.add(i.screenId));
+        IntArray result = new IntArray(len);
+        IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add);
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
new file mode 100644
index 0000000..6aa41eb
--- /dev/null
+++ b/src/com/android/launcher3/model/PredictionModel.java
@@ -0,0 +1,93 @@
+/*
+ * 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.launcher3.model;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.UserHandle;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.util.ComponentKey;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Model helper for app predictions in workspace
+ */
+public class PredictionModel {
+    private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
+    private static final int MAX_CACHE_ITEMS = 5;
+
+    private final Context mContext;
+    private final SharedPreferences mDevicePrefs;
+    private ArrayList<ComponentKey> mCachedComponentKeys;
+
+    public PredictionModel(Context context) {
+        mContext = context;
+        mDevicePrefs = Utilities.getDevicePrefs(mContext);
+    }
+
+    /**
+     * Formats and stores a list of component key in device preferences.
+     */
+    public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
+        StringBuilder builder = new StringBuilder();
+        int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
+        for (int i = 0; i < count; i++) {
+            builder.append(componentKeys.get(i));
+            builder.append("\n");
+        }
+        mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
+        mCachedComponentKeys = null;
+    }
+
+    /**
+     * parses and returns ComponentKeys saved by
+     * {@link PredictionModel#cachePredictionComponentKeys(List)}
+     */
+    public List<ComponentKey> getPredictionComponentKeys() {
+        if (mCachedComponentKeys == null) {
+            mCachedComponentKeys = new ArrayList<>();
+
+            String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
+            for (String line : cachedBlob.split("\n")) {
+                ComponentKey key = ComponentKey.fromString(line);
+                if (key != null) {
+                    mCachedComponentKeys.add(key);
+                }
+            }
+        }
+        return mCachedComponentKeys;
+    }
+
+    /**
+     * Remove uninstalled applications from model
+     */
+    public void removePackage(String pkgName, UserHandle user, ArrayList<AppInfo> ids) {
+        for (int i = ids.size() - 1; i >= 0; i--) {
+            AppInfo info = ids.get(i);
+            if (info.user.equals(user) && pkgName.equals(info.componentName.getPackageName())) {
+                ids.remove(i);
+            }
+        }
+        cachePredictionComponentKeys(getPredictionComponentKeys().stream()
+                .filter(cn -> !(cn.user.equals(user) && cn.componentName.getPackageName().equals(
+                        pkgName))).collect(Collectors.toList()));
+    }
+}
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index ae79f0d..b17b062 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.model.data;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -74,7 +76,7 @@
 
     public AppInfo(LauncherActivityInfo info, UserHandle user, boolean quietModeEnabled) {
         this.componentName = info.getComponentName();
-        this.container = ItemInfo.NO_ID;
+        this.container = CONTAINER_ALL_APPS;
         this.user = user;
         intent = makeLaunchIntent(info);
 
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index cfe34c1..3ac6a22 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -21,6 +21,7 @@
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.util.ContentWriter;
 
@@ -155,4 +156,20 @@
         return super.dumpProperties()
                 + " manuallyTypedTitle=" + hasOption(FLAG_MANUAL_FOLDER_NAME);
     }
+
+    @Override
+    public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+        return getDefaultItemInfoBuilder()
+            .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
+            .setContainerInfo(getContainerInfo())
+            .build();
+    }
+
+    @Override
+    public ItemInfo makeShallowCopy() {
+        FolderInfo folderInfo = new FolderInfo();
+        folderInfo.copyFrom(this);
+        folderInfo.contents = this.contents;
+        return folderInfo;
+    }
 }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 14f9a3e..7611ee7 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -16,8 +16,10 @@
 
 package com.android.launcher3.model.data;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
@@ -35,6 +37,8 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.AllAppsContainer;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.Optional;
@@ -122,6 +126,12 @@
      */
     public CharSequence contentDescription;
 
+    /**
+     * When the instance is created using {@link #copyFrom}, this field is used to keep track of
+     * original {@link ComponentName}.
+     */
+    private ComponentName mComponentName;
+
     public UserHandle user;
 
     public ItemInfo() {
@@ -144,6 +154,7 @@
         container = info.container;
         user = info.user;
         contentDescription = info.contentDescription;
+        mComponentName = info.getTargetComponent();
     }
 
     public Intent getIntent() {
@@ -152,12 +163,7 @@
 
     @Nullable
     public ComponentName getTargetComponent() {
-        Intent intent = getIntent();
-        if (intent != null) {
-            return intent.getComponent();
-        } else {
-            return null;
-        }
+        return Optional.ofNullable(getIntent()).map(Intent::getComponent).orElse(mComponentName);
     }
 
     public void writeToValues(ContentWriter writer) {
@@ -246,10 +252,8 @@
     /**
      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
      */
-    public LauncherAtom.ItemInfo buildProto(Intent intent, FolderInfo fInfo) {
-
-        LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
-        itemBuilder.setIsWork(user != Process.myUserHandle());
+    public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+        LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
         Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
         switch (itemType) {
             case ITEM_TYPE_APPLICATION:
@@ -269,7 +273,14 @@
                                 .orElse(LauncherAtom.Shortcut.newBuilder()));
                 break;
             case ITEM_TYPE_APPWIDGET:
-                setItemBuilder(itemBuilder);
+                itemBuilder
+                        .setWidget(nullableComponent
+                                .map(component -> LauncherAtom.Widget.newBuilder()
+                                        .setComponentName(component.flattenToShortString())
+                                        .setPackageName(component.getPackageName()))
+                                .orElse(LauncherAtom.Widget.newBuilder())
+                                .setSpanX(spanX)
+                                .setSpanY(spanY));
                 break;
             default:
                 break;
@@ -281,6 +292,7 @@
 
             switch (fInfo.container) {
                 case CONTAINER_HOTSEAT:
+                case CONTAINER_HOTSEAT_PREDICTION:
                     folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
                             .setIndex(fInfo.screenId));
                     break;
@@ -290,21 +302,47 @@
                             .setGridX(fInfo.cellX).setGridY(fInfo.cellY));
                     break;
             }
-            itemBuilder.setFolder(folderBuilder);
-        } else {
-            switch (container) {
-                case CONTAINER_HOTSEAT:
-                    itemBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
-                            .setIndex(screenId));
-                    break;
-                case CONTAINER_DESKTOP:
-                    itemBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
-                            .setGridX(cellX)
-                            .setGridY(cellY)
-                            .setPageIndex(screenId));
-                    break;
-            }
+            itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
+        } else if (getContainerInfo().getContainerCase().getNumber() > 0) {
+            itemBuilder.setContainerInfo(getContainerInfo());
         }
         return itemBuilder.build();
     }
+
+    LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
+        LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
+        itemBuilder.setIsWork(user != Process.myUserHandle());
+        return itemBuilder;
+    }
+
+    ContainerInfo getContainerInfo() {
+        switch (container) {
+            case CONTAINER_HOTSEAT:
+            case CONTAINER_HOTSEAT_PREDICTION:
+                return ContainerInfo.newBuilder()
+                    .setHotseat(LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId))
+                    .build();
+            case CONTAINER_DESKTOP:
+                return ContainerInfo.newBuilder()
+                    .setWorkspace(
+                        LauncherAtom.WorkspaceContainer.newBuilder()
+                        .setGridX(cellX)
+                        .setGridY(cellY)
+                        .setPageIndex(screenId))
+                    .build();
+            case CONTAINER_ALL_APPS:
+                return ContainerInfo.newBuilder()
+                        .setAllAppsContainer(
+                                AllAppsContainer.getDefaultInstance())
+                        .build();
+        }
+        return ContainerInfo.getDefaultInstance();
+    }
+
+    /** Returns shallow copy of the object. */
+    public ItemInfo makeShallowCopy() {
+        ItemInfo itemInfo = new ItemInfo();
+        itemInfo.copyFrom(this);
+        return itemInfo;
+    }
 }
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index adb97dc..b0d19a6 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -21,6 +21,8 @@
 import android.content.Intent;
 import android.os.Process;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
@@ -142,6 +144,12 @@
         return appWidgetId <= CUSTOM_WIDGET_ID;
     }
 
+    @Nullable
+    @Override
+    public ComponentName getTargetComponent() {
+        return providerName;
+    }
+
     @Override
     public void onAddToDatabase(ContentWriter writer) {
         super.onAddToDatabase(writer);
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 976d7ba..901d27f 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -56,8 +56,6 @@
     // Set<String> of session ids of promise icons that have been added to the home screen
     // as FLAG_PROMISE_NEW_INSTALLS.
     protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
-    public static final String KEY_INSTALL_SESSION_CREATED_TIMESTAMP =
-            "key_install_session_created_timestamp";
 
     private static final boolean DEBUG = false;
 
@@ -166,14 +164,13 @@
     }
 
     /**
-     * Attempt to restore workspace layout if the session is triggered due to device restore and it
-     * has a newer timestamp.
+     * Attempt to restore workspace layout if the session is triggered due to device restore.
      */
     public boolean restoreDbIfApplicable(@NonNull final SessionInfo info) {
         if (!Utilities.ATLEAST_OREO || !FeatureFlags.ENABLE_DATABASE_RESTORE.get()) {
             return false;
         }
-        if (isRestore(info) && hasNewerTimestamp(mAppContext, info)) {
+        if (isRestore(info)) {
             LauncherSettings.Settings.call(mAppContext.getContentResolver(),
                     LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE);
             return true;
@@ -186,13 +183,6 @@
         return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
     }
 
-    private static boolean hasNewerTimestamp(
-            @NonNull final Context context, @NonNull final SessionInfo info) {
-        return PackageManagerHelper.getSessionCreatedTimeInMillis(info)
-                > Utilities.getDevicePrefs(context).getLong(
-                        KEY_INSTALL_SESSION_CREATED_TIMESTAMP, 0);
-    }
-
     public boolean promiseIconAddedForId(int sessionId) {
         return mPromiseIconIds.contains(sessionId);
     }
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 33eff57..53183bf 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.provider;
 
-import static com.android.launcher3.pm.InstallSessionHelper.KEY_INSTALL_SESSION_CREATED_TIMESTAMP;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
 import android.app.backup.BackupManager;
@@ -87,13 +86,10 @@
      */
     public static boolean restoreIfPossible(@NonNull Context context,
             @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager) {
-        Utilities.getDevicePrefs(context).edit().putLong(
-                KEY_INSTALL_SESSION_CREATED_TIMESTAMP, System.currentTimeMillis()).apply();
         final SQLiteDatabase db = helper.getWritableDatabase();
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             RestoreDbTask task = new RestoreDbTask();
             task.restoreWorkspace(context, db, helper, backupManager);
-            task.restoreAppWidgetIdsIfExists(context);
             t.commit();
             return true;
         } catch (Exception e) {
@@ -107,7 +103,6 @@
      */
     private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        // TODO(pinyaoting): Support backing up workspace with multiple grid options.
         new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
                 .doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
     }
@@ -115,13 +110,17 @@
     private void restoreWorkspace(@NonNull Context context, @NonNull SQLiteDatabase db,
             @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager)
             throws Exception {
-        // TODO(pinyaoting): Support restoring workspace with multiple grid options.
         final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
         GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
                 idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
-            sanitizeDB(helper, db, backupManager);
+            int itemsDeleted = sanitizeDB(helper, db, backupManager);
             LauncherAppState.getInstance(context).getModel().forceReload();
+            restoreAppWidgetIdsIfExists(context);
+            if (itemsDeleted == 0) {
+                // all the items are restored, we no longer need the backup table
+                dropTable(db, Favorites.BACKUP_TABLE_NAME);
+            }
         }
     }
 
@@ -132,8 +131,10 @@
      *      the restored apps get installed.
      *   3. If the user serial for any restored profile is different than that of the previous
      *      device, update the entries to the new profile id.
+     *
+     * @return number of items deleted.
      */
-    private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
+    private int sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
             throws Exception {
         // Primary user ids
         long myProfileId = helper.getDefaultUserSerial();
@@ -210,6 +211,7 @@
         if (myProfileId != oldProfileId) {
             changeDefaultColumn(db, myProfileId);
         }
+        return itemsDeleted;
     }
 
     /**
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index dd6fc49..4a15af1 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -196,6 +196,9 @@
     public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
 
     @Override
+    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
+
+    @Override
     public void bindScreens(IntArray orderedScreenIds) { }
 
     @Override
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
new file mode 100644
index 0000000..daec1d8
--- /dev/null
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -0,0 +1,55 @@
+/*
+ * 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.launcher3.statemanager;
+
+import android.content.Context;
+
+/**
+ * Interface representing a state of a StatefulActivity
+ */
+public interface BaseState<T extends BaseState> {
+
+    // Flag to indicate that Launcher is non-interactive in this state
+    int FLAG_NON_INTERACTIVE = 1 << 0;
+    int FLAG_DISABLE_RESTORE = 1 << 1;
+
+    static int getFlag(int index) {
+        // reserve few spots to base flags
+        return 1 << (index + 2);
+    }
+
+    /**
+     * @return How long the animation to this state should take (or from this state to NORMAL).
+     */
+    int getTransitionDuration(Context context);
+
+    /**
+     * Returns the state to go back to from this state
+     */
+    T getHistoryForState(T previousState);
+
+    /**
+     * @return true if the state can be persisted across activity restarts.
+     */
+    default boolean shouldDisableRestore() {
+        return hasFlag(FLAG_DISABLE_RESTORE);
+    }
+
+    /**
+     * Returns if the state has the provided flag
+     */
+    boolean hasFlag(int flagMask);
+}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
similarity index 69%
rename from src/com/android/launcher3/LauncherStateManager.java
rename to src/com/android/launcher3/statemanager/StateManager.java
index 1d2e866..4447166 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.statemanager;
 
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
 
 import android.animation.Animator;
@@ -25,93 +24,62 @@
 import android.animation.AnimatorSet;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
 
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.testing.TestProtocol;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
- * TODO: figure out what kind of tests we can write for this
- *
- * Things to test when changing the following class.
- *   - Home from workspace
- *          - from center screen
- *          - from other screens
- *   - Home from all apps
- *          - from center screen
- *          - from other screens
- *   - Back from all apps
- *          - from center screen
- *          - from other screens
- *   - Launch app from workspace and quit
- *          - with back
- *          - with home
- *   - Launch app from all apps and quit
- *          - with back
- *          - with home
- *   - Go to a screen that's not the default, then all
- *     apps, and launch and app, and go back
- *          - with back
- *          -with home
- *   - On workspace, long press power and go back
- *          - with back
- *          - with home
- *   - On all apps, long press power and go back
- *          - with back
- *          - with home
- *   - On workspace, power off
- *   - On all apps, power off
- *   - Launch an app and turn off the screen while in that app
- *          - Go back with home key
- *          - Go back with back key  TODO: make this not go to workspace
- *          - From all apps
- *          - From workspace
- *   - Enter and exit car mode (becase it causes an extra configuration changed)
- *          - From all apps
- *          - From the center workspace
- *          - From another workspace
+ * Class to manage transitions between different states for a StatefulActivity based on different
+ * states
  */
-public class LauncherStateManager {
+public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
 
     public static final String TAG = "StateManager";
 
     private final AnimationState mConfig = new AnimationState();
     private final Handler mUiHandler;
-    private final Launcher mLauncher;
-    private final ArrayList<StateListener> mListeners = new ArrayList<>();
+    private final StatefulActivity<STATE_TYPE> mActivity;
+    private final ArrayList<StateListener<STATE_TYPE>> mListeners = new ArrayList<>();
+    private final STATE_TYPE mBaseState;
 
     // Animators which are run on properties also controlled by state animations.
-    private Animator[] mStateElementAnimators;
+    private final AtomicAnimationFactory mAtomicAnimationFactory;
 
-    private StateHandler[] mStateHandlers;
-    private LauncherState mState = NORMAL;
+    private StateHandler<STATE_TYPE>[] mStateHandlers;
+    private STATE_TYPE mState;
 
-    private LauncherState mLastStableState = NORMAL;
-    private LauncherState mCurrentStableState = NORMAL;
+    private STATE_TYPE mLastStableState;
+    private STATE_TYPE mCurrentStableState;
 
-    private LauncherState mRestState;
+    private STATE_TYPE mRestState;
 
-    public LauncherStateManager(Launcher l) {
+    public StateManager(StatefulActivity<STATE_TYPE> l, STATE_TYPE baseState) {
         mUiHandler = new Handler(Looper.getMainLooper());
-        mLauncher = l;
+        mActivity = l;
+        mBaseState = baseState;
+        mState = mLastStableState = mCurrentStableState = baseState;
+        mAtomicAnimationFactory = l.createAtomicAnimationFactory();
     }
 
-    public LauncherState getState() {
+    public STATE_TYPE getState() {
         return mState;
     }
 
-    public LauncherState getCurrentStableState() {
+    public STATE_TYPE getCurrentStableState() {
         return mCurrentStableState;
     }
 
     public void dump(String prefix, PrintWriter writer) {
-        writer.println(prefix + "LauncherState:");
+        writer.println(prefix + "StateManager:");
         writer.println(prefix + "\tmLastStableState:" + mLastStableState);
         writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
         writer.println(prefix + "\tmState:" + mState);
@@ -121,7 +89,7 @@
 
     public StateHandler[] getStateHandlers() {
         if (mStateHandlers == null) {
-            mStateHandlers = mLauncher.createStateHandlers();
+            mStateHandlers = mActivity.createStateHandlers();
         }
         return mStateHandlers;
     }
@@ -138,29 +106,29 @@
      * Returns true if the state changes should be animated.
      */
     public boolean shouldAnimateStateChange() {
-        return !mLauncher.isForceInvisible() && mLauncher.isStarted();
+        return !mActivity.isForceInvisible() && mActivity.isStarted();
     }
 
     /**
      * @return {@code true} if the state matches the current state and there is no active
      *         transition to different state.
      */
-    public boolean isInStableState(LauncherState state) {
+    public boolean isInStableState(STATE_TYPE state) {
         return mState == state && mCurrentStableState == state
                 && (mConfig.targetState == null || mConfig.targetState == state);
     }
 
     /**
-     * @see #goToState(LauncherState, boolean, Runnable)
+     * @see #goToState(STATE_TYPE, boolean, Runnable)
      */
-    public void goToState(LauncherState state) {
+    public void goToState(STATE_TYPE state) {
         goToState(state, shouldAnimateStateChange());
     }
 
     /**
-     * @see #goToState(LauncherState, boolean, Runnable)
+     * @see #goToState(STATE_TYPE, boolean, Runnable)
      */
-    public void goToState(LauncherState state, boolean animated) {
+    public void goToState(STATE_TYPE state, boolean animated) {
         goToState(state, animated, 0, null);
     }
 
@@ -171,21 +139,21 @@
      *                true otherwise
      * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
      */
-    public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) {
+    public void goToState(STATE_TYPE state, boolean animated, Runnable onCompleteRunnable) {
         goToState(state, animated, 0, onCompleteRunnable);
     }
 
     /**
      * Changes the Launcher state to the provided state after the given delay.
      */
-    public void goToState(LauncherState state, long delay, Runnable onCompleteRunnable) {
+    public void goToState(STATE_TYPE state, long delay, Runnable onCompleteRunnable) {
         goToState(state, true, delay, onCompleteRunnable);
     }
 
     /**
      * Changes the Launcher state to the provided state after the given delay.
      */
-    public void goToState(LauncherState state, long delay) {
+    public void goToState(STATE_TYPE state, long delay) {
         goToState(state, true, delay, null);
     }
 
@@ -196,7 +164,7 @@
     public void reapplyState(boolean cancelCurrentAnimation) {
         boolean wasInAnimation = mConfig.currentAnimation != null;
         if (cancelCurrentAnimation) {
-            cancelAllStateElementAnimation();
+            mAtomicAnimationFactory.cancelAllStateElementAnimation();
             cancelAnimation();
         }
         if (mConfig.currentAnimation == null) {
@@ -209,10 +177,10 @@
         }
     }
 
-    private void goToState(LauncherState state, boolean animated, long delay,
+    private void goToState(STATE_TYPE state, boolean animated, long delay,
             final Runnable onCompleteRunnable) {
-        animated &= Utilities.areAnimationsEnabled(mLauncher);
-        if (mLauncher.isInState(state)) {
+        animated &= Utilities.areAnimationsEnabled(mActivity);
+        if (mActivity.isInState(state)) {
             if (mConfig.currentAnimation == null) {
                 // Run any queued runnable
                 if (onCompleteRunnable != null) {
@@ -230,11 +198,11 @@
         }
 
         // Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
-        LauncherState fromState = mState;
+        STATE_TYPE fromState = mState;
         mConfig.reset();
 
         if (!animated) {
-            cancelAllStateElementAnimation();
+            mAtomicAnimationFactory.cancelAllStateElementAnimation();
             onStateTransitionStart(state);
             for (StateHandler handler : getStateHandlers()) {
                 handler.setState(state);
@@ -263,13 +231,13 @@
         }
     }
 
-    private void goToStateAnimated(LauncherState state, LauncherState fromState,
+    private void goToStateAnimated(STATE_TYPE state, STATE_TYPE fromState,
             Runnable onCompleteRunnable) {
-        // Since state NORMAL can be reached from multiple states, just assume that the
+        // Since state mBaseState can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
-        mConfig.duration = state == NORMAL
-                ? fromState.getTransitionDuration(mLauncher)
-                : state.getTransitionDuration(mLauncher);
+        mConfig.duration = state == mBaseState
+                ? fromState.getTransitionDuration(mActivity)
+                : state.getTransitionDuration(mActivity);
         prepareForAtomicAnimation(fromState, state, mConfig);
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
         if (onCompleteRunnable != null) {
@@ -283,20 +251,20 @@
      * - Setting interpolators for various animations included in the state transition.
      * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
      */
-    public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
+    public void prepareForAtomicAnimation(STATE_TYPE fromState, STATE_TYPE toState,
             StateAnimationConfig config) {
-        toState.prepareForAtomicAnimation(mLauncher, fromState, config);
+        mAtomicAnimationFactory.prepareForAtomicAnimation(fromState, toState, config);
     }
 
     /**
      * Creates an animation representing atomic transitions between the provided states
      */
     public AnimatorSet createAtomicAnimation(
-            LauncherState fromState, LauncherState toState, StateAnimationConfig config) {
+            STATE_TYPE fromState, STATE_TYPE toState, StateAnimationConfig config) {
         PendingAnimation builder = new PendingAnimation(config.duration);
         prepareForAtomicAnimation(fromState, toState, config);
 
-        for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
+        for (StateHandler handler : mActivity.getStateManager().getStateHandlers()) {
             handler.setStateWithAnimation(toState, config, builder);
         }
         return builder.getAnim();
@@ -306,23 +274,23 @@
      * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
      * state transition.
      * @param state the final state for the transition.
-     * @param duration intended duration for normal playback. Use higher duration for better
+     * @param duration intended duration for state playback. Use higher duration for better
      *                accuracy.
      */
     public AnimatorPlaybackController createAnimationToNewWorkspace(
-            LauncherState state, long duration) {
+            STATE_TYPE state, long duration) {
         return createAnimationToNewWorkspace(state, duration, ANIM_ALL_COMPONENTS);
     }
 
     public AnimatorPlaybackController createAnimationToNewWorkspace(
-            LauncherState state, long duration, @AnimationFlags int animComponents) {
+            STATE_TYPE state, long duration, @AnimationFlags int animComponents) {
         StateAnimationConfig config = new StateAnimationConfig();
         config.duration = duration;
         config.animFlags = animComponents;
         return createAnimationToNewWorkspace(state, config);
     }
 
-    public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
+    public AnimatorPlaybackController createAnimationToNewWorkspace(STATE_TYPE state,
             StateAnimationConfig config) {
         config.userControlled = true;
         mConfig.reset();
@@ -332,7 +300,11 @@
         return mConfig.playbackController;
     }
 
-    private PendingAnimation createAnimationToNewWorkspaceInternal(final LauncherState state) {
+    private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "createAnimationToNewWorkspaceInternal: "
+                    + state);
+        }
         PendingAnimation builder = new PendingAnimation(mConfig.duration);
         for (StateHandler handler : getStateHandlers()) {
             handler.setStateWithAnimation(state, mConfig, builder);
@@ -347,6 +319,9 @@
 
             @Override
             public void onAnimationSuccess(Animator animator) {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "onAnimationSuccess: " + state);
+                }
                 onStateTransitionEnd(state);
             }
         });
@@ -354,46 +329,33 @@
         return builder;
     }
 
-    private void onStateTransitionStart(LauncherState state) {
-        if (mState != state) {
-            mState.onStateDisabled(mLauncher);
-        }
+    private void onStateTransitionStart(STATE_TYPE state) {
         mState = state;
-        mState.onStateEnabled(mLauncher);
-        mLauncher.onStateSetStart(mState);
-
-        if (state.disablePageClipping) {
-            // Only disable clipping if needed, otherwise leave it as previous value.
-            mLauncher.getWorkspace().setClipChildren(false);
-        }
+        mActivity.onStateSetStart(mState);
 
         for (int i = mListeners.size() - 1; i >= 0; i--) {
             mListeners.get(i).onStateTransitionStart(state);
         }
     }
 
-    private void onStateTransitionEnd(LauncherState state) {
+    private void onStateTransitionEnd(STATE_TYPE state) {
         // Only change the stable states after the transitions have finished
         if (state != mCurrentStableState) {
             mLastStableState = state.getHistoryForState(mCurrentStableState);
             mCurrentStableState = state;
         }
 
-        state.onStateTransitionEnd(mLauncher);
-        mLauncher.onStateSetEnd(state);
-
-        if (state == NORMAL) {
+        mActivity.onStateSetEnd(state);
+        if (state == mBaseState) {
             setRestState(null);
         }
 
         for (int i = mListeners.size() - 1; i >= 0; i--) {
             mListeners.get(i).onStateTransitionComplete(state);
         }
-
-        AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
     }
 
-    public LauncherState getLastState() {
+    public STATE_TYPE getLastState() {
         return mLastStableState;
     }
 
@@ -402,18 +364,18 @@
             // The user is doing something. Lets not mess it up
             return;
         }
-        if (mState.disableRestore) {
+        if (mState.shouldDisableRestore()) {
             goToState(getRestState());
             // Reset history
-            mLastStableState = NORMAL;
+            mLastStableState = mBaseState;
         }
     }
 
-    public LauncherState getRestState() {
-        return mRestState == null ? NORMAL : mRestState;
+    public STATE_TYPE getRestState() {
+        return mRestState == null ? mBaseState : mRestState;
     }
 
-    public void setRestState(LauncherState restState) {
+    public void setRestState(STATE_TYPE restState) {
         mRestState = restState;
     }
 
@@ -461,42 +423,23 @@
         mConfig.setAnimation(anim, null);
     }
 
-    private void cancelAllStateElementAnimation() {
-        if (mStateElementAnimators == null) {
-            return;
-        }
-
-        for (Animator animator : mStateElementAnimators) {
-            if (animator != null) {
-                animator.cancel();
-            }
-        }
-    }
-
     /**
      * Cancels a currently running gesture animation
      */
     public void cancelStateElementAnimation(int index) {
-        if (mStateElementAnimators == null) {
-            return;
-        }
-        if (mStateElementAnimators[index] != null) {
-            mStateElementAnimators[index].cancel();
+        if (mAtomicAnimationFactory.mStateElementAnimators[index] != null) {
+            mAtomicAnimationFactory.mStateElementAnimators[index].cancel();
         }
     }
 
     public Animator createStateElementAnimation(int index, float... values) {
         cancelStateElementAnimation(index);
-        LauncherAppTransitionManager latm = mLauncher.getAppTransitionManager();
-        if (mStateElementAnimators == null) {
-            mStateElementAnimators = new Animator[latm.getStateElementAnimationsCount()];
-        }
-        Animator anim = latm.createStateElementAnimation(index, values);
-        mStateElementAnimators[index] = anim;
+        Animator anim = mAtomicAnimationFactory.createStateElementAnimation(index, values);
+        mAtomicAnimationFactory.mStateElementAnimators[index] = anim;
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                mStateElementAnimators[index] = null;
+                mAtomicAnimationFactory.mStateElementAnimators[index] = null;
             }
         });
         return anim;
@@ -527,13 +470,14 @@
         }
     }
 
-    private static class AnimationState extends StateAnimationConfig implements AnimatorListener {
+    private static class AnimationState<STATE_TYPE> extends StateAnimationConfig
+            implements AnimatorListener {
 
         private static final StateAnimationConfig DEFAULT = new StateAnimationConfig();
 
         public AnimatorPlaybackController playbackController;
         public AnimatorSet currentAnimation;
-        public LauncherState targetState;
+        public STATE_TYPE targetState;
 
         // Id to keep track of config changes, to tie an animation with the corresponding request
         public int changeId = 0;
@@ -568,7 +512,7 @@
             }
         }
 
-        public void setAnimation(AnimatorSet animation, LauncherState targetState) {
+        public void setAnimation(AnimatorSet animation, STATE_TYPE targetState) {
             currentAnimation = animation;
             this.targetState = targetState;
             currentAnimation.addListener(this);
@@ -584,24 +528,66 @@
         public void onAnimationRepeat(Animator animator) { }
     }
 
-    public interface StateHandler {
+    public interface StateHandler<STATE_TYPE> {
 
         /**
          * Updates the UI to {@param state} without any animations
          */
-        void setState(LauncherState state);
+        void setState(STATE_TYPE state);
 
         /**
          * Sets the UI to {@param state} by animating any changes.
          */
         void setStateWithAnimation(
-                LauncherState toState, StateAnimationConfig config, PendingAnimation animation);
+                STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation);
     }
 
-    public interface StateListener {
+    public interface StateListener<STATE_TYPE> {
 
-        default void onStateTransitionStart(LauncherState toState) { }
+        default void onStateTransitionStart(STATE_TYPE toState) { }
 
-        default void onStateTransitionComplete(LauncherState finalState) { }
+        default void onStateTransitionComplete(STATE_TYPE finalState) { }
+    }
+
+    /**
+     * Factory class to configure and create atomic animations.
+     */
+    public static class AtomicAnimationFactory<STATE_TYPE> {
+
+        private final Animator[] mStateElementAnimators;
+
+        /**
+         *
+         * @param sharedElementAnimCount number of animations which run on state properties
+         */
+        public AtomicAnimationFactory(int sharedElementAnimCount) {
+            mStateElementAnimators = new Animator[sharedElementAnimCount];
+        }
+
+        void cancelAllStateElementAnimation() {
+            for (Animator animator : mStateElementAnimators) {
+                if (animator != null) {
+                    animator.cancel();
+                }
+            }
+        }
+
+        /**
+         * Creates animations for elements which can be also be part of state transitions. The
+         * actual definition of the animation is up to the app to define.
+         *
+         */
+        public Animator createStateElementAnimation(int index, float... values) {
+            throw new RuntimeException("Unknown gesture animation " + index);
+        }
+
+        /**
+         * Prepares for a non-user controlled animation from fromState to this state. Preparations
+         * include:
+         * - Setting interpolators for various animations included in the state transition.
+         * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
+         */
+        public void prepareForAtomicAnimation(
+                STATE_TYPE fromState, STATE_TYPE toState, StateAnimationConfig config) { }
     }
 }
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
new file mode 100644
index 0000000..0a1607c
--- /dev/null
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -0,0 +1,149 @@
+/*
+ * 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.launcher3.statemanager;
+
+import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
+
+import android.os.Handler;
+
+import androidx.annotation.CallSuper;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * Abstract activity with state management
+ * @param <STATE_TYPE> Type of state object
+ */
+public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
+        extends BaseDraggingActivity {
+
+    public final Handler mHandler = new Handler();
+    private final Runnable mHandleDeferredResume = this::handleDeferredResume;
+    private boolean mDeferredResumePending;
+
+    /**
+     * Create handlers to control the property changes for this activity
+     */
+    protected abstract StateHandler<STATE_TYPE>[] createStateHandlers();
+
+    /**
+     * Returns true if the activity is in the provided state
+     */
+    public boolean isInState(STATE_TYPE state) {
+        return getStateManager().getState() == state;
+    }
+
+    /**
+     * Returns the state manager for this activity
+     */
+    public abstract StateManager<STATE_TYPE> getStateManager();
+
+    /**
+     * Called when transition to the state starts
+     */
+    @CallSuper
+    public void onStateSetStart(STATE_TYPE state) {
+        if (mDeferredResumePending) {
+            handleDeferredResume();
+        }
+    }
+
+    /**
+     * Called when transition to state ends
+     */
+    public void onStateSetEnd(STATE_TYPE state) { }
+
+    /**
+     * Creates a factory for atomic state animations
+     */
+    public AtomicAnimationFactory<STATE_TYPE> createAtomicAnimationFactory() {
+        return new AtomicAnimationFactory(0);
+    }
+
+    @Override
+    public void reapplyUi() {
+        reapplyUi(true /* cancelCurrentAnimation */);
+    }
+
+    /**
+     * Re-applies if any state transition is not running, optionally cancelling
+     * the transition if requested.
+     */
+    public void reapplyUi(boolean cancelCurrentAnimation) {
+        getStateManager().reapplyState(cancelCurrentAnimation);
+    }
+
+    @Override
+    protected void onStop() {
+        BaseDragLayer dragLayer = getDragLayer();
+        final boolean wasActive = isUserActive();
+        final STATE_TYPE origState = getStateManager().getState();
+        final int origDragLayerChildCount = dragLayer.getChildCount();
+        super.onStop();
+
+        getStateManager().moveToRestState();
+
+        // Workaround for b/78520668, explicitly trim memory once UI is hidden
+        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+
+        if (wasActive) {
+            // The expected condition is that this activity is stopped because the device goes to
+            // sleep and the UI may have noticeable changes.
+            dragLayer.post(() -> {
+                if ((!getStateManager().isInStableState(origState)
+                        // The drag layer may be animating (e.g. dismissing QSB).
+                        || dragLayer.getAlpha() < 1
+                        // Maybe an ArrowPopup is closed.
+                        || dragLayer.getChildCount() != origDragLayerChildCount)) {
+                    onUiChangedWhileSleeping();
+                }
+            });
+        }
+    }
+
+    /**
+     * Called if the Activity UI changed while the activity was not visible
+     */
+    protected void onUiChangedWhileSleeping() { }
+
+    private void handleDeferredResume() {
+        if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
+            onDeferredResumed();
+            addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
+
+            mDeferredResumePending = false;
+        } else {
+            mDeferredResumePending = true;
+        }
+    }
+
+    /**
+     * Called want the activity has stayed resumed for 1 frame.
+     */
+    protected void onDeferredResumed() { }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mHandler.removeCallbacks(mHandleDeferredResume);
+        Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
+    }
+}
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index 43f30f1..9ea8436 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -15,10 +15,10 @@
  */
 package com.android.launcher3.states;
 
+import android.content.Context;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
@@ -26,7 +26,7 @@
  */
 public class HintState extends LauncherState {
 
-    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE
+    private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
             | FLAG_HAS_SYS_UI_SCRIM;
 
     public HintState(int id) {
@@ -34,7 +34,7 @@
     }
 
     @Override
-    public int getTransitionDuration(Launcher launcher) {
+    public int getTransitionDuration(Context context) {
         return 80;
     }
 
@@ -48,16 +48,4 @@
         // Treat the QSB as part of the hotseat so they move together.
         return getHotseatScaleAndTranslation(launcher);
     }
-
-    @Override
-    public void onStateTransitionEnd(Launcher launcher) {
-        LauncherStateManager stateManager = launcher.getStateManager();
-        Workspace workspace = launcher.getWorkspace();
-        boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
-        stateManager.goToState(NORMAL, true, willMoveScreens ? null
-                : launcher.getScrimView()::startDragHandleEducationAnim);
-        if (willMoveScreens) {
-            workspace.post(workspace::moveToDefaultScreen);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index b2ff69a..f0e0557 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,13 +15,10 @@
  */
 package com.android.launcher3.states;
 
-import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
-
 import android.content.Context;
 import android.graphics.Rect;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
@@ -32,16 +29,17 @@
  */
 public class SpringLoadedState extends LauncherState {
 
-    private static final int STATE_FLAGS = FLAG_MULTI_PAGE |
-            FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED |
-            FLAG_DISABLE_PAGE_CLIPPING | FLAG_PAGE_BACKGROUNDS | FLAG_HIDE_BACK_BUTTON;
+    private static final int STATE_FLAGS = FLAG_MULTI_PAGE
+            | FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
+            | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS
+            | FLAG_HIDE_BACK_BUTTON;
 
     public SpringLoadedState(int id) {
         super(id, ContainerType.OVERVIEW, STATE_FLAGS);
     }
 
     @Override
-    public int getTransitionDuration(Launcher launcher) {
+    public int getTransitionDuration(Context context) {
         return 150;
     }
 
@@ -88,28 +86,7 @@
     }
 
     @Override
-    public void onStateEnabled(Launcher launcher) {
-        Workspace ws = launcher.getWorkspace();
-        ws.showPageIndicatorAtCurrentScroll();
-        ws.getPageIndicator().setShouldAutoHide(false);
-
-        // Prevent any Un/InstallShortcutReceivers from updating the db while we are
-        // in spring loaded mode
-        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
-        launcher.getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
-    }
-
-    @Override
     public float getWorkspaceScrimAlpha(Launcher launcher) {
         return 0.3f;
     }
-
-    @Override
-    public void onStateDisabled(final Launcher launcher) {
-        launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
-
-        // Re-enable any Un/InstallShortcutReceiver and now process any queued items
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
-    }
 }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index fba6269..0aab495 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -99,4 +99,6 @@
     public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
 
     public static final String PERMANENT_DIAG_TAG = "TaplTarget";
+    public static final String PAUSE_NOT_DETECTED = "b/139891609";
+    public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index cbc5257..52e2ab8 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -31,6 +31,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.os.SystemClock;
+import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 
@@ -43,6 +44,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -162,6 +164,9 @@
 
     @Override
     public final boolean onControllerTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "onControllerTouchEvent");
+        }
         return mDetector.onTouchEvent(ev);
     }
 
@@ -194,6 +199,10 @@
 
         mFromState = newFromState;
         mToState = newToState;
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "reinitCurrentAnimation: "
+                    + newToState.ordinal);
+        }
 
         mStartProgress = 0;
         mPassedOverviewAtomicThreshold = false;
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 1276ece..01b33d8 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -26,6 +26,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.testing.TestProtocol;
+
 import java.util.LinkedList;
 import java.util.Queue;
 
@@ -173,6 +175,9 @@
                 if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
                     setState(ScrollState.DRAGGING);
                 }
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.PAUSE_NOT_DETECTED, "before report dragging");
+                }
                 if (mState == ScrollState.DRAGGING) {
                     reportDragging(ev);
                 }
diff --git a/src/com/android/launcher3/touch/HomeRotatedPageHandler.java b/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
index 710b676..db5c659 100644
--- a/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
+++ b/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
@@ -18,6 +18,7 @@
 
 import android.graphics.RectF;
 import android.view.Surface;
+import android.widget.LinearLayout;
 
 public class HomeRotatedPageHandler extends PortraitPagedViewHandler {
     @Override
@@ -46,4 +47,9 @@
             }
         } // TODO (b/149609488) handle 180 case as well
     }
+
+    @Override
+    public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
+        return taskMenuLayout.getOrientation();
+    }
 }
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index dc50053..5007ca0 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -126,6 +126,11 @@
     }
 
     @Override
+    public int getClearAllScrollOffset(View view, boolean isRtl) {
+        return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2;
+    }
+
+    @Override
     public int getSecondaryDimension(View view) {
         return view.getWidth();
     }
@@ -172,6 +177,11 @@
     }
 
     @Override
+    public int getRotation() {
+        return Surface.ROTATION_90;
+    }
+
+    @Override
     public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
         if (displayRotation == Surface.ROTATION_0) {
             rect.offset(0, value);
@@ -241,7 +251,7 @@
     }
 
     @Override
-    public int getTaskMenuLayoutOrientation() {
+    public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
         return LinearLayout.HORIZONTAL;
     }
 
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index cc15f99..cdfe6d5 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -62,6 +62,7 @@
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
     float getPrimarySize(RectF rect);
+    int getClearAllScrollOffset(View view, boolean isRtl);
     int getSecondaryDimension(View view);
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
@@ -80,6 +81,7 @@
     void setMaxScroll(AccessibilityEvent event, int maxScroll);
     boolean getRecentsRtlSetting(Resources resources);
     float getDegreesRotated();
+    int getRotation();
     void offsetTaskRect(RectF rect, float value, int delta, int launcherRotation);
     int getPrimaryValue(int x, int y);
     int getSecondaryValue(int x, int y);
@@ -94,7 +96,7 @@
     float getTaskMenuX(float x, View thumbnailView);
     float getTaskMenuY(float y, View thumbnailView);
     int getTaskMenuWidth(View view);
-    int getTaskMenuLayoutOrientation();
+    int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout);
     void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
 
     /**
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 7c30e29..25dc1f6 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -123,6 +123,11 @@
     }
 
     @Override
+    public int getClearAllScrollOffset(View view, boolean isRtl) {
+        return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
+    }
+
+    @Override
     public int getSecondaryDimension(View view) {
         return view.getHeight();
     }
@@ -169,6 +174,11 @@
     }
 
     @Override
+    public int getRotation() {
+        return Surface.ROTATION_0;
+    }
+
+    @Override
     public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
         if (displayRotation == Surface.ROTATION_0) {
             rect.offset(value, 0);
@@ -238,7 +248,7 @@
     }
 
     @Override
-    public int getTaskMenuLayoutOrientation() {
+    public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
         return LinearLayout.VERTICAL;
     }
 
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 7beb7f7..dde2829 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -55,6 +55,11 @@
     }
 
     @Override
+    public int getRotation() {
+        return Surface.ROTATION_270;
+    }
+
+    @Override
     public boolean isGoingUp(float displacement) {
         return displacement < 0;
     }
@@ -75,4 +80,15 @@
     public float getTaskMenuY(float y, View thumbnailView) {
         return y + thumbnailView.getMeasuredHeight();
     }
+
+    @Override
+    public int getClearAllScrollOffset(View view, boolean isRtl) {
+        return (isRtl ? view.getPaddingTop() : - view.getPaddingBottom()) / 2;
+    }
+
+    @Override
+    public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
+        view.setTranslationX(0);
+        view.setTranslationY(-translation);
+    }
 }
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index d725486..875eefb 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.graphics.PointF;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
@@ -24,6 +25,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
@@ -103,6 +105,11 @@
         super(config, isRtl);
         mListener = l;
         mDir = dir;
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector.ctor "
+                    + l.getClass().getSimpleName()
+                    + " @ " + android.util.Log.getStackTraceString(new Throwable()));
+        }
     }
 
     public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -154,6 +161,10 @@
 
     @Override
     protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector "
+                    + mListener.getClass().getSimpleName());
+        }
         mListener.onDrag(mDir.extractDirection(displacement),
                 mDir.extractOrthogonalDirection(displacement), event);
     }
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/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java
index f18e411..fabdb4e 100644
--- a/src/com/android/launcher3/util/DefaultDisplay.java
+++ b/src/com/android/launcher3/util/DefaultDisplay.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.util;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.content.Context;
@@ -26,7 +28,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
-import android.view.WindowManager;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -46,6 +47,8 @@
     public static final int CHANGE_ROTATION = 1 << 1;
     public static final int CHANGE_FRAME_DELAY = 1 << 2;
 
+    public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION | CHANGE_FRAME_DELAY;
+
     private final Context mContext;
     private final int mId;
     private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
@@ -142,7 +145,7 @@
         }
 
         private Info(Context context) {
-            this(context.getSystemService(WindowManager.class).getDefaultDisplay());
+            this(context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
         }
 
         public Info(Display display) {
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index b54074e..528a6e9 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -12,6 +12,8 @@
 public class LogConfig {
     // These are list of strings that can be used to replace TAGNAME.
 
+    public static final String STATSLOG = "StatsLog";
+
     /**
      * After this tag is turned on, whenever there is n user event, debug information is
      * printed out to logcat.
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index baa1eee..1620289 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -21,7 +21,6 @@
 import androidx.annotation.StringDef;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherStateManager;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -71,13 +70,10 @@
 
     protected final T mLauncher;
     protected final SharedPreferences mSharedPrefs;
-    protected final LauncherStateManager mStateManager;
 
-    public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs,
-            LauncherStateManager stateManager) {
+    public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs) {
         mLauncher = launcher;
         mSharedPrefs = sharedPrefs;
-        mStateManager = stateManager;
     }
 
     /** @return The number of times we have seen the given event. */
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index 478141a..1a8e11b 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -101,7 +101,6 @@
     private @Nullable Drawable mForeground;
     private @Nullable Drawable mBackground;
 
-    private boolean mIsVerticalBarLayout = false;
     private boolean mIsAdaptiveIcon = false;
 
     private ValueAnimator mRevealAnimator;
@@ -145,7 +144,8 @@
     }
 
     void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
-            boolean isOpening, float scale, float minSize, LayoutParams parentLp) {
+            boolean isOpening, float scale, float minSize, LayoutParams parentLp,
+            boolean isVerticalBarLayout) {
         DeviceProfile dp = mLauncher.getDeviceProfile();
         float dX = mIsRtl
                 ? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
@@ -158,7 +158,7 @@
                 Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
                 LINEAR), 0, 1);
 
-        if (mIsVerticalBarLayout) {
+        if (isVerticalBarLayout) {
             mOutline.right = (int) (rect.width() / scale);
         } else {
             mOutline.bottom = (int) (rect.height() / scale);
@@ -183,16 +183,16 @@
                 mRevealAnimator.setCurrentFraction(shapeRevealProgress);
             }
 
-            float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
+            float drawableScale = (isVerticalBarLayout ? mOutline.width() : mOutline.height())
                     / minSize;
-            setBackgroundDrawableBounds(drawableScale);
+            setBackgroundDrawableBounds(drawableScale, isVerticalBarLayout);
             if (isOpening) {
                 // Center align foreground
                 int height = mFinalDrawableBounds.height();
                 int width = mFinalDrawableBounds.width();
-                int diffY = mIsVerticalBarLayout ? 0
+                int diffY = isVerticalBarLayout ? 0
                         : (int) (((height * drawableScale) - height) / 2);
-                int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
+                int diffX = isVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
                         : 0;
                 sTmpRect.set(mFinalDrawableBounds);
                 sTmpRect.offset(diffX, diffY);
@@ -210,11 +210,11 @@
         invalidateOutline();
     }
 
-    private void setBackgroundDrawableBounds(float scale) {
+    private void setBackgroundDrawableBounds(float scale, boolean isVerticalBarLayout) {
         sTmpRect.set(mFinalDrawableBounds);
         Utilities.scaleRectAboutCenter(sTmpRect, scale);
         // Since the drawable is at the top of the view, we need to offset to keep it centered.
-        if (mIsVerticalBarLayout) {
+        if (isVerticalBarLayout) {
             sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
         } else {
             sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
@@ -228,7 +228,8 @@
         }
     }
 
-    void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening) {
+    void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening,
+            boolean isVerticalBarLayout) {
         mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
         if (mIsAdaptiveIcon) {
             boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
@@ -264,7 +265,7 @@
             }
 
             float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
-            if (mIsVerticalBarLayout) {
+            if (isVerticalBarLayout) {
                 lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
             } else {
                 lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
@@ -285,7 +286,7 @@
                 bgDrawableStartScale = scale;
                 mOutline.set(0, 0, lp.width, lp.height);
             }
-            setBackgroundDrawableBounds(bgDrawableStartScale);
+            setBackgroundDrawableBounds(bgDrawableStartScale, isVerticalBarLayout);
             mEndRevealRect.set(0, 0, lp.width, lp.height);
             setOutlineProvider(new ViewOutlineProvider() {
                 @Override
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 6e21512..bd12e06 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -161,7 +161,7 @@
         float scale = Math.max(1f, Math.min(scaleX, scaleY));
 
         mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
-                minSize, lp);
+                minSize, lp, mIsVerticalBarLayout);
 
         setPivotX(0);
         setPivotY(0);
@@ -335,7 +335,7 @@
         final InsettableFrameLayout.LayoutParams lp =
                 (InsettableFrameLayout.LayoutParams) getLayoutParams();
         mBadge = badge;
-        mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening);
+        mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout);
         if (drawable instanceof AdaptiveIconDrawable) {
             final int originalHeight = lp.height;
             final int originalWidth = lp.width;
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index da874cf..a2c7d14 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -62,10 +62,10 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -116,7 +116,8 @@
     private final AccessibilityManager mAM;
     protected final int mEndScrim;
 
-    private final StateListener mAccessibilityLauncherStateListener = new StateListener() {
+    private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
+            new StateListener<LauncherState>() {
         @Override
         public void onStateTransitionComplete(LauncherState finalState) {
             setImportantForAccessibility(finalState == ALL_APPS
@@ -383,7 +384,7 @@
 
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
-        LauncherStateManager stateManager = mLauncher.getStateManager();
+        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
         stateManager.removeStateListener(mAccessibilityLauncherStateListener);
 
         if (enabled) {
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index dc0e2e0..513ce59 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -29,7 +29,7 @@
 import android.widget.TextView;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -44,7 +44,7 @@
     private static final long HIDE_DURATION_MS = 180;
     private static final int TIMEOUT_DURATION_MS = 4000;
 
-    private final Launcher mLauncher;
+    private final BaseDraggingActivity mActivity;
     private Runnable mOnDismissed;
 
     public Snackbar(Context context, AttributeSet attrs) {
@@ -53,25 +53,25 @@
 
     public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mLauncher = Launcher.getLauncher(context);
+        mActivity = BaseDraggingActivity.fromContext(context);
         inflate(context, R.layout.snackbar, this);
     }
 
-    public static void show(Launcher launcher, int labelStringResId, int actionStringResId,
-            Runnable onDismissed, Runnable onActionClicked) {
-        closeOpenViews(launcher, true, TYPE_SNACKBAR);
-        Snackbar snackbar = new Snackbar(launcher, null);
+    public static void show(BaseDraggingActivity activity, int labelStringResId,
+            int actionStringResId, Runnable onDismissed, Runnable onActionClicked) {
+        closeOpenViews(activity, true, TYPE_SNACKBAR);
+        Snackbar snackbar = new Snackbar(activity, null);
         // Set some properties here since inflated xml only contains the children.
         snackbar.setOrientation(HORIZONTAL);
         snackbar.setGravity(Gravity.CENTER_VERTICAL);
-        Resources res = launcher.getResources();
+        Resources res = activity.getResources();
         snackbar.setElevation(res.getDimension(R.dimen.snackbar_elevation));
         int padding = res.getDimensionPixelSize(R.dimen.snackbar_padding);
         snackbar.setPadding(padding, padding, padding, padding);
         snackbar.setBackgroundResource(R.drawable.round_rect_primary);
 
         snackbar.mIsOpen = true;
-        DragLayer dragLayer = launcher.getDragLayer();
+        BaseDragLayer dragLayer = activity.getDragLayer();
         dragLayer.addView(snackbar);
 
         DragLayer.LayoutParams params = (DragLayer.LayoutParams) snackbar.getLayoutParams();
@@ -80,7 +80,7 @@
         int maxMarginLeftRight = res.getDimensionPixelSize(R.dimen.snackbar_max_margin_left_right);
         int minMarginLeftRight = res.getDimensionPixelSize(R.dimen.snackbar_min_margin_left_right);
         int marginBottom = res.getDimensionPixelSize(R.dimen.snackbar_margin_bottom);
-        Rect insets = launcher.getDeviceProfile().getInsets();
+        Rect insets = activity.getDeviceProfile().getInsets();
         int maxWidth = dragLayer.getWidth() - minMarginLeftRight * 2 - insets.left - insets.right;
         int minWidth = dragLayer.getWidth() - maxMarginLeftRight * 2 - insets.left - insets.right;
         params.width = minWidth;
@@ -135,7 +135,7 @@
                 .setDuration(SHOW_DURATION_MS)
                 .setInterpolator(Interpolators.ACCEL_DEACCEL)
                 .start();
-        int timeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(launcher,
+        int timeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(activity,
                 TIMEOUT_DURATION_MS, FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS);
         snackbar.postDelayed(() -> snackbar.close(true), timeout);
     }
@@ -160,7 +160,7 @@
     }
 
     private void onClosed() {
-        mLauncher.getDragLayer().removeView(this);
+        mActivity.getDragLayer().removeView(this);
         if (mOnDismissed != null) {
             mOnDismissed.run();
         }
@@ -179,7 +179,7 @@
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            DragLayer dl = mLauncher.getDragLayer();
+            BaseDragLayer dl = mActivity.getDragLayer();
             if (!dl.isEventOverView(this, ev)) {
                 close(true);
             }
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index 552f662..d35a38f 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -32,18 +32,19 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsPagedView;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 /**
  * On boarding flow for users right after setting up work profile
  */
-public class WorkEduView extends AbstractSlideInView implements Insettable {
+public class WorkEduView extends AbstractSlideInView
+        implements Insettable, StateListener<LauncherState> {
 
     private static final int DEFAULT_CLOSE_DURATION = 200;
     public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
@@ -82,6 +83,12 @@
     }
 
     @Override
+    protected void onCloseComplete() {
+        super.onCloseComplete();
+        mLauncher.getStateManager().removeStateListener(this);
+    }
+
+    @Override
     public void logActionCommand(int command) {
         // Since this is on-boarding popup, it is not a user controlled action.
     }
@@ -150,6 +157,7 @@
     private void show() {
         attachToContainer();
         animateOpen();
+        mLauncher.getStateManager().addStateListener(this);
     }
 
     @Override
@@ -177,8 +185,8 @@
     /**
      * Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
      */
-    public static LauncherStateManager.StateListener showEduFlowIfNeeded(Launcher launcher,
-            @Nullable LauncherStateManager.StateListener oldListener) {
+    public static StateListener<LauncherState> showEduFlowIfNeeded(Launcher launcher,
+            @Nullable StateListener<LauncherState> oldListener) {
         if (oldListener != null) {
             launcher.getStateManager().removeStateListener(oldListener);
         }
@@ -187,7 +195,7 @@
             return null;
         }
 
-        LauncherStateManager.StateListener listener = new LauncherStateManager.StateListener() {
+        StateListener<LauncherState> listener = new StateListener<LauncherState>() {
             @Override
             public void onStateTransitionComplete(LauncherState finalState) {
                 if (finalState != LauncherState.ALL_APPS) return;
@@ -222,4 +230,9 @@
     private static boolean hasSeenLegacyEdu(Launcher launcher) {
         return launcher.getSharedPrefs().getBoolean(KEY_LEGACY_WORK_EDU_SEEN, false);
     }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        close(false);
+    }
 }
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index a4e7daa..ed42bc4 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -218,7 +218,7 @@
     }
 
     @Override
-    public void getVisualDragBounds(Rect bounds) {
+    public void getWorkspaceVisualDragBounds(Rect bounds) {
         int width = (int) (getMeasuredWidth() * mScaleToFit);
         int height = (int) (getMeasuredHeight() * mScaleToFit);
 
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 4a0b4ef..bef91d2 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -55,7 +55,7 @@
     private static final int FADE_IN_DURATION_MS = 90;
 
     /** Widget cell width is calculated by multiplying this factor to grid cell width. */
-    private static final float WIDTH_SCALE = 2.6f;
+    private static final float WIDTH_SCALE = 3f;
 
     /** Widget preview width is calculated by multiplying this factor to the widget cell width. */
     private static final float PREVIEW_SCALE = 0.8f;
@@ -104,7 +104,7 @@
     }
 
     private void setContainerWidth() {
-        mCellSize = (int) (mDeviceProfile.cellWidthPx * WIDTH_SCALE);
+        mCellSize = (int) (mDeviceProfile.allAppsIconSizePx * WIDTH_SCALE);
         mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
     }
 
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
index b865a20..d458fc4 100644
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.plugins;
 
+import android.app.Activity;
 import android.view.ViewGroup;
 import android.widget.EditText;
-
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
 /**
@@ -27,8 +27,9 @@
 @ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
 public interface AllAppsSearchPlugin extends Plugin {
     String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
-    int VERSION = 1;
+    int VERSION = 2;
 
-    void setup(ViewGroup parent);
+    void setup(ViewGroup parent, Activity activity);
     void setEditText(EditText editText);
+    void setProgress(float progress);
 }
diff --git a/src_plugins/com/android/systemui/plugins/OWNERS b/src_plugins/com/android/systemui/plugins/OWNERS
new file mode 100644
index 0000000..0514999
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OWNERS
@@ -0,0 +1,4 @@
+# When changing interface for this plugin OR when increasing version code, please add Alex
+# Only add other owners if Alex is not available
+per-file AllAppsSearchPlugin.java, globs = set noparent
+per-file AllAppsSearchPlugin.java = alexmang@google.com, hyunyoungs@google.com, sunnygoyal@google.com, twickham@google.com
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index 313ea05..ec3f93f 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -16,9 +16,9 @@
 package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
 
-import com.android.launcher3.AbstractFloatingView;
+import android.content.Context;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
@@ -31,7 +31,7 @@
 
     private static final float PARALLAX_COEFFICIENT = .125f;
 
-    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+    private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE;
 
     private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
         @Override
@@ -45,21 +45,11 @@
     }
 
     @Override
-    public int getTransitionDuration(Launcher context) {
+    public int getTransitionDuration(Context context) {
         return 320;
     }
 
     @Override
-    public void onStateEnabled(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
-            launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
-        }
-
-        AbstractFloatingView.closeAllOpenViews(launcher);
-        dispatchWindowStateChanged(launcher);
-    }
-
-    @Override
     public String getDescription(Launcher launcher) {
         return launcher.getString(R.string.all_apps_button_label);
     }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index 507ff59..7a6332c 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -15,7 +15,8 @@
  */
 package com.android.launcher3.uioverrides.states;
 
-import com.android.launcher3.Launcher;
+import android.content.Context;
+
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
@@ -29,7 +30,7 @@
     }
 
     @Override
-    public int getTransitionDuration(Launcher context) {
+    public int getTransitionDuration(Context context) {
         return 250;
     }
 
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 160daef..7cce044 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -37,6 +37,8 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -49,11 +51,11 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.common.WidgetUtils;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.tapl.TestHelpers;
@@ -276,6 +278,15 @@
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mTargetPackage = mTargetContext.getPackageName();
         mLauncherPid = mLauncher.getPid();
+
+        UserManager userManager = mTargetContext.getSystemService(UserManager.class);
+        if (userManager != null) {
+            for (UserHandle userHandle : userManager.getUserProfiles()) {
+                if (!userHandle.isSystem()) {
+                    mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier());
+                }
+            }
+        }
     }
 
     @After
@@ -297,6 +308,7 @@
             clearPackageData(mDevice.getLauncherPackageName());
             mLauncher.enableDebugTracing();
             mLauncherPid = mLauncher.getPid();
+            mLauncher.waitForLauncherInitialized();
         }
     }
 
@@ -525,7 +537,7 @@
     private static void checkLauncherIntegrity(
             Launcher launcher, ContainerType expectedContainerType) {
         if (launcher != null) {
-            final LauncherStateManager stateManager = launcher.getStateManager();
+            final StateManager<LauncherState> stateManager = launcher.getStateManager();
             final LauncherState stableState = stateManager.getCurrentStableState();
 
             assertTrue("Stable state != state: " + stableState.getClass().getSimpleName() + ", "
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index 5daac39..e1fde3b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -19,6 +19,7 @@
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
 
 import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
 
 import com.android.launcher3.testing.TestProtocol;
@@ -33,11 +34,14 @@
 
     AddToHomeScreenPrompt(LauncherInstrumentation launcher) {
         mLauncher = launcher;
-        mWidgetCell = launcher.waitForLauncherObject(By.clazz(
-                "com.android.launcher3.widget.WidgetCell"));
+        mWidgetCell = launcher.waitForLauncherObject(getSelector());
         mLauncher.assertNotNull("Can't find widget cell object", mWidgetCell);
     }
 
+    private static BySelector getSelector() {
+        return By.clazz("com.android.launcher3.widget.WidgetCell");
+    }
+
     public void addAutomatically() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             if (mLauncher.getNavigationModel()
@@ -53,6 +57,7 @@
             mLauncher.waitForObjectInContainer(
                     mWidgetCell.getParent().getParent().getParent().getParent(),
                     By.text(ADD_AUTOMATICALLY)).click();
+            mLauncher.waitUntilLauncherObjectGone(getSelector());
         }
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 80b8e89..94ab780 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -71,7 +71,6 @@
     }
 
     protected void goToOverviewUnchecked() {
-        final boolean launcherWasVisible = mLauncher.isLauncherVisible();
         switch (mLauncher.getNavigationModel()) {
             case ZERO_BUTTON: {
                 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
@@ -138,11 +137,6 @@
                 break;
         }
         expectSwitchToOverviewEvents();
-
-        if (!launcherWasVisible) {
-            mLauncher.expectEvent(
-                    TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START_ACTIVITY);
-        }
     }
 
     private void expectSwitchToOverviewEvents() {
@@ -192,11 +186,6 @@
                 }
                 final boolean isZeroButton = mLauncher.getNavigationModel()
                         == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
-                if (!launcherWasVisible) {
-                    mLauncher.expectEvent(
-                            TestProtocol.SEQUENCE_MAIN,
-                            LauncherInstrumentation.EVENT_START_ACTIVITY);
-                }
                 mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
                         launcherWasVisible && isZeroButton
                                 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
@@ -208,11 +197,6 @@
                 // Double press the recents button.
                 UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
                 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
-                if (!launcherWasVisible) {
-                    mLauncher.expectEvent(
-                            TestProtocol.SEQUENCE_MAIN,
-                            LauncherInstrumentation.EVENT_START_ACTIVITY);
-                }
                 mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
                 mLauncher.getOverview();
                 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
@@ -220,8 +204,6 @@
                 break;
         }
         mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
-        mLauncher.expectEvent(
-                TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
     }
 
     protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index df7436c..13ecfb8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -25,8 +25,6 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.testing.TestProtocol;
-
 /**
  * Ancestor for AppIcon and AppMenuItem.
  */
@@ -64,8 +62,6 @@
                 event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
                 () -> "Launching an app didn't open a new window: " + mObject.getText());
         expectActivityStartEvents();
-        mLauncher.expectEvent(
-                TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
 
         mLauncher.assertTrue(
                 "App didn't start: " + selector,
@@ -76,7 +72,8 @@
 
     /**
      * Drags an object to the center of homescreen.
-     * @param startsActivity whether it's expected to start an activity.
+     *
+     * @param startsActivity   whether it's expected to start an activity.
      * @param isWidgetShortcut whether we drag a widget shortcut
      */
     public void dragToWorkspace(boolean startsActivity, boolean isWidgetShortcut) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index d46845f..6e9c5a0 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -97,8 +97,6 @@
     private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
     private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
     private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
-    static final Pattern EVENT_START_ACTIVITY = Pattern.compile("Activity\\.onStart");
-    static final Pattern EVENT_STOP_ACTIVITY = Pattern.compile("Activity\\.onStop");
     static final Pattern EVENT_START = Pattern.compile("start:");
 
     static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
@@ -170,7 +168,6 @@
     private static boolean sCheckingEvents;
 
     private boolean mCheckEventsForSuccessfulGestures = false;
-    private int mExpectedPid;
     private Runnable mOnLauncherCrashed;
 
     private static Pattern getTouchEventPattern(String prefix, String action) {
@@ -362,33 +359,12 @@
         return null;
     }
 
-    private String getAnomalyMessage() {
-        if (mExpectedPid != 0 && mExpectedPid != getPid()) {
-            mExpectedPid = 0;
-            if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
-            return "Launcher crashed";
-        }
-
+    public void checkForAnomaly() {
         final String systemAnomalyMessage = getSystemAnomalyMessage();
         if (systemAnomalyMessage != null) {
-            return "http://go/tapl : Tests are broken by a non-Launcher system error: "
-                    + systemAnomalyMessage;
-        }
-
-        return null;
-    }
-
-    public void checkForAnomaly() {
-        final String anomalyMessage = getAnomalyMessage();
-        if (anomalyMessage != null) {
-            if (sCheckingEvents) {
-                sCheckingEvents = false;
-                sEventChecker.finishNoWait();
-            }
-            log("Hierarchy dump for: " + anomalyMessage);
-            dumpViewHierarchy();
-
-            Assert.fail(formatSystemHealthMessage(anomalyMessage));
+            Assert.fail(formatSystemHealthMessage(closeEvents(
+                    "http://go/tapl : Tests are broken by a non-Launcher system error: "
+                            + systemAnomalyMessage, false)));
         }
     }
 
@@ -448,23 +424,29 @@
         return message;
     }
 
-    private void fail(String message) {
-        checkForAnomaly();
-
-        message = "http://go/tapl : " + getContextDescription() + message
-                + " (visible state: " + getVisibleStateMessage() + ")";
+    private String closeEvents(String message, boolean checkEvents) {
+        if (sCheckingEvents) {
+            sCheckingEvents = false;
+            if (checkEvents) {
+                final String eventMismatch = sEventChecker.verify(0, false);
+                if (eventMismatch != null) {
+                    message = message + ", having produced " + eventMismatch;
+                }
+            } else {
+                sEventChecker.finishNoWait();
+            }
+        }
         log("Hierarchy dump for: " + message);
         dumpViewHierarchy();
 
-        if (sCheckingEvents) {
-            sCheckingEvents = false;
-            final String eventMismatch = sEventChecker.verify(0);
-            if (eventMismatch != null) {
-                message = message + ", having produced " + eventMismatch;
-            }
-        }
+        return message;
+    }
 
-        Assert.fail(formatSystemHealthMessage(message));
+    private void fail(String message) {
+        checkForAnomaly();
+        Assert.fail(formatSystemHealthMessage(closeEvents(
+                "http://go/tapl : " + getContextDescription() + message
+                        + " (visible state: " + getVisibleStateMessage() + ")", true)));
     }
 
     private String getContextDescription() {
@@ -560,32 +542,32 @@
                     if (mDevice.isNaturalOrientation()) {
                         waitForLauncherObject(APPS_RES_ID);
                     } else {
-                        waitUntilGone(APPS_RES_ID);
+                        waitUntilLauncherObjectGone(APPS_RES_ID);
                     }
-                    waitUntilGone(OVERVIEW_RES_ID);
-                    waitUntilGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
                     return waitForLauncherObject(WORKSPACE_RES_ID);
                 }
                 case WIDGETS: {
-                    waitUntilGone(WORKSPACE_RES_ID);
-                    waitUntilGone(APPS_RES_ID);
-                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
+                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     return waitForLauncherObject(WIDGETS_RES_ID);
                 }
                 case ALL_APPS: {
-                    waitUntilGone(WORKSPACE_RES_ID);
-                    waitUntilGone(OVERVIEW_RES_ID);
-                    waitUntilGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
                     return waitForLauncherObject(APPS_RES_ID);
                 }
                 case OVERVIEW: {
                     if (hasAllAppsInOverview()) {
                         waitForLauncherObject(APPS_RES_ID);
                     } else {
-                        waitUntilGone(APPS_RES_ID);
+                        waitUntilLauncherObjectGone(APPS_RES_ID);
                     }
-                    waitUntilGone(WORKSPACE_RES_ID);
-                    waitUntilGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
 
                     return waitForLauncherObject(OVERVIEW_RES_ID);
                 }
@@ -593,10 +575,10 @@
                     return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
                 }
                 case BACKGROUND: {
-                    waitUntilGone(WORKSPACE_RES_ID);
-                    waitUntilGone(APPS_RES_ID);
-                    waitUntilGone(OVERVIEW_RES_ID);
-                    waitUntilGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
+                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
                     return null;
                 }
                 default:
@@ -640,14 +622,19 @@
      * @return the Workspace object.
      */
     public Workspace pressHome() {
+        mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(
+                e -> Log.d("b/155926212", e.toString()));
         try (LauncherInstrumentation.Closable e = eventsCheck()) {
+            waitForLauncherInitialized();
             // Click home, then wait for any accessibility event, then wait until accessibility
             // events stop.
             // We need waiting for any accessibility event generated after pressing Home because
             // otherwise waitForIdle may return immediately in case when there was a big enough
             // pause in accessibility events prior to pressing Home.
             final String action;
+            Log.d("b/155926212", "Before isLauncherVisible()");
             final boolean launcherWasVisible = isLauncherVisible();
+            Log.d("b/155926212", "After isLauncherVisible(): " + launcherWasVisible);
             if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
                 checkForAnomaly();
 
@@ -661,7 +648,7 @@
                             false, GestureScope.INSIDE_TO_OUTSIDE);
                     try (LauncherInstrumentation.Closable c = addContextLayer(
                             "Swiped up from context menu to home")) {
-                        waitUntilGone(CONTEXT_MENU_RES_ID);
+                        waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
                     }
                 }
                 if (hasLauncherObject(WORKSPACE_RES_ID)) {
@@ -669,7 +656,7 @@
                 } else {
                     log("Hierarchy before swiping up to home:");
                     dumpViewHierarchy();
-                    log(action = "swiping up to home from " + getVisibleStateMessage());
+                    action = "swiping up to home";
 
                     try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
                         swipeToState(
@@ -680,20 +667,12 @@
                                         ? GestureScope.INSIDE_TO_OUTSIDE
                                         : GestureScope.OUTSIDE);
                     }
-                    if (!launcherWasVisible) {
-                        expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_START_ACTIVITY);
-                    }
                 }
             } else {
-                if (!launcherWasVisible) {
-                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_START_ACTIVITY);
-                }
                 log("Hierarchy before clicking home:");
                 dumpViewHierarchy();
-                log(action = "clicking home button from " + getVisibleStateMessage());
+                action = "clicking home button";
                 try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
-                    mDevice.waitForIdle();
-
                     if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
                         expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
                         expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
@@ -705,13 +684,14 @@
                             !hasLauncherObject(WORKSPACE_RES_ID)
                                     && (hasLauncherObject(APPS_RES_ID)
                                     || hasLauncherObject(OVERVIEW_RES_ID)));
-                    mDevice.waitForIdle();
                 }
             }
             try (LauncherInstrumentation.Closable c = addContextLayer(
                     "performed action to switch to Home - " + action)) {
                 return getWorkspace();
             }
+        } finally {
+            mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(null);
         }
     }
 
@@ -821,9 +801,17 @@
         }
     }
 
-    void waitUntilGone(String resId) {
-        assertTrue("Unexpected launcher object visible: " + resId,
-                mDevice.wait(Until.gone(getLauncherObjectSelector(resId)),
+    void waitUntilLauncherObjectGone(String resId) {
+        waitUntilGoneBySelector(getLauncherObjectSelector(resId));
+    }
+
+    void waitUntilLauncherObjectGone(BySelector selector) {
+        waitUntilGoneBySelector(makeLauncherSelector(selector));
+    }
+
+    private void waitUntilGoneBySelector(BySelector launcherSelector) {
+        assertTrue("Unexpected launcher object visible: " + launcherSelector,
+                mDevice.wait(Until.gone(launcherSelector),
                         WAIT_TIME_MS));
     }
 
@@ -950,9 +938,9 @@
         executeAndWaitForEvent(
                 command,
                 event -> isSwitchToStateEvent(event, expectedState, actualEvents),
-                () -> "Failed to receive an event for the state change: expected "
+                () -> "Failed to receive an event for the state change: expected ["
                         + TestProtocol.stateOrdinalToString(expectedState)
-                        + ", actual: " + eventListToString(actualEvents));
+                        + "], actual: " + eventListToString(actualEvents));
     }
 
     private boolean isSwitchToStateEvent(
@@ -1307,19 +1295,25 @@
     public Closable eventsCheck() {
         Assert.assertTrue("Nested event checking", !sCheckingEvents);
         disableSensorRotation();
-        sCheckingEvents = true;
-        mExpectedPid = getPid();
+        final int initialPid = getPid();
         if (sEventChecker == null) sEventChecker = new LogEventChecker();
         sEventChecker.start();
+        sCheckingEvents = true;
 
         return () -> {
-            checkForAnomaly();
+            if (initialPid != getPid()) {
+                if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
+                checkForAnomaly();
+                Assert.fail(
+                        formatSystemHealthMessage(closeEvents("Launcher crashed", false)));
+            }
 
             if (sCheckingEvents) {
                 sCheckingEvents = false;
                 if (mCheckEventsForSuccessfulGestures) {
-                    final String message = sEventChecker.verify(WAIT_TIME_MS);
+                    final String message = sEventChecker.verify(WAIT_TIME_MS, true);
                     if (message != null) {
+                        checkForAnomaly();
                         Assert.fail(formatSystemHealthMessage(
                                 "http://go/tapl : successful gesture produced " + message));
                     }
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 78dfc36..ac90b1b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -69,8 +69,9 @@
                 mFinished.await();
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
+            } finally {
+                mFinished = null;
             }
-            mFinished = null;
         }
         mEvents.clear();
         Log.d(SKIP_EVENTS_TAG, "Cleared events");
@@ -79,24 +80,33 @@
         final String id = UUID.randomUUID().toString();
         mStartCommand = START_PREFIX + id;
         mFinishCommand = FINISH_PREFIX + id;
+        Log.d(SKIP_EVENTS_TAG, "Expected finish command: " + mFinishCommand);
         Log.d(TestProtocol.TAPL_EVENTS_TAG, mStartCommand);
     }
 
     private void onRun() {
+        while (true) readEvents();
+    }
+
+    private void readEvents() {
         try {
             // Note that we use Runtime.exec to start the log reading process instead of running
             // it via UIAutomation, so that we can directly access the "Process" object and
             // ensure that the instrumentation is not stuck forever.
             final String cmd = "logcat -s " + TestProtocol.TAPL_EVENTS_TAG;
 
+            final Process logcatProcess = Runtime.getRuntime().exec(cmd);
             try (BufferedReader reader = new BufferedReader(new InputStreamReader(
-                    Runtime.getRuntime().exec(cmd).getInputStream()))) {
-                for (; ; ) {
+                    logcatProcess.getInputStream()))) {
+                while (true) {
                     // Skip everything before the next start command.
                     for (; ; ) {
                         final String event = reader.readLine();
-                        if (event.contains(TestProtocol.TAPL_EVENTS_TAG)
-                                && event.contains(mStartCommand)) {
+                        if (event == null) {
+                            Log.d(SKIP_EVENTS_TAG, "Read a null line while waiting for start");
+                            return;
+                        }
+                        if (event.contains(mStartCommand)) {
                             Log.d(SKIP_EVENTS_TAG, "Read start: " + event);
                             break;
                         }
@@ -105,22 +115,30 @@
                     // Store all actual events until the finish command.
                     for (; ; ) {
                         final String event = reader.readLine();
-                        if (event.contains(TestProtocol.TAPL_EVENTS_TAG)) {
-                            if (event.contains(mFinishCommand)) {
-                                mFinished.countDown();
-                                Log.d(SKIP_EVENTS_TAG, "Read finish: " + event);
-                                break;
+                        if (event == null) {
+                            Log.d(SKIP_EVENTS_TAG, "Read a null line after waiting for start");
+                            mEventsCounter.drainPermits();
+                            mEvents.clear();
+                            return;
+                        }
+                        if (event.contains(mFinishCommand)) {
+                            mFinished.countDown();
+                            Log.d(SKIP_EVENTS_TAG, "Read finish: " + event);
+                            break;
+                        } else {
+                            final Matcher matcher = EVENT_LOG_ENTRY.matcher(event);
+                            if (matcher.find()) {
+                                mEvents.add(matcher.group("sequence"), matcher.group("event"));
+                                Log.d(SKIP_EVENTS_TAG, "Read event: " + event);
+                                mEventsCounter.release();
                             } else {
-                                final Matcher matcher = EVENT_LOG_ENTRY.matcher(event);
-                                if (matcher.find()) {
-                                    mEvents.add(matcher.group("sequence"), matcher.group("event"));
-                                    Log.d(SKIP_EVENTS_TAG, "Read event: " + event);
-                                    mEventsCounter.release();
-                                }
+                                Log.d(SKIP_EVENTS_TAG, "Read something unexpected: " + event);
                             }
                         }
                     }
                 }
+            } finally {
+                logcatProcess.destroyForcibly();
             }
         } catch (IOException e) {
             throw new RuntimeException(e);
@@ -150,7 +168,7 @@
         Log.d(TestProtocol.TAPL_EVENTS_TAG, mFinishCommand);
     }
 
-    String verify(long waitForExpectedCountMs) {
+    String verify(long waitForExpectedCountMs, boolean successfulGesture) {
         finishSync(waitForExpectedCountMs);
 
         final StringBuilder sb = new StringBuilder();
@@ -161,7 +179,8 @@
             List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
             Log.d(SKIP_EVENTS_TAG, "Verifying events");
             final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
-            hasMismatches = hasMismatches || mismatchPosition != -1;
+            hasMismatches = hasMismatches
+                    || mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
             formatSequenceWithMismatch(
                     sb,
                     sequence,
@@ -172,7 +191,8 @@
         // Check for unexpected event sequences in the actual data.
         for (String actualNamedSequence : mEvents.keySet()) {
             if (!mExpectedEvents.containsKey(actualNamedSequence)) {
-                hasMismatches = true;
+                hasMismatches = hasMismatches
+                        || !ignoreMistatch(successfulGesture, actualNamedSequence);
                 formatSequenceWithMismatch(
                         sb,
                         actualNamedSequence,
@@ -185,6 +205,11 @@
         return hasMismatches ? "mismatching events: " + sb.toString() : null;
     }
 
+    // Workaround for b/154157191
+    private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
+        return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
+    }
+
     // If the list of actual events matches the list of expected events, returns -1, otherwise
     // the position of the mismatch.
     private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java
index d1268cc..42b6bc9 100644
--- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.tapl;
 
-import android.os.Build;
-
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
@@ -44,12 +42,6 @@
                     + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
             mLauncher.clickLauncherObject(mObject);
             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
-            if (!Build.MODEL.contains("Cuttlefish") ||
-                    Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q &&
-                            !"R".equals(Build.VERSION.CODENAME)) {
-                mLauncher.expectEvent(
-                        TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
-            }
             mLauncher.assertTrue(
                     "App didn't start: " + By.pkg(expectedPackageName),
                     mLauncher.getDevice().wait(Until.hasObject(By.pkg(expectedPackageName)),
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index fae5f19..b235919 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -79,8 +79,6 @@
                         () -> "Launching task didn't open a new window: "
                                 + mTask.getParent().getContentDescription());
                 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
-                mLauncher.expectEvent(
-                        TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
             }
             return new Background(mLauncher);
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 0d91dc2..f0e686f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -229,12 +229,8 @@
         if (startsActivity || isWidgetShortcut) {
             launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
         }
-        if (startsActivity) {
-            launcher.expectEvent(
-                    TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
-        }
         LauncherInstrumentation.log("dragIconToWorkspace: end");
-        launcher.waitUntilGone("drop_target_bar");
+        launcher.waitUntilLauncherObjectGone("drop_target_bar");
     }
 
     /**