Merge "Default ENABLE_PREDICTION_DISMISS  to true" into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index d747bb8..6d105ac 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -71,6 +71,7 @@
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
         android:fullBackupOnly="true"
+        android:backupInForeground="true"
         android:fullBackupContent="@xml/backupscheme"
         android:hardwareAccelerated="true"
         android:icon="@drawable/ic_launcher_home"
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 60afddb..527bfc3 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -52,7 +52,7 @@
             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
-            android:taskAffinity=""
+            android:taskAffinity="${packageName}.launcher"
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index a06a2dd..d360613 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -93,6 +93,7 @@
             android:name="com.android.quickstep.interaction.GestureSandboxActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
+            android:taskAffinity="${packageName}.launcher"
             android:screenOrientation="portrait">
             <intent-filter>
                 <action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
diff --git a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
index ef272ed..686189e 100644
--- a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
+++ b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
@@ -28,4 +28,9 @@
         android:clipToPadding="false"
         android:outlineProvider="none"
         android:theme="@style/HomeScreenElementTheme" />
+
+    <include
+        android:id="@+id/overview_actions_view"
+        layout="@layout/overview_actions_holder" />
+
 </com.android.quickstep.fallback.RecentsRootView>
diff --git a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml b/quickstep/recents_ui_overrides/res/layout/overview_panel.xml
index a572cad..eac0bfa 100644
--- a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml
+++ b/quickstep/recents_ui_overrides/res/layout/overview_panel.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
      Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,17 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.InsettableFrameLayout
+<com.android.quickstep.views.LauncherRecentsView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-    <com.android.quickstep.views.LauncherRecentsView
-        android:id="@+id/overview_panel_recents"
-        android:theme="@style/HomeScreenElementTheme"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:accessibilityPaneTitle="@string/accessibility_recent_apps"
-        android:visibility="invisible" />
-</com.android.launcher3.InsettableFrameLayout>
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:accessibilityPaneTitle="@string/accessibility_recent_apps"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:theme="@style/HomeScreenElementTheme"
+    android:visibility="invisible" />
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 fe99037..c93cad6 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -35,25 +35,27 @@
 
         <TextView
             style="@style/TextHeadline"
+            android:id="@+id/hotseat_edu_heading"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="18dp"
             android:paddingLeft="@dimen/bottom_sheet_edu_padding"
             android:paddingRight="@dimen/bottom_sheet_edu_padding"
-            android:text="@string/hotseat_migrate_title"
+            android:text="@string/hotseat_edu_title_migrate"
             android:textAlignment="center"
             android:textColor="@android:color/white"
             android:textSize="20sp" />
 
         <TextView
             android:layout_width="match_parent"
+            android:id="@+id/hotseat_edu_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="18dp"
             android:layout_marginBottom="18dp"
             android:fontFamily="roboto-medium"
             android:paddingLeft="@dimen/bottom_sheet_edu_padding"
             android:paddingRight="@dimen/bottom_sheet_edu_padding"
-            android:text="@string/hotseat_migrate_message"
+            android:text="@string/hotseat_edu_message_migrate"
             android:textAlignment="center"
             android:textColor="@android:color/white"
             android:textSize="16sp" />
@@ -83,7 +85,7 @@
                     android:layout_height="wrap_content"
                     android:layout_gravity="end"
                     android:background="?android:attr/selectableItemBackground"
-                    android:text="@string/hotseat_migrate_accept"
+                    android:text="@string/hotseat_edu_accept"
                     android:textAlignment="textEnd"
                     android:textColor="@android:color/white" />
 
@@ -91,7 +93,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:id="@+id/no_thanks"
-                    android:text="@string/hotseat_migrate_dismiss"
+                    android:text="@string/hotseat_edu_dismiss"
                     android:layout_gravity="start"
                     android:background="?android:attr/selectableItemBackground"
                     android:textColor="@android:color/white" />
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index de97d08..61c576e 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -23,10 +23,4 @@
 
     <!-- Minimum distance to swipe to trigger accessibility gesture -->
     <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
-
-    <!-- Swipe up to home related -->
-    <dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
-    <dimen name="swipe_up_y_overshoot">10dp</dimen>
-    <dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
-
 </resources>
\ No newline at end of file
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 e7290a4..a07cd1d 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
@@ -27,11 +27,15 @@
 
 import androidx.core.app.NotificationCompat;
 
+import com.android.launcher3.CellLayout;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.WorkspaceLayoutManager;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.Themes;
@@ -61,16 +65,25 @@
         mNotification = createNotification();
     }
 
-    void migrate() {
+    boolean migrate() {
+        Workspace workspace = mLauncher.getWorkspace();
+        CellLayout firstScreen = workspace.getScreenWithId(WorkspaceLayoutManager.FIRST_SCREEN_ID);
+        int toPage = Workspace.FIRST_SCREEN_ID;
+        int toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
+        if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
+            toPage = workspace.getScreenIdForPageIndex(workspace.getPageCount());
+            toRow = 0;
+        } else if (!firstScreen.makeSpaceForHotseatMigration(true)) {
+            return false;
+        }
         ViewGroup hotseatVG = mLauncher.getHotseat().getShortcutsAndWidgets();
-        int workspacePageCount = mLauncher.getWorkspace().getPageCount();
         for (int i = 0; i < hotseatVG.getChildCount(); i++) {
             View child = hotseatVG.getChildAt(i);
             ItemInfo tag = (ItemInfo) child.getTag();
             mLauncher.getModelWriter().moveItemInDatabase(tag,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP, workspacePageCount, tag.screenId,
-                    0);
+                    LauncherSettings.Favorites.CONTAINER_DESKTOP, toPage, tag.screenId, toRow);
         }
+        return true;
     }
 
     void removeNotification() {
@@ -93,7 +106,7 @@
 
     private void createNotificationChannel() {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
-        CharSequence name = mLauncher.getString(R.string.hotseat_migrate_title);
+        CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
         int importance = NotificationManager.IMPORTANCE_LOW;
         NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name,
                 importance);
@@ -104,8 +117,8 @@
         Intent intent = new Intent(mLauncher.getApplicationContext(), mLauncher.getClass());
         intent = new NotificationHandler().addToIntent(intent);
 
-        CharSequence name = mLauncher.getString(R.string.hotseat_migrate_prompt_title);
-        String description = mLauncher.getString(R.string.hotseat_migrate_prompt_content);
+        CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title);
+        String description = mLauncher.getString(R.string.hotseat_edu_prompt_content);
         NotificationCompat.Builder builder = new NotificationCompat.Builder(mLauncher,
                 NOTIFICATION_CHANNEL_ID)
                 .setContentTitle(name)
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 8926246..538b7f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -26,6 +26,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.launcher3.CellLayout;
@@ -34,7 +35,9 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.WorkspaceLayoutManager;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -49,12 +52,24 @@
 public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
 
     private static final int DEFAULT_CLOSE_DURATION = 200;
