Show discovery tip for hybrid hotseat
Doc: go/hybrid-hotseat-tips
Issue 157683315: for fully populated hotseat, count returns to home screen and show discovery tip if Tip action was not tapped.
Issue 158301717: Don't use cached items if client has predicted items.
Test: Manual
Change-Id: I4747a1148caa62a6262fb6592d5185bdf216ede6
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 f1ce72e..49f3eb8 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
@@ -47,12 +47,12 @@
public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen";
public static final String HOTSEAT_EDU_ACTION =
"com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
- private static final String SETTINGS_ACTION =
+ public static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
private final Launcher mLauncher;
private final Hotseat mHotseat;
- private final HotseatRestoreHelper mRestoreHelper;
+ private HotseatRestoreHelper mRestoreHelper;
private List<WorkspaceItemInfo> mPredictedApps;
private HotseatEduDialog mActiveDialog;
@@ -71,14 +71,17 @@
* Checks what type of migration should be used and migrates hotseat
*/
void migrate() {
- mRestoreHelper.createBackup();
+ if (mRestoreHelper != null) {
+ mRestoreHelper.createBackup();
+ }
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
migrateToFolder();
} else {
migrateHotseatWhole();
}
- Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled, R.string.hotseat_turn_off,
- null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+ Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
}
/**
@@ -223,15 +226,15 @@
void finishOnboarding() {
mOnOnboardingComplete.run();
- destroy();
mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
}
void showDimissTip() {
if (mHotseat.getShortcutsAndWidgets().getChildCount()
< mLauncher.getDeviceProfile().inv.numHotseatIcons) {
- Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled, R.string.hotseat_turn_off,
- null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
} else {
new ArrowTipView(mLauncher).show(
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
@@ -242,12 +245,6 @@
mPredictedApps = predictedApps;
}
- void destroy() {
- if (mActiveDialog != null) {
- mActiveDialog.setHotseatEduController(null);
- }
- }
-
void showEdu() {
int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID);
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 99cb3b3..bbc128f 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
@@ -29,6 +29,7 @@
import android.widget.Button;
import android.widget.TextView;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
@@ -245,6 +246,7 @@
|| mHotseatEduController == null) {
return;
}
+ AbstractFloatingView.closeAllOpenViews(mLauncher);
attachToContainer();
logOnBoardingSeen();
animateOpen();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index bd4d713..27423a5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.SETTINGS_ACTION;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -27,6 +28,7 @@
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.content.ComponentName;
+import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -64,6 +66,8 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.launcher3.views.Snackbar;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -107,8 +111,6 @@
private boolean mIsCacheEmpty;
private boolean mIsDestroyed = false;
- private HotseatEduController mHotseatEduController;
-
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
@@ -146,11 +148,48 @@
}
/**
- * Transitions to NORMAL workspace mode and shows edu
+ * Shows appropriate hotseat education based on prediction enabled and migration states.
*/
public void showEdu() {
- if (mHotseatEduController == null) return;
- mHotseatEduController.showEdu();
+ if (mComponentKeyMappers.isEmpty()) {
+ // launcher has empty predictions set
+ Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(
+ new Intent(SETTINGS_ACTION)));
+ } else if (isEduSeen()) {
+ // user has already went through education
+ new ArrowTipView(mLauncher).show(
+ mLauncher.getString(R.string.hotsaet_tip_prediction_enabled),
+ mHotseat.getTop());
+ } else {
+ HotseatEduController eduController = new HotseatEduController(mLauncher, mRestoreHelper,
+ this::createPredictor);
+ eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
+ eduController.showEdu();
+ }
+ }
+
+ /**
+ * Shows educational tip for hotseat if user does not go through Tips app.
+ */
+ public void showDiscoveryTip() {
+ if (getPredictedIcons().size() == mHotSeatItemsCount) {
+ new ArrowTipView(mLauncher).show(
+ mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+ } else {
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
+ }
+ }
+
+ /**
+ * Returns if hotseat client has predictions
+ * @return
+ */
+ public boolean hasPredictions() {
+ return !mComponentKeyMappers.isEmpty();
}
@Override
@@ -250,10 +289,6 @@
if (mAppPredictor != null) {
mAppPredictor.destroy();
}
- if (mHotseatEduController != null) {
- mHotseatEduController.destroy();
- mHotseatEduController = null;
- }
}
/**
@@ -299,10 +334,6 @@
mAppPredictor.requestPredictionUpdate();
});
setPauseUIUpdate(false);
- if (!isEduSeen()) {
- mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper,
- this::createPredictor);
- }
}
/**
@@ -350,9 +381,6 @@
if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
updateDependencies();
fillGapsWithPrediction();
- if (!isEduSeen() && mHotseatEduController != null) {
- mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
- }
cachePredictionComponentKeysIfNecessary(componentKeys);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 494a98d..f7d0cd5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -88,7 +88,6 @@
*/
public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
- private HotseatPredictionController mHotseatPredictionController;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -169,13 +168,6 @@
}
/**
- * Returns Prediction controller for hybrid hotseat
- */
- public HotseatPredictionController getHotseatPredictionController() {
- return mHotseatPredictionController;
- }
-
- /**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
*/
private void onStateOrResumeChanging(boolean inTransition) {
@@ -195,7 +187,8 @@
@Override
public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
super.bindPredictedItems(appInfos, ranks);
- if (mHotseatPredictionController != null) {
+ if (mHotseatPredictionController != null
+ && !mHotseatPredictionController.hasPredictions()) {
mHotseatPredictionController.showCachedItems(appInfos, ranks);
}
}
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a27c127..1b82826 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -76,8 +76,8 @@
<!-- Button text to dismiss opt in for fully predicted hotseat -->
<string name="hotseat_edu_dismiss">No thanks</string>
- <!-- action shown to turn off predictions after onboarding -->
- <string name="hotseat_turn_off">Settings</string>
+ <!-- action shown to toggle predictions after onboarding -->
+ <string name="hotseat_prediction_settings">Settings</string>
<!-- tip shown if user has no items in hotseat to migrate -->
<string name="hotseat_auto_enrolled">Most-used apps appear here, and change based on routines</string>
@@ -86,7 +86,9 @@
<!-- tip shown if user declines migration and has some open spots for prediction -->
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
<!-- tip shown when user migrates and predictions are enabled in hotseat -->
- <string name="hotsaet_tip_prediction_enabled">App suggestions Enabled</string>
+ <string name="hotsaet_tip_prediction_enabled">App suggestions enabled</string>
+ <!-- tip shown when hotseat edu is requested while predicions are disabled -->
+ <string name="hotsaet_tip_prediction_disabled">App suggestions are disabled</string>
<!-- content description for hotseat items -->
<string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4874307..d2e0339 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -31,6 +31,7 @@
import android.os.CancellationSignal;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -75,6 +76,7 @@
private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
private OverviewActionsView mActionsView;
+ protected HotseatPredictionController mHotseatPredictionController;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -305,6 +307,13 @@
return mShelfPeekAnim;
}
+ /**
+ * Returns Prediction controller for hybrid hotseat
+ */
+ public HotseatPredictionController getHotseatPredictionController() {
+ return mHotseatPredictionController;
+ }
+
public void setHintUserWillBeActive() {
addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
}
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 7e8222c..1abe903 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -31,6 +31,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.OnboardingPrefs;
@@ -100,6 +101,28 @@
});
}
+ if (!hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
+ stateManager.addStateListener(new StateListener<LauncherState>() {
+ boolean mFromAllApps = false;
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ mFromAllApps = mLauncher.getStateManager().getCurrentStableState() == ALL_APPS;
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ HotseatPredictionController client = mLauncher.getHotseatPredictionController();
+ if (mFromAllApps && finalState == NORMAL && client.hasPredictions()) {
+ if (incrementEventCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
+ client.showDiscoveryTip();
+ stateManager.removeStateListener(this);
+ }
+ }
+ }
+ });
+ }
+
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON
&& FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
stateManager.addStateListener(new StateListener<LauncherState>() {
diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml
index 087e45a..2c97e89 100644
--- a/res/layout/arrow_toast.xml
+++ b/res/layout/arrow_toast.xml
@@ -34,6 +34,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:paddingTop="5dp"
+ android:paddingBottom="5dp"
android:gravity="center"
android:layout_gravity="center_vertical"
android:textColor="@android:color/white"
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 1620289..90a1c82 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -37,13 +37,14 @@
public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
+ public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
/**
* Events that either have happened or have not (booleans).
*/
@StringDef(value = {
HOME_BOUNCE_SEEN,
- SHELF_BOUNCE_SEEN,
+ SHELF_BOUNCE_SEEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {}
@@ -55,6 +56,7 @@
HOME_BOUNCE_COUNT,
SHELF_BOUNCE_COUNT,
ALL_APPS_COUNT,
+ HOTSEAT_DISCOVERY_TIP_COUNT
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventCountKey {}
@@ -65,6 +67,7 @@
maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5);
+ maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
}