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 fe99037ccf..c93cad65bf 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 @@
@@ -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 e7290a4482..a07cd1d82c 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 android.view.ViewGroup;
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 @@ public class HotseatEduController {
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 @@ public class HotseatEduController {
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 @@ public class HotseatEduController {
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 8926246ad2..538b7f3952 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.util.AttributeSet;
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.Insettable;
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 @@ import java.util.List;
public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
private static final int DEFAULT_CLOSE_DURATION = 200;
+ protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
+
+ // 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;
- public static boolean shown = false;
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 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
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 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
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 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
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 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
+
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@@ -183,17 +206,12 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
handleClose(false);
}
- /**
- * Opens User education dialog with a list of suggested apps
- */
- public void show(List 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 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 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
}
}
+ @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 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 45a62ab151..90d42456d2 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -66,27 +66,32 @@
Close
+
- Easily access your most-used apps
+ Get app suggestions based on your routines
- Pixel suggests your favorite apps based on your routines. Tap to learn more.
-
- Suggested apps replace the bottom row of apps
-
- Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.
+ Tap to set up
+
+
+
+ Suggested apps replace the bottom row of apps
+ Your hotseat items will be moved up on the homescreen
+ Your hotseat items will be moved to the last page of your workspace
+
+
+
+ Suggested apps will be found at the bottom row of your home screen
+ Drag one or many apps off the bottom row of home screen to see app suggestions
+
- Bottom row of apps moved to last screen
+ Bottom row of apps moved up.
+ Bottom row of apps moved to last page.
Bottom row won\'t be replaced. Manually drag apps for predictions.
- Turn On
+ Got it
- No thanks
-
- Your hotseat just got smarter
-
- Tap here to set it up
-
+ No thanks
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8718820bd2..e3eb38792a 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2783,6 +2783,26 @@ public class CellLayout extends ViewGroup implements Transposable {
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 3d8a9d7bbf..96903e416d 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -113,6 +113,10 @@ public final class FeatureFlags {
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");