Merge "Consider folder diff differently" into ub-launcher3-rvc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index c6e8c20b..7e8e51e 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -53,6 +53,7 @@
     SearchResultContainer search_result_container = 7;
     ShortcutsContainer shortcuts_container = 8;
     SettingsContainer settings_container = 9;
+    PredictedHotseatContainer predicted_hotseat_container = 10;
   }
 }
 
@@ -151,6 +152,14 @@
   optional int32 index = 1;
 }
 
+// Represents hotseat container with prediction feature enabled.
+message PredictedHotseatContainer {
+  optional int32 index = 1;
+
+  // No of hotseat positions filled with predicted items.
+  optional int32 cardinality = 2;
+}
+
 message FolderContainer {
   optional int32 page_index = 1 [default = -1];
   optional int32 grid_x = 2 [default = -1];
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 f1ce72e..49f3eb8 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
@@ -47,12 +47,12 @@
     public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen";
     public static final String HOTSEAT_EDU_ACTION =
             "com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
-    private static final String SETTINGS_ACTION =
+    public static final String SETTINGS_ACTION =
             "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
 
     private final Launcher mLauncher;
     private final Hotseat mHotseat;
-    private final HotseatRestoreHelper mRestoreHelper;
+    private HotseatRestoreHelper mRestoreHelper;
     private List<WorkspaceItemInfo> mPredictedApps;
     private HotseatEduDialog mActiveDialog;
 
@@ -71,14 +71,17 @@
      * Checks what type of migration should be used and migrates hotseat
      */
     void migrate() {
-        mRestoreHelper.createBackup();
+        if (mRestoreHelper != null) {
+            mRestoreHelper.createBackup();
+        }
         if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
             migrateToFolder();
         } else {
             migrateHotseatWhole();
         }
-        Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled, R.string.hotseat_turn_off,
-                null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+        Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
+                R.string.hotseat_prediction_settings, null,
+                () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
     }
 
     /**
@@ -223,15 +226,15 @@
 
     void finishOnboarding() {
         mOnOnboardingComplete.run();
-        destroy();
         mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
     }
 
     void showDimissTip() {
         if (mHotseat.getShortcutsAndWidgets().getChildCount()
                 < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
-            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled, R.string.hotseat_turn_off,
-                    null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+                    R.string.hotseat_prediction_settings, null,
+                    () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
         } else {
             new ArrowTipView(mLauncher).show(
                     mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
@@ -242,12 +245,6 @@
         mPredictedApps = predictedApps;
     }
 
-    void destroy() {
-        if (mActiveDialog != null) {
-            mActiveDialog.setHotseatEduController(null);
-        }
-    }
-
     void showEdu() {
         int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
         CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID);
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 99cb3b3..bbc128f 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
@@ -29,6 +29,7 @@
 import android.widget.Button;
 import android.widget.TextView;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
@@ -245,6 +246,7 @@
                 || mHotseatEduController == null) {
             return;
         }
+        AbstractFloatingView.closeAllOpenViews(mLauncher);
         attachToContainer();
         logOnBoardingSeen();
         animateOpen();
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 bd4d713..27423a5 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
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.SETTINGS_ACTION;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -27,6 +28,7 @@
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -64,6 +66,8 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.launcher3.views.Snackbar;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -107,8 +111,6 @@
     private boolean mIsCacheEmpty;
     private boolean mIsDestroyed = false;
 
-    private HotseatEduController mHotseatEduController;
-
 
     private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
 
@@ -146,11 +148,48 @@
     }
 
     /**
-     * Transitions to NORMAL workspace mode and shows edu
+     * Shows appropriate hotseat education based on prediction enabled and migration states.
      */
     public void showEdu() {
-        if (mHotseatEduController == null) return;
-        mHotseatEduController.showEdu();
+        if (mComponentKeyMappers.isEmpty()) {
+            // launcher has empty predictions set
+            Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
+                    R.string.hotseat_prediction_settings, null,
+                    () -> mLauncher.startActivity(
+                            new Intent(SETTINGS_ACTION)));
+        } else if (isEduSeen()) {
+            // user has already went through education
+            new ArrowTipView(mLauncher).show(
+                    mLauncher.getString(R.string.hotsaet_tip_prediction_enabled),
+                    mHotseat.getTop());
+        } else {
+            HotseatEduController eduController = new HotseatEduController(mLauncher, mRestoreHelper,
+                    this::createPredictor);
+            eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
+            eduController.showEdu();
+        }
+    }
+
+    /**
+     * Shows educational tip for hotseat if user does not go through Tips app.
+     */
+    public void showDiscoveryTip() {
+        if (getPredictedIcons().size() == mHotSeatItemsCount) {
+            new ArrowTipView(mLauncher).show(
+                    mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+        } else {
+            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+                    R.string.hotseat_prediction_settings, null,
+                    () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+        }
+    }
+
+    /**
+     * Returns if hotseat client has predictions
+     * @return
+     */
+    public boolean hasPredictions() {
+        return !mComponentKeyMappers.isEmpty();
     }
 
     @Override
@@ -250,10 +289,6 @@
         if (mAppPredictor != null) {
             mAppPredictor.destroy();
         }
-        if (mHotseatEduController != null) {
-            mHotseatEduController.destroy();
-            mHotseatEduController = null;
-        }
     }
 
     /**
@@ -299,10 +334,6 @@
             mAppPredictor.requestPredictionUpdate();
         });
         setPauseUIUpdate(false);
-        if (!isEduSeen()) {
-            mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper,
-                    this::createPredictor);
-        }
     }
 
     /**
@@ -350,9 +381,6 @@
         if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
         updateDependencies();
         fillGapsWithPrediction();
-        if (!isEduSeen() && mHotseatEduController != null) {
-            mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
-        }
         cachePredictionComponentKeysIfNecessary(componentKeys);
     }
 
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 494a98d..f7d0cd5 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
@@ -88,7 +88,6 @@
      */
     public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
             SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