+    protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
 
-    public static boolean shown = false;
+    // We don't migrate if user has more than SAME_PAGE_MAX_ROWS rows of item in their screen
+    private static final int SAME_PAGE_MAX_ROWS = 2;
+
+    private static final int MIGRATE_SAME_PAGE = 0;
+    private static final int MIGRATE_NEW_PAGE = 1;
+    private static final int MIGRATE_NO_MIGRATE = 2;
+
 
     private final Rect mInsets = new Rect();
     private View mHotseatWrapper;
     private CellLayout mSampleHotseat;
+    private TextView mEduHeading;
+    private TextView mEduContent;
+    private Button mDismissBtn;
+
+    private int mMigrationMode = MIGRATE_SAME_PAGE;
 
     public void setHotseatEduController(HotseatEduController hotseatEduController) {
         mHotseatEduController = hotseatEduController;
@@ -78,6 +93,8 @@
         super.onFinishInflate();
         mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
         mSampleHotseat = findViewById(R.id.sample_prediction);
+        mEduHeading = findViewById(R.id.hotseat_edu_heading);
+        mEduContent = findViewById(R.id.hotseat_edu_content);
 
         DeviceProfile grid = mLauncher.getDeviceProfile();
         Rect padding = grid.getHotseatLayoutPadding();
@@ -87,24 +104,27 @@
         mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
 
         Button turnOnBtn = findViewById(R.id.turn_predictions_on);
-        turnOnBtn.setOnClickListener(this::onMigrate);
+        turnOnBtn.setOnClickListener(this::onAccept);
 
-        Button learnMoreBtn = findViewById(R.id.no_thanks);
-        learnMoreBtn.setOnClickListener(this::onKeepDefault);
+        mDismissBtn = findViewById(R.id.no_thanks);
+        mDismissBtn.setOnClickListener(this::onDismiss);
 
     }
 
-    private void onMigrate(View v) {
-        if (mHotseatEduController == null) return;
+    private void onAccept(View v) {
+        if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) {
+            onDismiss(v);
+            return;
+        }
         handleClose(true);
-        mHotseatEduController.migrate();
         mHotseatEduController.finishOnboarding();
         logUserAction(true);
-        Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
+        int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE
+                ? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
+        Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
     }
 
-    private void onKeepDefault(View v) {
-        if (mHotseatEduController == null) return;
+    private void onDismiss(View v) {
         Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
         mHotseatEduController.finishOnboarding();
         logUserAction(false);
@@ -148,6 +168,8 @@
         target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
         target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
                 : HYBRID_HOTSEAT_CANCELED;
+        // encoding migration type on pageIndex
+        target.pageIndex = mMigrationMode;
         LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
         UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
     }
@@ -161,6 +183,7 @@
         LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
         UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
     }
+
     private void animateOpen() {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
             return;
@@ -183,17 +206,12 @@
         handleClose(false);
     }
 
-    /**
-     * Opens User education dialog with a list of suggested apps
-     */
-    public void show(List<WorkspaceItemInfo> predictions) {
-        if (getParent() != null
-                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
-            return;
-        }
-        attachToContainer();
-        logOnBoardingSeen();
-        animateOpen();
+    @Override
+    protected int getScrimColor(Context context) {
+        return FINAL_SCRIM_BG_COLOR;
+    }
+
+    private void populatePreview(List<WorkspaceItemInfo> predictions) {
         for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
             WorkspaceItemInfo info = predictions.get(i);
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
@@ -204,6 +222,43 @@
         }
     }
 
+    @Override
+    protected void attachToContainer() {
+        super.attachToContainer();
+        if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
+            mEduContent.setText(R.string.hotseat_edu_message_migrate_alt);
+            mMigrationMode = MIGRATE_NEW_PAGE;
+            return;
+        }
+        CellLayout page = mLauncher.getWorkspace().getScreenWithId(
+                WorkspaceLayoutManager.FIRST_SCREEN_ID);
+
+        int maxItemsOnPage = SAME_PAGE_MAX_ROWS * mLauncher.getDeviceProfile().inv.numColumns
+                + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
+        if (page.getShortcutsAndWidgets().getChildCount() > maxItemsOnPage
+                || !page.makeSpaceForHotseatMigration(false)) {
+            mMigrationMode = MIGRATE_NO_MIGRATE;
+            mEduContent.setText(R.string.hotseat_edu_message_no_migrate);
+            mEduHeading.setText(R.string.hotseat_edu_title_no_migrate);
+            mDismissBtn.setVisibility(GONE);
+        }
+    }
+
+    /**
+     * Opens User education dialog with a list of suggested apps
+     */
+    public void show(List<WorkspaceItemInfo> predictions) {
+        if (getParent() != null
+                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
+                || mHotseatEduController == null) {
+            return;
+        }
+        attachToContainer();
+        logOnBoardingSeen();
+        animateOpen();
+        populatePreview(predictions);
+    }
+
     /**
      * Factory method for HotseatPredictionUserEdu dialog
      */
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 f50bb3e..a17476e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.model.PagedViewOrientedState;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -146,8 +147,8 @@
         VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
     }
 
-    public Consumer<MotionEvent> getRecentsViewDispatcher() {
-        return mRecentsView != null ? mRecentsView.getEventDispatcher() : null;
+    public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode navBarRotationMode) {
+        return mRecentsView != null ? mRecentsView.getEventDispatcher(navBarRotationMode) : null;
     }
 
     @UiThread
@@ -447,7 +448,7 @@
             RotationHelper.mapInverseRectFromNormalOrientation(startRect,
                 mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
         }
-        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext.getResources());
+        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
         if (isFloatingIconView) {
             FloatingIconView fiv = (FloatingIconView) floatingView;
             anim.addAnimatorListener(fiv);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index bd9f330..893868b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -176,7 +176,8 @@
         // Proxy events to recents view
         if (mPassedWindowMoveSlop && mInteractionHandler != null
                 && !mRecentsViewDispatcher.hasConsumer()) {
-            mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher());
+            mRecentsViewDispatcher.setConsumer(mInteractionHandler
+                .getRecentsViewDispatcher(mNavBarPosition.getRotationMode()));
         }
         int edgeFlags = ev.getEdgeFlags();
         ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
