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");