-    private HotseatPredictionController mHotseatPredictionController;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -169,13 +168,6 @@
     }
 
     /**
-     * Returns Prediction controller for hybrid hotseat
-     */
-    public HotseatPredictionController getHotseatPredictionController() {
-        return mHotseatPredictionController;
-    }
-
-    /**
      * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
      */
     private void onStateOrResumeChanging(boolean inTransition) {
@@ -195,7 +187,8 @@
     @Override
     public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
         super.bindPredictedItems(appInfos, ranks);
-        if (mHotseatPredictionController != null) {
+        if (mHotseatPredictionController != null
+                && !mHotseatPredictionController.hasPredictions()) {
             mHotseatPredictionController.showCachedItems(appInfos, ranks);
         }
     }
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
index 79dc3e2..ba8656d 100644
--- 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
@@ -156,6 +156,7 @@
         if (toState == NORMAL && fromState == OVERVIEW) {
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+            config.setInterpolator(ANIM_ALL_APPS_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);
@@ -210,6 +211,7 @@
                 }
             }
             config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
             Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index a28dabc..ebc83c6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -37,7 +37,7 @@
             case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
                 final float swipeHeight =
                         LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile,
-                                PagedOrientationHandler.HOME_ROTATED);
+                                PagedOrientationHandler.PORTRAIT);
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
                 return response;
             }
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 97cd11b..a6a08cb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static android.view.Surface.ROTATION_0;
+
 import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 
 import android.annotation.SuppressLint;
@@ -24,6 +26,7 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Build;
+import android.view.View;
 import android.widget.Toast;
 
 import androidx.annotation.RequiresApi;
@@ -31,9 +34,12 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
@@ -58,6 +64,19 @@
                 shortcuts.add(shortcut);
             }
         }
+        RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
+        boolean canLauncherRotate = orientedState.canLauncherRotate();
+        boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
+
+        // Add overview actions to the menu when in in-place rotate landscape mode.
+        if (!canLauncherRotate && isInLandscape) {
+            for (TaskShortcutFactory actionMenuOption : ACTION_MENU_OPTIONS) {
+                SystemShortcut shortcut = actionMenuOption.getShortcut(activity, taskView);
+                if (shortcut != null) {
+                    shortcuts.add(shortcut);
+                }
+            }
+        }
         return shortcuts;
     }
 
@@ -85,6 +104,11 @@
             TaskShortcutFactory.WELLBEING
     };
 
+    private static final TaskShortcutFactory[] ACTION_MENU_OPTIONS = new TaskShortcutFactory[]{
+        TaskShortcutFactory.SCREENSHOT,
+        TaskShortcutFactory.MODAL
+    };
+
     /**
      * Overlay on each task handling Overview Action Buttons.
      */
@@ -94,10 +118,14 @@
         protected final TaskThumbnailView mThumbnailView;
 
         private T mActionsView;
+        private ImageActionsApi mImageApi;
+        private boolean mIsAllowedByPolicy;
 
         protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
             mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
             mThumbnailView = taskThumbnailView;