index 682c92c..dde7605 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -17,7 +17,6 @@
 
 import android.animation.Animator;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.RectF;
 
@@ -106,7 +105,7 @@
     private float mMinVisChange;
     private float mYOvershoot;
 
-    public RectFSpringAnim(RectF startRect, RectF targetRect, Resources resources) {
+    public RectFSpringAnim(RectF startRect, RectF targetRect, Context context) {
         mStartRect = startRect;
         mTargetRect = targetRect;
         mCurrentCenterX = mStartRect.centerX();
@@ -114,8 +113,9 @@
         mTrackingBottomY = startRect.bottom < targetRect.bottom;
         mCurrentY = mTrackingBottomY ? mStartRect.bottom : mStartRect.top;
 
-        mMinVisChange = resources.getDimensionPixelSize(R.dimen.swipe_up_fling_min_visible_change);
-        mYOvershoot = resources.getDimensionPixelSize(R.dimen.swipe_up_y_overshoot);
+        ResourceProvider rp = DynamicResource.provider(context);
+        mMinVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change);
+        mYOvershoot = rp.getDimension(R.dimen.swipe_up_y_overshoot);
     }
 
     public void onTargetPositionChanged() {
@@ -160,7 +160,7 @@
         float endX = mTargetRect.centerX();
         float minXValue = Math.min(startX, endX);
         float maxXValue = Math.max(startX, endX);
-        mRectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, startX, endX,
+        mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
                 velocityPxPerMs.x * 1000, mMinVisChange, minXValue, maxXValue, 1f, onXEndListener);
 
         float startVelocityY = velocityPxPerMs.y * 1000;
@@ -170,13 +170,13 @@
         float endY = mTrackingBottomY ? mTargetRect.bottom : mTargetRect.top;
         float minYValue = Math.min(startY, endY - mYOvershoot);
         float maxYValue = Math.max(startY, endY);
-        mRectYAnim = new FlingSpringAnim(this, RECT_Y, startY, endY, startVelocityY,
+        mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY, startVelocityY,
                 mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener);
 
         float minVisibleChange = Math.abs(1f / mStartRect.height());
         ResourceProvider rp = DynamicResource.provider(context);
-        float damping = rp.getFloat(R.dimen.swipe_up_rect_damping_ratio);
-        float stiffness = rp.getFloat(R.dimen.swipe_up_rect_stiffness);
+        float damping = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio);
+        float stiffness = rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
 
         mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
                 .setSpring(new SpringForce(1f)
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 c201455..9bc95d7 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
@@ -29,6 +29,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 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_OVERVIEW_ACTIONS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
@@ -66,6 +67,7 @@
 import android.util.FloatProperty;
 import android.util.Property;
 import android.util.SparseBooleanArray;
+import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -84,7 +86,9 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
+import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
@@ -96,6 +100,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -111,6 +116,7 @@
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
+import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskUtils;
@@ -204,7 +210,7 @@
     private boolean mDwbToastShown;
     protected boolean mDisallowScrollToClearAll;
     private boolean mOverlayEnabled;
-    private boolean mFreezeViewVisibility;
+    protected boolean mFreezeViewVisibility;
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -274,6 +280,9 @@
         }
     };
 
+    private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener =
+        isForcedRotation -> RecentsView.this.disableMultipleLayoutRotations(!isForcedRotation);
+
     private final PinnedStackAnimationListener mIPinnedStackAnimationListener =
             new PinnedStackAnimationListener();
 
@@ -320,6 +329,8 @@
 
     // Keeps track of the index where the first TaskView should be
     private int mTaskViewStartIndex = 0;
+    private View mActionsView;
+    private boolean mGestureRunning = false;
 
     private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
             (inMultiWindowMode) -> {
@@ -378,6 +389,11 @@
                 int rotation = RotationHelper.getRotationFromDegrees(i);
                 if (mPreviousRotation != rotation) {
                     animateRecentsRotationInPlace(rotation);
+                    if (rotation == 0) {
+                        showActionsView();
+                    } else {
+                        hideActionsView();
+                    }
                     mPreviousRotation = rotation;
                 }
             }
@@ -466,6 +482,9 @@
         mIPinnedStackAnimationListener.setActivity(mActivity);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
                 mIPinnedStackAnimationListener);
+        Launcher launcher = Launcher.getLauncher(getContext());
+        launcher.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener);
+        addActionsView();
     }
 
     @Override
@@ -480,6 +499,8 @@
         mIdp.removeOnChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
         mIPinnedStackAnimationListener.setActivity(null);
+        Launcher launcher = Launcher.getLauncher(getContext());
+        launcher.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener);
     }
 
     @Override
@@ -627,6 +648,7 @@
         if (getTaskViewCount() != requiredTaskCount) {
             if (indexOfChild(mClearAllButton) != -1) {
                 removeView(mClearAllButton);
+                hideActionsView();
             }
             for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
                 addView(mTaskViewPool.getView());
@@ -636,6 +658,7 @@
             }
             if (requiredTaskCount > 0) {
                 addView(mClearAllButton);
+                showActionsView();
             }
         }
 
@@ -675,6 +698,7 @@
         if (indexOfChild(mClearAllButton) != -1) {
             removeView(mClearAllButton);
         }
