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