+            mImageApi = new ImageActionsApi(
+                mApplicationContext, mThumbnailView::getThumbnail);
         }
 
         protected T getActionsView() {
@@ -112,15 +140,12 @@
          * Called when the current task is interactive for the user
          */
         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() {
                     if (isAllowedByPolicy) {
-                        imageApi.startShareActivity();
+                        mImageApi.startShareActivity();
                     } else {
                         showBlockedByPolicyMessage();
                     }
@@ -129,16 +154,23 @@
                 @SuppressLint("NewApi")
                 @Override
                 public void onScreenshot() {
-                    if (isAllowedByPolicy) {
-                        imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
-                                getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
-                    } else {
-                        showBlockedByPolicyMessage();
-                    }
+                    saveScreenshot(task);
                 }
             });
         }
 
+        /**
+         * Called to save screenshot of the task thumbnail.
+         */
+        @SuppressLint("NewApi")
+        private void saveScreenshot(Task task) {
+            if (mThumbnailView.isRealSnapshot()) {
+                mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+                        getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
+            } else {
+                showBlockedByPolicyMessage();
+            }
+        }
 
         /**
          * Called when the overlay is no longer used.
@@ -147,6 +179,20 @@
         }
 
         /**
+         * Gets the modal state system shortcut.
+         */
+        public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo) {
+            return null;
+        }
+
+        /**
+         * Gets the system shortcut for the screenshot that will be added to the task menu.
+         */
+        public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
+                ItemInfo iteminfo) {
+            return new ScreenshotSystemShortcut(activity, iteminfo);
+        }
+        /**
          * Gets the task snapshot as it is displayed on the screen.
          *
          * @return the bounds of the snapshot in screen coordinates.
@@ -175,6 +221,22 @@
                     R.string.blocked_by_policy,
                     Toast.LENGTH_LONG).show();
         }
+
+        private class ScreenshotSystemShortcut extends SystemShortcut {
+
+            private final BaseDraggingActivity mActivity;
+
+            ScreenshotSystemShortcut(BaseDraggingActivity activity, ItemInfo itemInfo) {
+                super(R.drawable.ic_screenshot, R.string.action_screenshot, activity, itemInfo);
+                mActivity = activity;
+            }
+
+            @Override
+            public void onClick(View view) {
+                saveScreenshot(mThumbnailView.getTaskView().getTask());
+                dismissTaskMenuView(mActivity);
+            }
+        }
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
index 3623e67..ea1795c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -18,6 +18,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
@@ -332,4 +334,19 @@
 
     TaskShortcutFactory WELLBEING = (activity, view) ->
             WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
+
+    TaskShortcutFactory SCREENSHOT = (activity, tv) -> {
+        if (ENABLE_OVERVIEW_ACTIONS.get()) {
+            return tv.getThumbnail().getTaskOverlay()
+                .getScreenshotShortcut(activity, dummyInfo(tv));
+        }
+        return null;
+    };
+
+    TaskShortcutFactory MODAL = (activity, tv) -> {
+        if (ENABLE_OVERVIEW_ACTIONS.get() && ENABLE_OVERVIEW_SELECTIONS.get()) {
+            return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(dummyInfo(tv));
+        }
+        return null;
+    };
 }
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 98784ef..05f4e91 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
@@ -607,6 +607,17 @@
         }
     }
 
+    /**
+     * Whether the Clear All button is hidden or fully visible. Used to determine if center
+     * displayed page is a task or the Clear All button.
+     *
+     * @return True = Clear All button not fully visible, center page is a task. False = Clear All
+     * button fully visible, center page is Clear All button.
+     */
+    public boolean isClearAllHidden() {
+        return mClearAllButton.getAlpha() != 1f;
+    }
+
     @Override
     protected void onPageBeginTransition() {
         super.onPageBeginTransition();
@@ -616,7 +627,7 @@
     @Override
     protected void onPageEndTransition() {
         super.onPageEndTransition();
-        if (getScrollX() == getScrollForPage(getPageNearestToCenterOfScreen())) {
+        if (isClearAllHidden()) {
             LayoutUtils.setViewEnabled(mActionsView, true);
         }
         if (getNextPage() > 0) {
@@ -2182,6 +2193,11 @@
         if (getCurrentPageTaskView() != null) {
             getCurrentPageTaskView().setModalness(modalness);
         }
+        // Only show actions view when it's modal for in-place landscape mode.
+        boolean inPlaceLandscape = !mOrientationState.canLauncherRotate()
+                && mOrientationState.getTouchRotation() != ROTATION_0;
+        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
+        LayoutUtils.setViewEnabled(mActionsView, true);
     }
 
     @Nullable
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 a371dd2..26fb563 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
@@ -448,6 +448,16 @@
     }
 
     /**
+     * Returns whether the snapshot is real.
+     */
+    public boolean isRealSnapshot() {
+        if (mThumbnailData == null) {
+            return false;
+        }
+        return mThumbnailData.isRealSnapshot;
+    }
+
+    /**
      * Utility class to position the thumbnail in the TaskView
      */
     public static class PreviewPositionHelper {
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 cadf6c4..e25c85b 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
@@ -425,13 +425,16 @@
     }
 
     private boolean showTaskMenu(int action) {
-        getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
-        mMenuView = TaskMenuView.showForTask(this);
-        mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto());
-        UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
-                LauncherLogProto.ItemType.TASK_ICON);
-        if (mMenuView != null) {
-            mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
+        if (!getRecentsView().isClearAllHidden()) {
+            getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
+        } else {
+            mMenuView = TaskMenuView.showForTask(this);
+            mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto());
+            UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
+                    LauncherLogProto.ItemType.TASK_ICON);
+            if (mMenuView != null) {
+                mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
+            }
         }
         return mMenuView != null;
     }
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a27c127..1b82826 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -76,8 +76,8 @@
     <!-- Button text to dismiss opt in for fully predicted hotseat -->
     <string name="hotseat_edu_dismiss">No thanks</string>
 