+        hideActionsView();
     }
 
     public int getTaskViewCount() {
@@ -922,6 +946,7 @@
         setEnableDrawingLiveTile(false);
         setRunningTaskHidden(true);
         setRunningTaskIconScaledDown(true);
+        mGestureRunning = true;
     }
 
     /**
@@ -991,6 +1016,7 @@
         }
         setRunningTaskHidden(false);
         animateUpRunningTaskIconScale();
+        mGestureRunning = false;
     }
 
     /**
@@ -1007,6 +1033,7 @@
             addView(taskView, mTaskViewStartIndex);
             if (wasEmpty) {
                 addView(mClearAllButton);
+                showActionsView();
             }
             // The temporary running task is only used for the duration between the start of the
             // gesture and the task list is loaded and applied
@@ -1332,6 +1359,7 @@
 
                     if (getTaskViewCount() == 0) {
                         removeView(mClearAllButton);
+                        hideActionsView();
                         startHome();
                     } else {
                         snapToPageImmediately(pageToSnapTo);
@@ -1476,15 +1504,17 @@
             }
         }
         mClearAllButton.setContentAlpha(mContentAlpha);
-
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
         mEmptyIcon.setAlpha(alphaInt);
-
         if (alpha > 0) {
             setVisibility(VISIBLE);
+            if (!mGestureRunning) {
+                showActionsView();
+            }
         } else if (!mFreezeViewVisibility) {
             setVisibility(GONE);
+            hideActionsView();
         }
     }
 
@@ -1498,6 +1528,11 @@
 
             if (!mFreezeViewVisibility) {
                 setVisibility(mContentAlpha > 0 ? VISIBLE : GONE);
+                if (mContentAlpha > 0) {
+                    showActionsView();
+                } else {
+                    hideActionsView();
+                }
             }
         }
     }
@@ -1946,8 +1981,13 @@
         return offsetX;
     }
 
-    public Consumer<MotionEvent> getEventDispatcher() {
-        int degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation);
+    public Consumer<MotionEvent> getEventDispatcher(RotationMode navBarRotationMode) {
+        float degreesRotated;
+        if (navBarRotationMode == RotationMode.NORMAL) {
+            degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation);
+        } else {
+            degreesRotated = -navBarRotationMode.surfaceRotation;
+        }
         if (degreesRotated == 0) {
             return super::onTouchEvent;
         }
@@ -2043,6 +2083,7 @@
         void onEmptyMessageUpdated(boolean isEmpty);
     }
 
+
     private static class PinnedStackAnimationListener<T extends BaseActivity> extends
             IPinnedStackAnimationListener.Stub {
         private T mActivity;
@@ -2058,4 +2099,34 @@
             mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
         }
     }
+
+    private void showActionsView() {
+        if (mActionsView != null && getTaskViewCount() > 0) {
+            mActionsView.setVisibility(VISIBLE);
+        }
+    }
+
+    private void hideActionsView() {
+        if (mActionsView != null) {
+            mActionsView.setVisibility(GONE);
+        }
+    }
+
+    private void addActionsView() {
+        if (mActionsView == null && ENABLE_OVERVIEW_ACTIONS.get()
+                && SysUINavigationMode.removeShelfFromOverview(mActivity)) {
+            mActionsView = ((ViewGroup) getParent()).findViewById(R.id.overview_actions_view);
+            if (mActionsView != null) {
+                Rect rect = new Rect();
+                getTaskSize(rect);
+                InsettableFrameLayout.LayoutParams layoutParams =
+                        new InsettableFrameLayout.LayoutParams(rect.width(),
+                                getResources().getDimensionPixelSize(
+                                        R.dimen.overview_actions_height));
+                layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+                mActionsView.setLayoutParams(layoutParams);
+                showActionsView();
+            }
+        }
+    }
 }
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 9150cc7..e09e01f 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
@@ -16,6 +16,20 @@
 
 package com.android.quickstep.views;
 
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.CENTER_VERTICAL;
+import static android.view.Gravity.END;
+import static android.view.Gravity.START;
+import static android.view.Gravity.TOP;
+import static android.widget.Toast.LENGTH_SHORT;
+
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+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 android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -39,14 +53,11 @@
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.states.RotationHelper;
@@ -76,20 +87,6 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.view.Gravity.END;
-import static android.view.Gravity.START;
-import static android.view.Gravity.TOP;
-import static android.widget.Toast.LENGTH_SHORT;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-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.quickstep.SysUINavigationMode.removeShelfFromOverview;
-
 /**
  * A task in the Recents view.
  */
@@ -173,8 +170,6 @@
     private final float mWindowCornerRadius;
     private final BaseDraggingActivity mActivity;
 
-    @Nullable private View mActionsView;
-
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
     private float mFocusTransitionProgress = 1;
@@ -193,8 +188,6 @@
     private float mFooterVerticalOffset = 0;
     private float mFooterAlpha = 1;
     private int mStackHeight;
-    private boolean mHideActionsView;
-    private PagedOrientationHandler mOrientationHandler;
 
     public TaskView(Context context) {
         this(context, null);
@@ -246,18 +239,6 @@
         TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams();
         thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(context);
         mSnapshotView.setLayoutParams(thumbnailParams);
-
-
-        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
-            mActionsView = mSnapshotView.getTaskOverlay().getActionsView();
-            if (mActionsView != null) {
-                TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT,
-                        getResources().getDimensionPixelSize(R.dimen.overview_actions_height),
-                        BOTTOM);
-                addView(mActionsView, params);
-                mActionsView.setAlpha(0);
-            }
-        }
     }
 
     public boolean isTaskOverlayModal() {
@@ -457,7 +438,6 @@
         int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
         LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
         int rotation = RotationHelper.getDegreesFromRotation(iconRotation);
-        mHideActionsView = true;
         switch (iconRotation) {
             case Surface.ROTATION_90:
                 iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
@@ -479,13 +459,11 @@
                 iconParams.gravity = TOP | CENTER_HORIZONTAL;
                 iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin =
                     iconParams.bottomMargin = 0;
-                mHideActionsView = false;
                 break;
         }
         mSnapshotView.setLayoutParams(snapshotParams);
         mIconView.setLayoutParams(iconParams);
         mIconView.setRotation(rotation);
-        updateActionsViewVisibility(!mHideActionsView);
     }
 
     private void setIconAndDimTransitionProgress(float progress, boolean invert) {
@@ -502,11 +480,6 @@
         mIconView.setScaleX(scale);
         mIconView.setScaleY(scale);
 
-
-        if (mActionsView != null && isRunningTask()) {
-            mActionsView.setAlpha(scale);
-        }
-
         mFooterVerticalOffset = 1.0f - scale;
         for (FooterWrapper footer : mFooters) {
             if (footer != null) {
@@ -597,31 +570,8 @@
             mMenuView.setScaleX(getScaleX());
             mMenuView.setScaleY(getScaleY());
         }
-
-        // This is not the proper implementation and will be replaced with a proper layout.
-        if (mActionsView != null) {
-            if (mFocusTransitionProgress == 1f) {
-                mActionsView.setAlpha(1 - curveInterpolation / MAX_PAGE_SCRIM_ALPHA);
-            }
-            maintainActionViewPosition(curveScaleForCurveInterpolation);
-        }
-
     }
 
-    private void maintainActionViewPosition(float curveScaleForCurveInterpolation) {
-        float inverseCurveScaleFactor = curveScaleForCurveInterpolation == 0 ? 0 :
-                (1f / curveScaleForCurveInterpolation);
-        mActionsView.setScaleX(inverseCurveScaleFactor);
-        mActionsView.setScaleY(inverseCurveScaleFactor);
-        mActionsView.setTranslationX(inverseCurveScaleFactor * (-getX()
-                + getRecentsView().getScrollX() + getRecentsView().scrollOffsetLeft()));
-        mActionsView.setTranslationY(
-                (1f - curveScaleForCurveInterpolation) * (mSnapshotView.getHeight()
-                        + mActionsView.getHeight()) / 2f
-                        + inverseCurveScaleFactor * (-getTranslationY()));
-    }
-
-
     /**
      * Sets the footer at the specific index and returns the previously set footer.
      */
@@ -903,7 +853,6 @@
         mFullscreenProgress = progress;
         boolean isFullscreen = mFullscreenProgress > 0;
         mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
-        updateActionsViewVisibility(progress < 1 && !mHideActionsView);
         setClipChildren(!isFullscreen);
         setClipToPadding(!isFullscreen);
 
@@ -937,12 +886,6 @@
         invalidateOutline();
     }
 
