Enable support for same page hotseat migration

Bug: 142753423
Test: Manual
Change-Id: Ib53a64629a595c412e30ca5ff46077efc73a3f3e
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/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/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/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/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 3d8a9d7..96903e4 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");