Loading quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +18 −5 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import com.android.launcher3.views.Snackbar; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; /** * Controller class for managing user onboaridng flow for hybrid hotseat Loading Loading @@ -110,7 +111,8 @@ public class HotseatEduController { ItemInfo info = (ItemInfo) view.getTag(); if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { folders.add((FolderInfo) info); } else if (info instanceof WorkspaceItemInfo) { } else if (info instanceof WorkspaceItemInfo && info.container == LauncherSettings .Favorites.CONTAINER_HOTSEAT) { putIntoFolder.add((WorkspaceItemInfo) info); } } Loading Loading @@ -206,6 +208,7 @@ public class HotseatEduController { View child = mHotseat.getChildAt(i, 0); if (child == null || child.getTag() == null) continue; ItemInfo tag = (ItemInfo) child.getTag(); if (tag.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) continue; mLauncher.getModelWriter().moveItemInDatabase(tag, LauncherSettings.Favorites.CONTAINER_DESKTOP, pageId, i, toRow); mNewItems.add(tag); Loading Loading @@ -300,13 +303,23 @@ public class HotseatEduController { } void showEdu() { int childCount = mHotseat.getShortcutsAndWidgets().getChildCount(); CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID); // hotseat is already empty and does not require migration. show edu tip if (mHotseat.getShortcutsAndWidgets().getChildCount() == 0) { new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_auto_enrolled), boolean requiresMigration = IntStream.range(0, childCount).anyMatch(i -> { View v = mHotseat.getShortcutsAndWidgets().getChildAt(i); return v != null && v.getTag() != null && ((ItemInfo) v.getTag()).container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; }); boolean canMigrateToFirstPage = cellLayout.makeSpaceForHotseatMigration(false); if (requiresMigration && canMigrateToFirstPage) { showDialog(); } else { new ArrowTipView(mLauncher).show(mLauncher.getString( requiresMigration ? R.string.hotseat_tip_no_empty_slots : R.string.hotseat_auto_enrolled), mHotseat.getTop()); finishOnboarding(); } else { showDialog(); } } Loading quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +18 −51 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.logging.LoggerUtils.newAction; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; import android.animation.Animator; import android.animation.AnimatorSet; Loading @@ -32,7 +29,6 @@ import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.os.Bundle; import android.os.Process; import android.provider.DeviceConfig; import android.util.Log; import android.view.View; import android.view.ViewGroup; Loading Loading @@ -60,7 +56,6 @@ import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.icons.IconCache; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; Loading @@ -70,12 +65,12 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.uioverrides.DeviceFlag; import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; Loading Loading @@ -155,9 +150,9 @@ public class HotseatPredictionController implements DragController.DragListener, } /** * Returns whether or not the prediction controller is ready to show predictions * Returns whether or not user has seen hybrid hotseat education */ public boolean isReady() { public boolean isEduSeen() { return mLauncher.getSharedPrefs().getBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN, false); } Loading Loading @@ -186,7 +181,7 @@ public class HotseatPredictionController implements DragController.DragListener, } private void fillGapsWithPrediction(boolean animate, Runnable callback) { if (!isReady() || mUIUpdatePaused || mDragObject != null) { if (mUIUpdatePaused || mDragObject != null) { return; } List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers); Loading Loading @@ -262,6 +257,10 @@ public class HotseatPredictionController implements DragController.DragListener, if (mAppPredictor != null) { mAppPredictor.destroy(); } if (mHotseatEduController != null) { mHotseatEduController.destroy(); mHotseatEduController = null; } } /** Loading Loading @@ -291,11 +290,16 @@ public class HotseatPredictionController implements DragController.DragListener, .setPredictedTargetCount(mHotSeatItemsCount) .setExtras(getAppPredictionContextExtra()) .build()); mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(), this::setPredictedApps); WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this); mAppPredictor.registerPredictionUpdates(mLauncher.getApplicationContext().getMainExecutor(), list -> { if (controllerRef.get() != null) { controllerRef.get().setPredictedApps(list); } }); setPauseUIUpdate(false); performBetaCheck(); if (!isReady()) { if (!isEduSeen()) { mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor); } mAppPredictor.requestPredictionUpdate(); Loading Loading @@ -386,9 +390,8 @@ public class HotseatPredictionController implements DragController.DragListener, predictionLog.append("]"); if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString()); updateDependencies(); if (isReady()) { fillGapsWithPrediction(); } else if (mHotseatEduController != null) { if (!isEduSeen() && mHotseatEduController != null) { mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers)); } // should invalidate cache if AiAi sends empty list of AppTargets Loading Loading @@ -682,42 +685,6 @@ public class HotseatPredictionController implements DragController.DragListener, } } private void performBetaCheck() { if (isReady()) return; int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount(); int maxItems = DeviceConfig.getInt( DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5); // -1 to exclude smart space int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId( Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1; // opt user into the feature without onboarding tip or migration if they don't have any // open spots in their hotseat and have more than maxItems in their hotseat + workspace if (hotseatItemsCount == mHotSeatItemsCount && workspaceItemCount + hotseatItemsCount > maxItems) { mLauncher.getSharedPrefs().edit().putBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN, true).apply(); LauncherLogProto.Action action = newAction(LauncherLogProto.Action.Type.TOUCH); LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.TIP); action.touch = LauncherLogProto.Action.Touch.TAP; target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT; target.controlType = LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED; // temporarily encode details in log target (go/hotseat_migration) target.rank = 2; target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount; target.pageIndex = maxItems; LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null); } } /** * Fill in predicted_rank field based on app prediction. * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT Loading quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +1 −0 Original line number Diff line number Diff line Loading @@ -170,6 +170,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { super.onDestroy(); if (mHotseatPredictionController != null) { mHotseatPredictionController.destroy(); mHotseatPredictionController = null; } } Loading src/com/android/launcher3/config/FeatureFlags.java +2 −2 Original line number Diff line number Diff line Loading @@ -117,8 +117,8 @@ public final class FeatureFlags { "ASSISTANT_GIVES_LAUNCHER_FOCUS", false, "Allow Launcher to handle nav bar gestures while Assistant is running over it"); public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag( "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps"); public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag( "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps"); public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag( "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder"); Loading Loading
quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +18 −5 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import com.android.launcher3.views.Snackbar; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; /** * Controller class for managing user onboaridng flow for hybrid hotseat Loading Loading @@ -110,7 +111,8 @@ public class HotseatEduController { ItemInfo info = (ItemInfo) view.getTag(); if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { folders.add((FolderInfo) info); } else if (info instanceof WorkspaceItemInfo) { } else if (info instanceof WorkspaceItemInfo && info.container == LauncherSettings .Favorites.CONTAINER_HOTSEAT) { putIntoFolder.add((WorkspaceItemInfo) info); } } Loading Loading @@ -206,6 +208,7 @@ public class HotseatEduController { View child = mHotseat.getChildAt(i, 0); if (child == null || child.getTag() == null) continue; ItemInfo tag = (ItemInfo) child.getTag(); if (tag.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) continue; mLauncher.getModelWriter().moveItemInDatabase(tag, LauncherSettings.Favorites.CONTAINER_DESKTOP, pageId, i, toRow); mNewItems.add(tag); Loading Loading @@ -300,13 +303,23 @@ public class HotseatEduController { } void showEdu() { int childCount = mHotseat.getShortcutsAndWidgets().getChildCount(); CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID); // hotseat is already empty and does not require migration. show edu tip if (mHotseat.getShortcutsAndWidgets().getChildCount() == 0) { new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_auto_enrolled), boolean requiresMigration = IntStream.range(0, childCount).anyMatch(i -> { View v = mHotseat.getShortcutsAndWidgets().getChildAt(i); return v != null && v.getTag() != null && ((ItemInfo) v.getTag()).container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; }); boolean canMigrateToFirstPage = cellLayout.makeSpaceForHotseatMigration(false); if (requiresMigration && canMigrateToFirstPage) { showDialog(); } else { new ArrowTipView(mLauncher).show(mLauncher.getString( requiresMigration ? R.string.hotseat_tip_no_empty_slots : R.string.hotseat_auto_enrolled), mHotseat.getTop()); finishOnboarding(); } else { showDialog(); } } Loading
quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +18 −51 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.logging.LoggerUtils.newAction; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; import android.animation.Animator; import android.animation.AnimatorSet; Loading @@ -32,7 +29,6 @@ import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.os.Bundle; import android.os.Process; import android.provider.DeviceConfig; import android.util.Log; import android.view.View; import android.view.ViewGroup; Loading Loading @@ -60,7 +56,6 @@ import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.icons.IconCache; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; Loading @@ -70,12 +65,12 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.uioverrides.DeviceFlag; import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; Loading Loading @@ -155,9 +150,9 @@ public class HotseatPredictionController implements DragController.DragListener, } /** * Returns whether or not the prediction controller is ready to show predictions * Returns whether or not user has seen hybrid hotseat education */ public boolean isReady() { public boolean isEduSeen() { return mLauncher.getSharedPrefs().getBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN, false); } Loading Loading @@ -186,7 +181,7 @@ public class HotseatPredictionController implements DragController.DragListener, } private void fillGapsWithPrediction(boolean animate, Runnable callback) { if (!isReady() || mUIUpdatePaused || mDragObject != null) { if (mUIUpdatePaused || mDragObject != null) { return; } List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers); Loading Loading @@ -262,6 +257,10 @@ public class HotseatPredictionController implements DragController.DragListener, if (mAppPredictor != null) { mAppPredictor.destroy(); } if (mHotseatEduController != null) { mHotseatEduController.destroy(); mHotseatEduController = null; } } /** Loading Loading @@ -291,11 +290,16 @@ public class HotseatPredictionController implements DragController.DragListener, .setPredictedTargetCount(mHotSeatItemsCount) .setExtras(getAppPredictionContextExtra()) .build()); mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(), this::setPredictedApps); WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this); mAppPredictor.registerPredictionUpdates(mLauncher.getApplicationContext().getMainExecutor(), list -> { if (controllerRef.get() != null) { controllerRef.get().setPredictedApps(list); } }); setPauseUIUpdate(false); performBetaCheck(); if (!isReady()) { if (!isEduSeen()) { mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor); } mAppPredictor.requestPredictionUpdate(); Loading Loading @@ -386,9 +390,8 @@ public class HotseatPredictionController implements DragController.DragListener, predictionLog.append("]"); if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString()); updateDependencies(); if (isReady()) { fillGapsWithPrediction(); } else if (mHotseatEduController != null) { if (!isEduSeen() && mHotseatEduController != null) { mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers)); } // should invalidate cache if AiAi sends empty list of AppTargets Loading Loading @@ -682,42 +685,6 @@ public class HotseatPredictionController implements DragController.DragListener, } } private void performBetaCheck() { if (isReady()) return; int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount(); int maxItems = DeviceConfig.getInt( DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5); // -1 to exclude smart space int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId( Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1; // opt user into the feature without onboarding tip or migration if they don't have any // open spots in their hotseat and have more than maxItems in their hotseat + workspace if (hotseatItemsCount == mHotSeatItemsCount && workspaceItemCount + hotseatItemsCount > maxItems) { mLauncher.getSharedPrefs().edit().putBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN, true).apply(); LauncherLogProto.Action action = newAction(LauncherLogProto.Action.Type.TOUCH); LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.TIP); action.touch = LauncherLogProto.Action.Touch.TAP; target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT; target.controlType = LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED; // temporarily encode details in log target (go/hotseat_migration) target.rank = 2; target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount; target.pageIndex = maxItems; LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null); } } /** * Fill in predicted_rank field based on app prediction. * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT Loading
quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +1 −0 Original line number Diff line number Diff line Loading @@ -170,6 +170,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { super.onDestroy(); if (mHotseatPredictionController != null) { mHotseatPredictionController.destroy(); mHotseatPredictionController = null; } } Loading
src/com/android/launcher3/config/FeatureFlags.java +2 −2 Original line number Diff line number Diff line Loading @@ -117,8 +117,8 @@ public final class FeatureFlags { "ASSISTANT_GIVES_LAUNCHER_FOCUS", false, "Allow Launcher to handle nav bar gestures while Assistant is running over it"); public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag( "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps"); public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag( "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps"); public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag( "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder"); Loading