-    private void updateActionsViewVisibility(boolean isVisible) {
-        if (mActionsView != null) {
-            mActionsView.setVisibility(isVisible ? VISIBLE : GONE);
-        }
-    }
-
     public boolean isRunningTask() {
         if (getRecentsView() == null) {
             return false;
@@ -990,5 +933,4 @@
             mScale = scale;
         }
     }
-
 }
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 45a62ab..90d4245 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -66,27 +66,32 @@
     <!-- Content description for a close button. [CHAR LIMIT=NONE] -->
     <string  name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
 
+
     <!-- Hotseat migration notification title -->
-    <string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
+    <string translatable="false" name="hotseat_edu_prompt_title">Get app suggestions based on your routines</string>
     <!-- Hotseat migration notification content -->
-    <string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
-    <!-- Hotseat migration wizard title -->
-    <string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
-    <!-- Hotseat migration wizard message -->
-    <string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
+    <string translatable="false" name="hotseat_edu_prompt_content">Tap to set up</string>
+
+
+    <!-- Hotseat educational strings for users who don't qualify for migration -->
+    <string translatable="false" name="hotseat_edu_title_migrate">Suggested apps replace the bottom row of apps</string>
+    <string translatable="false" name="hotseat_edu_message_migrate">Your hotseat items will be moved up on the homescreen</string>
+    <string translatable="false" name="hotseat_edu_message_migrate_alt">Your hotseat items will be moved to the last page of your workspace</string>
+
+
+    <!-- Hotseat educational strings for users who don't qualify -->
+    <string translatable="false" name="hotseat_edu_title_no_migrate">Suggested apps will be found at the bottom row of your home screen</string>
+    <string translatable="false" name="hotseat_edu_message_no_migrate">Drag one or many apps off the bottom row of home screen to see app suggestions</string>
+
     <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
-    <string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
+    <string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved up.</string>
+    <string translatable="false" name="hotseat_items_migrated_alt">Bottom row of apps moved to last page.</string>
     <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
     <string translatable="false" name="hotseat_no_migration">Bottom row won\'t be replaced. Manually drag apps for predictions.</string>
     <!-- Button text to opt in for fully predicted hotseat -->
-    <string translatable="false" name="hotseat_migrate_accept">Turn On</string>
+    <string translatable="false" name="hotseat_edu_accept">Got it</string>
     <!-- Button text to dismiss opt in for fully predicted hotseat -->
-    <string translatable="false" name="hotseat_migrate_dismiss">No thanks</string>
-    <!-- Hotseat onboard notification title -->
-    <string translatable="false" name="hotseat_onboard_notification_title">Your hotseat just got smarter</string>
-    <!-- Hotseat onboard notification detail -->
-    <string translatable="false" name="hotseat_onboard_notification_detail">Tap here to set it up</string>
-
+    <string translatable="false" name="hotseat_edu_dismiss">No thanks</string>
 
 
     <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index fa0e840..c5d7c95 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -196,7 +196,10 @@
 
         if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
             // Overview is above all other launcher elements, including qsb, so move it to the top.