-    <!-- action shown to turn off predictions after onboarding -->
-    <string name="hotseat_turn_off">Settings</string>
+    <!-- action shown to toggle predictions after onboarding -->
+    <string name="hotseat_prediction_settings">Settings</string>
 
     <!-- tip shown if user has no items in hotseat to migrate -->
     <string name="hotseat_auto_enrolled">Most-used apps appear here, and change based on routines</string>
@@ -86,7 +86,9 @@
     <!-- tip shown if user declines migration and has some open spots for prediction -->
     <string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
     <!-- tip shown when user migrates and predictions are enabled in hotseat -->
-    <string name="hotsaet_tip_prediction_enabled">App suggestions Enabled</string>
+    <string name="hotsaet_tip_prediction_enabled">App suggestions enabled</string>
+    <!-- tip shown when hotseat edu is requested while predicions are disabled -->
+    <string name="hotsaet_tip_prediction_disabled">App suggestions are disabled</string>
 
     <!-- content description for hotseat items -->
     <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4874307..d2e0339 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -31,6 +31,7 @@
 import android.os.CancellationSignal;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -75,6 +76,7 @@
     private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
 
     private OverviewActionsView mActionsView;
+    protected HotseatPredictionController mHotseatPredictionController;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -305,6 +307,13 @@
         return mShelfPeekAnim;
     }
 
+    /**
+     * Returns Prediction controller for hybrid hotseat
+     */
+    public HotseatPredictionController getHotseatPredictionController() {
+        return mHotseatPredictionController;
+    }
+
     public void setHintUserWillBeActive() {
         addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 07aed52..79b38f2 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -16,6 +16,7 @@
 package com.android.quickstep;
 
 import static android.content.Intent.ACTION_USER_UNLOCKED;
+import static android.view.Surface.ROTATION_0;
 
 import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
 import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
@@ -48,15 +49,18 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.view.MotionEvent;
+import android.view.OrientationEventListener;
 
 import androidx.annotation.BinderThread;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DefaultDisplay;
 import com.android.launcher3.util.SecureSettingsObserver;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.util.NavBarPosition;
+import com.android.quickstep.util.RecentsOrientedState;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -112,9 +116,53 @@
             }
             enableMultipleRegions(false);
         }
+
+        @Override
+        public void onActivityRotation(int displayId) {
+            super.onActivityRotation(displayId);
+            // This always gets called before onDisplayInfoChanged() so we know how to process
+            // the rotation in that method. This is done to avoid having a race condition between
+            // the sensor readings and onDisplayInfoChanged() call
+            if (displayId != mDisplayId) {
+                return;
+            }
+
+            mPrioritizeDeviceRotation = true;
+            if (mInOverview) {
+                // reset, launcher must be rotating
+                mExitOverviewRunnable.run();
+            }
+        }
+    };
+
+    private Runnable mExitOverviewRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mInOverview = false;
+            enableMultipleRegions(false);
+        }
     };
 
     private OrientationTouchTransformer mOrientationTouchTransformer;
+    /**
+     * Used to listen for when the device rotates into the orientation of the current
+     * foreground app. For example, if a user quickswitches from a portrait to a fixed landscape
+     * app and then rotates rotates the device to match that orientation, this triggers calls to
+     * sysui to adjust the navbar.
+     */
+    private OrientationEventListener mOrientationListener;
+    private int mPreviousRotation = ROTATION_0;
+    /**
+     * This is the configuration of the foreground app or the app that will be in the foreground
+     * once a quickstep gesture finishes.
+     */
+    private int mCurrentAppRotation = -1;
+    /**
+     * This flag is set to true when the device physically changes orientations. When true,
+     * we will always report the current rotation of the foreground app whenever the display
+     * changes, as it would indicate the user's intention to rotate the foreground app.
+     */
+    private boolean mPrioritizeDeviceRotation = false;
 
     private Region mExclusionRegion;
     private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -193,6 +241,26 @@
             userSetupObserver.register();
             runOnDestroy(userSetupObserver::unregister);
         }