-            getOverviewPanelContainer().bringToFront();
+            getOverviewPanel().bringToFront();
+            if (getActionsView() != null) {
+                getActionsView().bringToFront();
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 174e49b..7481445 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -52,10 +52,12 @@
         implements StateHandler {
     protected final T mRecentsView;
     protected final Launcher mLauncher;
+    protected final View mActionsView;
 
     public BaseRecentsViewStateController(@NonNull Launcher launcher) {
         mLauncher = launcher;
         mRecentsView = launcher.getOverviewPanel();
+        mActionsView = launcher.getActionsView();
     }
 
     @Override
@@ -72,6 +74,10 @@
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
         OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
         SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
+        if (mActionsView != null) {
+            mActionsView.setTranslationX(translationX);
+            mActionsView.setAlpha(state.overviewUi ? 1f : 0);
+        }
     }
 
     @Override
@@ -118,6 +124,11 @@
         OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
         setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
                 builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
+        if (mActionsView != null) {
+            setter.setFloat(mActionsView, View.TRANSLATION_X, translationX, translateXInterpolator);
+            setter.setFloat(mActionsView, View.ALPHA, toState.overviewUi ? 1 : 0,
+                    builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 832baf5..92eb036 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -49,7 +49,7 @@
 class OrientationTouchTransformer {
 
     private static final String TAG = "OrientationTouchTransformer";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
     private static final int MAX_ORIENTATIONS = 4;
 
     private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 6c66897..196eb0f 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -44,8 +44,13 @@
             layout="@layout/hotseat" />
 
         <include
-            android:id="@+id/overview_panel_container"
-            layout="@layout/overview_panel"/>
+            android:id="@+id/overview_panel"
+            layout="@layout/overview_panel"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/overview_actions_view"
+            layout="@layout/overview_actions_holder" />
 
         <!-- Keep these behind the workspace so that they are not visible when
          we go into AllApps -->
diff --git a/res/layout/overview_actions_holder.xml b/res/layout/overview_actions_holder.xml
new file mode 100644
index 0000000..5946bf6
--- /dev/null
+++ b/res/layout/overview_actions_holder.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<Space
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="0dp" />
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 7fff711..2637f03 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -14,9 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:id="@+id/overview_panel_recents"
-      android:layout_width="0dp"
-      android:layout_height="0dp"
-      android:visibility="gone" />
\ No newline at end of file
+<Space
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 35e5e6d..df0f233 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -133,12 +133,21 @@
     <item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.75</item>
     <item name="horizontal_spring_stiffness" type="dimen" format="float">200</item>
 
-    <item name="swipe_up_rect_damping_ratio" type="dimen" format="float">0.75</item>
-    <item name="swipe_up_rect_stiffness" type="dimen" format="float">200</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>
+
+    <item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
+    <item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
+    <item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
 
     <item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
     <item name="staggered_stiffness" type="dimen" format="float">150</item>
 
+    <!-- Swipe up to home related -->
+    <dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
+    <dimen name="swipe_up_y_overshoot">10dp</dimen>
+    <dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
+
     <array name="dynamic_resources">
         <item>@dimen/all_apps_spring_damping_ratio</item>
         <item>@dimen/all_apps_spring_stiffness</item>
@@ -152,10 +161,17 @@
         <item>@dimen/horizontal_spring_damping_ratio</item>
         <item>@dimen/horizontal_spring_stiffness</item>
 
-        <item>@dimen/swipe_up_rect_damping_ratio</item>
-        <item>@dimen/swipe_up_rect_stiffness</item>
+        <item>@dimen/swipe_up_rect_scale_damping_ratio</item>
+        <item>@dimen/swipe_up_rect_scale_stiffness</item>
+
+        <item>@dimen/swipe_up_rect_xy_fling_friction</item>
+        <item>@dimen/swipe_up_rect_xy_damping_ratio</item>
+        <item>@dimen/swipe_up_rect_xy_stiffness</item>
 
         <item>@dimen/staggered_damping_ratio</item>
         <item>@dimen/staggered_stiffness</item>
+
+        <item>@dimen/swipe_up_fling_min_visible_change</item>
+        <item>@dimen/swipe_up_y_overshoot</item>
     </array>
 </resources>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8718820..e3eb387 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2783,6 +2783,26 @@
         return false;
     }
 
+    /**
+     * Finds solution to accept hotseat migration to cell layout. commits solution if commitConfig
+     */
+    public boolean makeSpaceForHotseatMigration(boolean commitConfig) {
+        if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) return false;
+        int[] cellPoint = new int[2];
+        int[] directionVector = new int[]{0, -1};
+        cellToPoint(0, mCountY, cellPoint);
+        ItemConfiguration configuration = new ItemConfiguration();
+        if (findReorderSolution(cellPoint[0], cellPoint[1], mCountX, 1, mCountX, 1,
+                directionVector, null, false, configuration).isSolution) {
+            if (commitConfig) {
+                copySolutionToTempState(configuration, null);
+                commitTempPlacement();
+            }
+            return true;
+        }
+        return false;
+    }
+
     public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
         return mOccupied.isRegionVacant(x, y, spanX, spanY);
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 33f5a95..7c85bb7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -80,7 +80,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
-import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import androidx.annotation.Nullable;
@@ -274,7 +273,7 @@
 
     // UI and state for the overview panel
     private View mOverviewPanel;
-    private FrameLayout mOverviewPanelContainer;
+    private View mActionsView;
 
     @Thunk
     boolean mWorkspaceLoading = true;
@@ -1145,8 +1144,8 @@
         mFocusHandler = mDragLayer.getFocusIndicatorHelper();
         mWorkspace = mDragLayer.findViewById(R.id.workspace);
         mWorkspace.initParentViews(mDragLayer);
-        mOverviewPanel = findViewById(R.id.overview_panel_recents);
-        mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
+        mOverviewPanel = findViewById(R.id.overview_panel);
+        mActionsView = findViewById(R.id.overview_actions_view);
         mHotseat = findViewById(R.id.hotseat);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1389,8 +1388,8 @@
         return (T) mOverviewPanel;
     }
 
-    public FrameLayout getOverviewPanelContainer() {
-        return mOverviewPanelContainer;
+    public View getActionsView() {
+        return mActionsView;
     }
 
     public DropTargetBar getDropTargetBar() {
@@ -2305,6 +2304,13 @@
                     item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
                     getModelWriter().updateItemInDatabase(item);
                 }
+                else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
+                        && appWidgetInfo.configure != null) {
+                    if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) {
+                        item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+                        getModelWriter().updateItemInDatabase(item);
+                    }
+                }
             }
 
             if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
@@ -2318,6 +2324,11 @@
                 item.minSpanX = appWidgetInfo.minSpanX;
                 item.minSpanY = appWidgetInfo.minSpanY;
                 view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+            } else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
+                    && appWidgetInfo != null) {
+                mAppWidgetHost.addPendingView(item.appWidgetId,
+                        new PendingAppWidgetHostView(this, item, mIconCache, false));
+                view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
             } else {
                 view = new PendingAppWidgetHostView(this, item, mIconCache, false);
             }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 4e29a95..9921f76 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.widget.DeferredAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
@@ -53,12 +54,14 @@
 
     private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
     private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
+    private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
 
     private final Context mContext;
     private int mFlags = FLAG_RESUMED;
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
+
     public LauncherAppWidgetHost(Context context) {
         this(context, null);
     }
@@ -73,7 +76,13 @@
     @Override
     protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+        final LauncherAppWidgetHostView view;
+        if (mPendingViews.get(appWidgetId) != null) {
+            view = mPendingViews.get(appWidgetId);
+            mPendingViews.remove(appWidgetId);
+        } else {
+            view = new LauncherAppWidgetHostView(context);
+        }
         mViews.put(appWidgetId, view);
         return view;
     }
@@ -189,6 +198,10 @@
         }
     }
 
+    void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
+        mPendingViews.put(appWidgetId, view);
+    }
+
     public AppWidgetHostView createView(Context context, int appWidgetId,
             LauncherAppWidgetProviderInfo appWidget) {
         if (appWidget.isCustomWidget()) {
@@ -238,8 +251,8 @@
 
     /**
      * Called on an appWidget is removed for a widgetId
-     * @param appWidgetId
-     * TODO: make this override when SDK is updated
+     *
+     * @param appWidgetId TODO: make this override when SDK is updated
      */
     public void onAppWidgetRemoved(int appWidgetId) {
         if (mAppWidgetRemovedCallback == null) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ef6bd3d..a6180a6 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.PagedViewOrientedState;
 import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.PortraitPagedViewHandler;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.PagedOrientationHandler;
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index eaf3b1c..06d0f1c 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -15,32 +15,40 @@
  */
 package com.android.launcher3.anim;
 
+import android.content.Context;
+
 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
 import androidx.dynamicanimation.animation.FlingAnimation;
 import androidx.dynamicanimation.animation.FloatPropertyCompat;
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
+import com.android.launcher3.R;
+import com.android.launcher3.util.DynamicResource;
+import com.android.systemui.plugins.ResourceProvider;
+
 /**
  * Given a property to animate and a target value and starting velocity, first apply friction to
  * the fling until we pass the target, then apply a spring force to pull towards the target.
  */
 public class FlingSpringAnim {
 
-    private static final float FLING_FRICTION = 1.5f;
-    private static final float SPRING_STIFFNESS = 200;
-    private static final float SPRING_DAMPING = 0.8f;
-
     private final FlingAnimation mFlingAnim;
     private SpringAnimation mSpringAnim;
 
     private float mTargetPosition;
 
-    public <K> FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition,
-            float targetPosition, float startVelocity, float minVisChange, float minValue,
-            float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) {
+    public <K> FlingSpringAnim(K object, Context context, FloatPropertyCompat<K> property,
+            float startPosition, float targetPosition, float startVelocity, float minVisChange,
+            float minValue, float maxValue, float springVelocityFactor,
+            OnAnimationEndListener onEndListener) {
+        ResourceProvider rp = DynamicResource.provider(context);
+        float damping = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio);
+        float stiffness = rp.getFloat(R.dimen.swipe_up_rect_xy_stiffness);
+        float friction = rp.getFloat(R.dimen.swipe_up_rect_xy_fling_friction);
+
         mFlingAnim = new FlingAnimation(object, property)
-                .setFriction(FLING_FRICTION)
+                .setFriction(friction)
                 // Have the spring pull towards the target if we've slowed down too much before
                 // reaching it.
                 .setMinimumVisibleChange(minVisChange)
@@ -54,8 +62,8 @@
                     .setStartValue(value)
                     .setStartVelocity(velocity * springVelocityFactor)
                     .setSpring(new SpringForce(mTargetPosition)
-                            .setStiffness(SPRING_STIFFNESS)
-                            .setDampingRatio(SPRING_DAMPING));
+                            .setStiffness(stiffness)
+                            .setDampingRatio(damping));
             mSpringAnim.addEndListener(onEndListener);
             mSpringAnim.animateToFinalPosition(mTargetPosition);
         }));
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 6762435..b689a0a 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -113,6 +113,10 @@
     public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
             "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
 
+    public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = new DeviceFlag(
+            "HOTSEAT_MIGRATE_NEW_PAGE", true,
+            "Migrates hotseat to a new workspace page instead of same page");
+
     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
             "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
 
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 049fda9..8dc2e61 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -96,6 +96,7 @@
 
         initFlags();
         loadPluginPrefs();
+        maybeAddSandboxCategory();
     }
 
     @Override
@@ -203,6 +204,31 @@
         });
     }
 
+    private void maybeAddSandboxCategory() {
+        Context context = getContext();
+        if (context == null) {
+            return;
+        }
+        Intent launchSandboxIntent =
+                new Intent("com.android.quickstep.action.GESTURE_SANDBOX")
+                        .setPackage(context.getPackageName())
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (launchSandboxIntent.resolveActivity(context.getPackageManager()) == null) {
+            return;
+        }
+        PreferenceCategory sandboxCategory = newCategory("Sandbox");
+        Preference launchSandboxPreference = new Preference(context);
+        launchSandboxPreference.setKey("launchSandbox");
+        launchSandboxPreference.setTitle("Launch Gesture Navigation Sandbox");
+        launchSandboxPreference.setSummary(
+                "This provides tutorials and a place to practice navigation gestures.");
+        launchSandboxPreference.setOnPreferenceClickListener(preference -> {
+            startActivity(launchSandboxIntent);
+            return true;
+        });
+        sandboxCategory.addPreference(launchSandboxPreference);
+    }
+
     private String toName(String action) {
         String str = action.replace("com.android.systemui.action.PLUGIN_", "")
                 .replace("com.android.launcher3.action.PLUGIN_", "");
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 3b134b0..43d54eb 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -20,6 +20,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.ContentResolver;
@@ -42,6 +43,9 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.UiThreadHelper;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Utility class to manage launcher rotation
  */
@@ -50,16 +54,38 @@
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
     public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
+    private final ContentResolver mContentResolver;
+
+    /**
+     * Listener to receive changes when {@link #FIXED_ROTATION_TRANSFORM_SETTING_NAME} flag changes.
+     */
+    public interface ForcedRotationChangedListener {
+        void onForcedRotationChanged(boolean isForcedRotation);
+    }
 
     public static boolean getAllowRotationDefaultValue() {
-        // If the device was scaled, used the original dimensions to determine if rotation
-        // is allowed of not.
+        // If the device's pixel density was scaled (usually via settings for A11y), use the
+        // original dimensions to determine if rotation is allowed of not.
         Resources res = Resources.getSystem();
         int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
                 * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
         return originalSmallestWidth >= 600;
     }
 
+
+    private final ContentObserver mContentObserver =
+        new ContentObserver(MAIN_EXECUTOR.getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                boolean forcedRotation = Utilities.isForcedRotation(mLauncher);
+                PagedView.sFlagForcedRotation = forcedRotation;
+                updateForcedRotation();
+                for (ForcedRotationChangedListener listener : mForcedRotationChangedListeners) {
+                    listener.onForcedRotationChanged(forcedRotation);
+                }
+            }
+        };
+
     public static final int REQUEST_NONE = 0;
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
@@ -69,6 +95,8 @@
 
     private boolean mIgnoreAutoRotateSettings;
     private boolean mAutoRotateEnabled;
+    private boolean mForcedRotation;
+    private List<ForcedRotationChangedListener> mForcedRotationChangedListeners = new ArrayList<>();
 
     /**
      * Rotation request made by
@@ -97,6 +125,7 @@
 
         // On large devices we do not handle auto-rotate differently.
         mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation);
+        updateForcedRotation();
         if (!mIgnoreAutoRotateSettings) {
             mPrefs = Utilities.getPrefs(mLauncher);
             mPrefs.registerOnSharedPreferenceChangeListener(this);
@@ -107,16 +136,24 @@
         }
 
         // TODO(b/150260456) Add this in home settings as well
-        final ContentResolver resolver = launcher.getContentResolver();
-        final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                PagedView.sFlagForcedRotation = Utilities.isForcedRotation(mLauncher);
-            }
-        };
-        resolver.registerContentObserver(Settings.Global.getUriFor(
-            FIXED_ROTATION_TRANSFORM_SETTING_NAME),
-            false, observer);
+        mContentResolver = launcher.getContentResolver();
+        mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+            FIXED_ROTATION_TRANSFORM_SETTING_NAME), false, mContentObserver);
+    }
+
+    private void updateForcedRotation() {
+        mForcedRotation = !getAllowRotationDefaultValue() && Utilities.isForcedRotation(mLauncher);
+    }
+
+    /**
+     * will not be called when first registering the listener.
+     */
+    public void addForcedRotationCallback(ForcedRotationChangedListener listener) {
+        mForcedRotationChangedListeners.add(listener);
+    }
+
+    public void removeForcedRotationCallback(ForcedRotationChangedListener listener) {
+        mForcedRotationChangedListeners.remove(listener);
     }
 
     public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) {
@@ -198,6 +235,10 @@
             if (mPrefs != null) {
                 mPrefs.unregisterOnSharedPreferenceChangeListener(this);
             }
+            if (mContentResolver != null) {
+                mContentResolver.unregisterContentObserver(mContentObserver);
+            }
+            mForcedRotationChangedListeners.clear();
         }
     }
 