+
+        mOrientationListener = new OrientationEventListener(context) {
+            @Override
+            public void onOrientationChanged(int degrees) {
+                int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
+                        mPreviousRotation);
+                if (newRotation == mPreviousRotation) {
+                    return;
+                }
+
+                mPreviousRotation = newRotation;
+                mPrioritizeDeviceRotation = true;
+
+                if (newRotation == mCurrentAppRotation) {
+                    // When user rotates device to the orientation of the foreground app after
+                    // quickstepping
+                    toggleSecondaryNavBarsForRotation(false);
+                }
+            }
+        };
     }
 
     private void setupOrientationSwipeHandler() {
@@ -268,6 +336,18 @@
         mNavBarPosition = new NavBarPosition(mMode, info);
         updateGestureTouchRegions();
         mOrientationTouchTransformer.createOrAddTouchRegion(info);
+        mCurrentAppRotation = mDisplayRotation;
+
+        /* Update nav bars on the following:
+         * a) if we're not expecting quickswitch, this is coming from an activity rotation
+         * b) we launch an app in the orientation that user is already in
+         * c) We're not in overview, since overview will always be portrait (w/o home rotation)
+         */
+        if ((mPrioritizeDeviceRotation
+                || mCurrentAppRotation == mPreviousRotation) // switch to an app of orientation user is in
+                && !mInOverview) {
+            toggleSecondaryNavBarsForRotation(false);
+        }
     }
 
     /**
@@ -553,9 +633,13 @@
         mOrientationTouchTransformer.transform(event);
     }
 
-    void enableMultipleRegions(boolean enable) {
-        mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
-        notifySysuiForRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
+    private void enableMultipleRegions(boolean enable) {
+        toggleSecondaryNavBarsForRotation(enable);
+        if (enable && !TestProtocol.sDisableSensorRotation) {
+            mOrientationListener.enable();
+        } else {
+            mOrientationListener.disable();
+        }
     }
 
     private void notifySysuiForRotation(int rotation) {
@@ -581,10 +665,7 @@
                 // If we're in landscape w/o ever quickswitching, show the navbar in landscape
                 enableMultipleRegions(true);
             }
-            activityInterface.onExitOverview(this, () -> {
-                mInOverview = false;
-                enableMultipleRegions(false);
-            });
+            activityInterface.onExitOverview(this, mExitOverviewRunnable);
         } else if (endTarget == GestureState.GestureEndTarget.HOME) {
             enableMultipleRegions(false);
         } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
@@ -594,6 +675,11 @@
             } else {
                 notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
             }
+
+            // A new gesture is starting, reset the current device rotation
+            // This is done under the assumption that the user won't rotate the phone and then
+            // quickswitch in the old orientation.
+            mPrioritizeDeviceRotation = false;
         } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
             if (!mTaskListFrozen) {
                 // touched nav bar but didn't go anywhere and not quickswitching, do nothing
@@ -603,7 +689,24 @@
         }
     }
 
-    int getCurrentActiveRotation() {
+    private void notifySysuiOfCurrentRotation(int rotation) {
+        UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
+                .onQuickSwitchToNewTask(rotation));
+    }
+
+    /**
+     * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
+     * notifies system UI of the primary rotation the user is interacting with
+     *
+     * @param enable if {@code true}, this will report to sysUI the navbar of the region the gesture
+     *               started in (during ACTION_DOWN), otherwise will report {@param displayRotation}
+     */
+    private void toggleSecondaryNavBarsForRotation(boolean enable) {
+        mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+        notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
+    }
+
+    public int getCurrentActiveRotation() {
         if (!mMode.hasGestures) {
             // touch rotation should always match that of display for 3 button
             return mDisplayRotation;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 348a78d..390330f 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -17,6 +17,7 @@
 package com.android.quickstep.logging;
 
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.PREDICTED_HOTSEAT_CONTAINER;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
@@ -93,6 +94,11 @@
         log(event, DEFAULT_INSTANCE_ID, info);
     }
 
+    @Override
+    public void log(EventEnum event, ItemInfo itemInfo) {
+        logInternal(event, DEFAULT_INSTANCE_ID, itemInfo);
+    }
+
     /**
      * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
      */
@@ -134,6 +140,27 @@
         logInternal(event, DEFAULT_INSTANCE_ID, info, srcState, dstState);
     }
 
+    private void logInternal(EventEnum event, InstanceId instanceId, @Nullable ItemInfo info) {
+        LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+                new BaseModelUpdateTask() {
+                    @Override
+                    public void execute(LauncherAppState app, BgDataModel dataModel,
+                            AllAppsList apps) {
+                        LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
+                        if (info != null) {
+                            if (info.container >= 0) {
+                                atomInfo = info.buildProto(dataModel.folders.get(info.container));
+                            } else {
+                                atomInfo = info.buildProto();
+                            }
+                        }
+                        logInternal(event, instanceId, atomInfo,
+                                LAUNCHER_UICHANGED__DST_STATE__HOME,
+                                LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
+                    }
+                });
+    }
+
     /**
      * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
      */
@@ -142,14 +169,14 @@
         info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info;
 
         if (IS_VERBOSE) {
-            String name = (event instanceof LauncherEvent) ? ((LauncherEvent) event).name() :
+            String name = (event instanceof Enum) ? ((Enum) event).name() :
                     event.getId() + "";
 
             Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
                     ? String.format("\n%s (State:%s->%s) \n%s", name, getStateString(srcState),
-                            getStateString(dstState), info)
-                    : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name, instanceId,
-                            getStateString(srcState), getStateString(dstState), info));
+                    getStateString(dstState), info)
+                    : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
+                            getStateString(srcState), getStateString(dstState), instanceId, info));
         }
 
         if (!Utilities.ATLEAST_R) {
@@ -181,7 +208,7 @@
                 info.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
                 info.getFolderIcon().getToLabelState().getNumber() /* toState */,
                 info.getFolderIcon().getLabelInfo() /* edittext */,
-                info.getFolderIcon().getCardinality() /* cardinality */);
+                getCardinality(info) /* cardinality */);
     }
 
     /**
@@ -245,11 +272,17 @@
                 getHierarchy(info) /* hierarchy */,
                 info.getIsWork() /* is_work_profile */,
                 info.getAttribute().getNumber() /* origin */,
-                info.getFolderIcon().getCardinality() /* cardinality */,
+                getCardinality(info) /* cardinality */,
                 info.getWidget().getSpanX(),
                 info.getWidget().getSpanY());
     }
 