@@ -207,7 +248,10 @@
         }
 
         final int activityFlags;
-        if (mStateHandlerRequest != REQUEST_NONE) {
+        if (mForcedRotation) {
+            // TODO(b/150214193) Properly address this
+            activityFlags = SCREEN_ORIENTATION_PORTRAIT;
+        } else if (mStateHandlerRequest != REQUEST_NONE) {
             activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
                     SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
         } else if (mCurrentTransitionRequest != REQUEST_NONE) {
@@ -277,7 +321,7 @@
      * Creates a matrix to transform the given motion event specified by degrees.
      * If {@param inverse} is {@code true}, the inverse of that matrix will be applied
      */
-    public static void transformEvent(int degrees, MotionEvent ev, boolean inverse) {
+    public static void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
         Matrix transform = new Matrix();
         transform.setRotate(degrees);
         if (inverse) {
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 6038873..895f8de 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -33,6 +33,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.RemoteViews;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.FastBitmapDrawable;
@@ -94,6 +95,15 @@
     }
 
     @Override
+    public void updateAppWidget(RemoteViews remoteViews) {
+        super.updateAppWidget(remoteViews);
+        WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext());
+        if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) {
+            reInflate();
+        }
+    }
+
+    @Override
     public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
             int maxHeight) {
         // No-op
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 8b08d77..f3c7822 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -49,6 +49,9 @@
  */
 public class WidgetManagerHelper {
 
+    //TODO: replace this with OPTION_APPWIDGET_RESTORE_COMPLETED b/63667276
+    public static final String WIDGET_OPTION_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
+
     final AppWidgetManager mAppWidgetManager;
     final Context mContext;
 
@@ -127,6 +130,14 @@
         return null;
     }
 
+    /**
+     * Returns if a AppWidgetProvider has marked a widget restored
+     */
+    public boolean isAppWidgetRestored(int appWidgetId) {
+        return !WidgetsModel.GO_DISABLE_WIDGETS && mAppWidgetManager.getAppWidgetOptions(
+                appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
+    }
+
     public static Map<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap(Context context) {
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             return Collections.emptyMap();
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index e93df96..c6192bc 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -35,9 +35,11 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.os.Debug;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
@@ -117,6 +119,14 @@
                                 // so let's just mark the fact that the leak has happened.
                                 if (sDetectedActivityLeak == null) {
                                     sDetectedActivityLeak = violation.toString();
+                                    try {
+                                        Debug.dumpHprofData(
+                                                getInstrumentation().getTargetContext()
+                                                        .getFilesDir().getPath()
+                                                        + "/ActivityLeakHeapDump.hprof");
+                                    } catch (IOException e) {
+                                        Log.e(TAG, "dumpHprofData failed", e);
+                                    }
                                 }
                             });
             StrictMode.setVmPolicy(builder.build());
@@ -126,18 +136,6 @@
     public static void checkDetectedLeaks() {
         if (sDetectedActivityLeak != null && !sActivityLeakReported) {
             sActivityLeakReported = true;
-
-            final UiDevice device = UiDevice.getInstance(getInstrumentation());
-            try {
-                device.executeShellCommand(
-                        "am dumpheap "
-                                + device.getLauncherPackageName()
-                                + " "
-                                + getInstrumentation().getTargetContext().getFilesDir().getPath()
-                                + "/ActivityLeakHeapDump.hprof");
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
         }
     }
 
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 21a654f..793af48 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.appwidget.AppWidgetHost;
@@ -33,6 +34,7 @@
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.os.Bundle;
+import android.widget.RemoteViews;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -41,6 +43,7 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -86,7 +89,8 @@
 
         // Clear all existing data
         LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
-        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+        LauncherSettings.Settings.call(mResolver,
+                LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
     }
 
     @After
@@ -172,6 +176,26 @@
         assertNotNull(AppWidgetManager.getInstance(mTargetContext)
                 .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
                         LauncherSettings.Favorites.APPWIDGET_ID))));
+
+        // send OPTION_APPWIDGET_RESTORE_COMPLETED
+        int appWidgetId = mCursor.getInt(
+                mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID));
+        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext);
+
+        Bundle b = new Bundle();
+        b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true);
+        RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready);
+        appWidgetManager.updateAppWidgetOptions(appWidgetId, b);
+        appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
+
+
+        // verify changes are reflected
+        waitForLauncherCondition("App widget options did not update",
+                l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
+                        WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
+        executeOnLauncher(l -> l.getAppWidgetHost().startListening());
+        verifyWidgetPresent(info);
+        assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT));
     }
 
     @Test
@@ -254,6 +278,7 @@
 
     /**
      * Creates a LauncherAppWidgetInfo corresponding to {@param info}
+     *
      * @param bindWidget if true the info is bound and a valid widgetId is assigned to
      *                   the LauncherAppWidgetInfo
      */
@@ -306,7 +331,7 @@
                     .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
             return packages;
         });
-        while(true) {
+        while (true) {
             try {
                 mTargetContext.getPackageManager().getPackageInfo(
                         pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
@@ -316,7 +341,7 @@
                 }
             }
             pkg = invalidPackage + count;
-            count ++;
+            count++;
         }
         LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10,
                 new ComponentName(pkg, "com.test.widgetprovider"));
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 17858a0..9b12a62 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -145,7 +145,7 @@
 
     private static final String WORKSPACE_RES_ID = "workspace";
     private static final String APPS_RES_ID = "apps_view";
-    private static final String OVERVIEW_RES_ID = "overview_panel_recents";
+    private static final String OVERVIEW_RES_ID = "overview_panel";
     private static final String WIDGETS_RES_ID = "widgets_list_view";
     private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
     public static final int WAIT_TIME_MS = 10000;