+    private static int getCardinality(LauncherAtom.ItemInfo info) {
+        return info.getContainerInfo().getContainerCase().equals(PREDICTED_HOTSEAT_CONTAINER)
+                ? info.getContainerInfo().getPredictedHotseatContainer().getCardinality()
+                : info.getFolderIcon().getCardinality();
+    }
+
     private static String getPackageName(LauncherAtom.ItemInfo info) {
         switch (info.getItemCase()) {
             case APPLICATION:
@@ -326,7 +359,7 @@
     }
 
     private static String getStateString(int state) {
-        switch(state) {
+        switch (state) {
             case LAUNCHER_UICHANGED__DST_STATE__BACKGROUND:
                 return "BACKGROUND";
             case LAUNCHER_UICHANGED__DST_STATE__HOME:
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 7e8222c..1abe903 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.OnboardingPrefs;
@@ -100,6 +101,28 @@
             });
         }
 
+        if (!hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
+            stateManager.addStateListener(new StateListener<LauncherState>() {
+                boolean mFromAllApps = false;
+
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                    mFromAllApps = mLauncher.getStateManager().getCurrentStableState() == ALL_APPS;
+                }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    HotseatPredictionController client = mLauncher.getHotseatPredictionController();
+                    if (mFromAllApps && finalState == NORMAL && client.hasPredictions()) {
+                        if (incrementEventCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
+                            client.showDiscoveryTip();
+                            stateManager.removeStateListener(this);
+                        }
+                    }
+                }
+            });
+        }
+
         if (SysUINavigationMode.getMode(launcher) == NO_BUTTON
                 && FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
             stateManager.addStateListener(new StateListener<LauncherState>() {
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 7715cca..5af3d70 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -197,7 +197,7 @@
         mPreviousRotation = touchRotation;
 
         if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
-            mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
+            mOrientationHandler = PagedOrientationHandler.PORTRAIT;
             if (DEBUG) {
                 Log.d(TAG, "current RecentsOrientedState: " + this);
             }
@@ -445,7 +445,8 @@
                 }
                 break;
             case ROTATION_270:
-                if (degrees < (90 - threshold)) {
+                if (degrees < (90 - threshold) ||
+                        (degrees > (270 + threshold) && degrees < 360)) {
                     return ROTATION_0;
                 }
                 if (degrees > (90 + threshold) && degrees < 180) {
@@ -468,7 +469,8 @@
                 if (degrees < (270 - threshold) && degrees > 90) {
                     return ROTATION_180;
                 }
-                if (degrees > (270 + threshold) && degrees < 360) {
+                if (degrees > (270 + threshold) && degrees < 360
+                        || (degrees >= 0 && degrees < threshold)) {
                     return ROTATION_0;
                 }
                 // flip from landscape to seascape
diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml
index 087e45a..2c97e89 100644
--- a/res/layout/arrow_toast.xml
+++ b/res/layout/arrow_toast.xml
@@ -34,6 +34,8 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
+            android:paddingTop="5dp"
+            android:paddingBottom="5dp"
             android:gravity="center"
             android:layout_gravity="center_vertical"
             android:textColor="@android:color/white"
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 572615f..cd27a2d 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -174,7 +174,8 @@
                 targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
 
         if (mIsOpen) {
-            performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+            getAccessibilityInitialFocusView().performAccessibilityAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
         }
         ActivityContext.lookupContext(getContext()).getDragLayer()
                 .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
@@ -184,6 +185,11 @@
         return null;
     }
 
+    /** Returns the View that Accessibility services should focus on first. */
+    protected View getAccessibilityInitialFocusView() {
+        return this;
+    }
+
     /**
      * Returns a view matching FloatingViewType
      */
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index fdf0ea4..c7487cb 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -745,6 +745,11 @@
                 : getContext().getString(R.string.folder_closed));
     }
 
+    @Override
+    protected View getAccessibilityInitialFocusView() {
+        return mContent.getFirstItem();
+    }
+
     private void closeComplete(boolean wasAnimated) {
         // TODO: Clear all active animations.
         DragLayer parent = (DragLayer) getParent();
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index f216f81..6af0eef 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -127,7 +127,10 @@
         LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP(522),
 
         @UiEvent(doc = "User is shown All Apps education view.")
-        LAUNCHER_ALL_APPS_EDU_SHOWN(523);
+        LAUNCHER_ALL_APPS_EDU_SHOWN(523),
+
+        @UiEvent(doc = "User opened a folder.")
+        LAUNCHER_FOLDER_OPEN(551);
         // ADD MORE
 
         private final int mId;
@@ -195,6 +198,13 @@
     }
 
     /**
+     * Logs an event and accompanying {@link com.android.launcher3.model.data.ItemInfo}.
+     */
+    public void log(EventEnum event,
+            com.android.launcher3.model.data.ItemInfo itemInfo) {
+    }
+
+    /**
      * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
      */
     public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 8dcdec1..d52b7eb 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -337,10 +337,13 @@
     ContainerInfo getContainerInfo() {
         switch (container) {
             case CONTAINER_HOTSEAT:
-            case CONTAINER_HOTSEAT_PREDICTION:
                 return ContainerInfo.newBuilder()
                         .setHotseat(LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId))
                         .build();
+            case CONTAINER_HOTSEAT_PREDICTION:
+                return ContainerInfo.newBuilder().setPredictedHotseatContainer(
+                        LauncherAtom.PredictedHotseatContainer.newBuilder().setIndex(screenId))
+                        .build();
             case CONTAINER_DESKTOP:
                 return ContainerInfo.newBuilder()
                         .setWorkspace(
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 5b0c388..d5b32fc 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -389,6 +389,11 @@
         return Pair.create(this, "");
     }
 
+    @Override
+    protected View getAccessibilityInitialFocusView() {
+        return getChildCount() > 0 ? getChildAt(0) : this;
+    }
+
     private void animateOpen() {
         setVisibility(View.VISIBLE);
 
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 2c21609..171c5ee 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -310,6 +310,9 @@
     }
 
     protected void updateProgress(float fraction) {
+        if (mCurrentAnimation == null) {
+            return;
+        }
         mCurrentAnimation.setPlayFraction(fraction);
         if (mAtomicComponentsController != null) {
             // Make sure we don't divide by 0, and have at least a small runway.
diff --git a/src/com/android/launcher3/touch/HomeRotatedPageHandler.java b/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
deleted file mode 100644
index db5c659..0000000
--- a/src/com/android/launcher3/touch/HomeRotatedPageHandler.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.touch;
-
-import android.graphics.RectF;
-import android.view.Surface;
-import android.widget.LinearLayout;
-
-public class HomeRotatedPageHandler extends PortraitPagedViewHandler {
-    @Override
-    public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
-        if (launcherRotation == Surface.ROTATION_0) {
-            super.offsetTaskRect(rect, value, displayRotation, launcherRotation);
-        } else if (launcherRotation == Surface.ROTATION_90) {
-            if (displayRotation == Surface.ROTATION_0) {
-                rect.offset(0, value);
-            } else if (displayRotation == Surface.ROTATION_90) {
-                rect.offset(value, 0);
-            } else if (displayRotation == Surface.ROTATION_180) {
-                rect.offset(-value, 0);
-            } else {
-                rect.offset(-value, 0);
-            }
-        } else if (launcherRotation == Surface.ROTATION_270) {
-            if (displayRotation == Surface.ROTATION_0) {
-                rect.offset(0, -value);
-            } else if (displayRotation == Surface.ROTATION_90) {
-                rect.offset(value, 0);
-            } else if (displayRotation == Surface.ROTATION_180) {
-                rect.offset(0, -value);
-            } else {
-                rect.offset(value, 0);
-            }
-        } // 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/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 6abca76..de16941 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
@@ -45,6 +46,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+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.ItemInfo;
@@ -111,6 +113,7 @@
         if (!folder.isOpen() && !folder.isDestroyed()) {
             // Open the requested folder
             folder.animateOpen();
+            StatsLogManager.newInstance(v.getContext()).log(LAUNCHER_FOLDER_OPEN, folder.mInfo);
         }
     }
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index d02c731..48c7734 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -181,19 +181,6 @@
     }
 
     @Override
-    public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
-        if (displayRotation == Surface.ROTATION_0) {
-            rect.offset(0, value);
-        } else if (displayRotation == Surface.ROTATION_90) {
-            rect.offset(value, 0);
-        } else if (displayRotation == Surface.ROTATION_180) {
-            rect.offset(0, -value);
-        } else {
-            rect.offset(-value, 0);
-        }
-    }
-
-    @Override
     public int getChildStart(View view) {
         return view.getTop();
     }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 2e0268d..65b1a7a 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -43,7 +43,6 @@
     PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
     PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
     PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
-    PagedOrientationHandler HOME_ROTATED = new HomeRotatedPageHandler();
 
     interface Int2DAction<T> {
         void call(T target, int x, int y);
@@ -82,7 +81,6 @@
     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);
     void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 2fc7a9f..79e5c87 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -179,19 +179,6 @@
     }
 
     @Override
-    public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
-        if (displayRotation == Surface.ROTATION_0) {
-            rect.offset(value, 0);
-        } else if (displayRotation == Surface.ROTATION_90) {
-            rect.offset(0, -value);
-        } else if (displayRotation == Surface.ROTATION_180) {
-            rect.offset(-value, 0);
-        } else {
-            rect.offset(0, value);
-        }
-    }
-
-    @Override
     public int getChildStart(View view) {
         return view.getLeft();
     }
@@ -250,7 +237,7 @@
 
     @Override
     public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
-        return LinearLayout.VERTICAL;
+        return taskMenuLayout.getOrientation();
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 4c1700e..d5ae2dc 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -18,7 +18,6 @@
 
 import android.content.res.Resources;
 import android.graphics.PointF;
-import android.graphics.RectF;
 import android.view.Surface;
 import android.view.View;
 
@@ -42,19 +41,6 @@
     }
 
     @Override
-    public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
-        if (displayRotation == Surface.ROTATION_0) {
-            rect.offset(0, value);
-        } else if (displayRotation == Surface.ROTATION_90) {
-            rect.offset(value, 0);
-        } else if (displayRotation == Surface.ROTATION_180) {
-            rect.offset(0, -value);
-        } else {
-            rect.offset(-value, 0);
-        }
-    }
-
-    @Override
     public float getDegreesRotated() {
         return 270;
     }
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 1620289..90a1c82 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -37,13 +37,14 @@
     public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
     public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
     public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
+    public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
 
     /**
      * Events that either have happened or have not (booleans).
      */
     @StringDef(value = {
             HOME_BOUNCE_SEEN,
-            SHELF_BOUNCE_SEEN,
+            SHELF_BOUNCE_SEEN
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventBoolKey {}
@@ -55,6 +56,7 @@
             HOME_BOUNCE_COUNT,
             SHELF_BOUNCE_COUNT,
             ALL_APPS_COUNT,
+            HOTSEAT_DISCOVERY_TIP_COUNT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventCountKey {}
@@ -65,6 +67,7 @@
         maxCounts.put(HOME_BOUNCE_COUNT, 3);
         maxCounts.put(SHELF_BOUNCE_COUNT, 3);
         maxCounts.put(ALL_APPS_COUNT, 5);
+        maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
         MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
     }