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
This commit is contained in:
parent
c11a808b99
commit
5b2da14e72
|
@ -47,12 +47,12 @@ public class HotseatEduController {
|
|||
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 @@ public class HotseatEduController {
|
|||
* Checks what type of migration should be used and migrates hotseat
|
||||
*/
|
||||
void migrate() {
|
||||
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 @@ public class HotseatEduController {
|
|||
|
||||
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 @@ public class HotseatEduController {
|
|||
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);
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.view.View;
|
|||
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 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
|| mHotseatEduController == null) {
|
||||
return;
|
||||
}
|
||||
AbstractFloatingView.closeAllOpenViews(mLauncher);
|
||||
attachToContainer();
|
||||
logOnBoardingSeen();
|
||||
animateOpen();
|
||||
|
|
|
@ -17,6 +17,7 @@ package com.android.launcher3.hybridhotseat;
|
|||
|
||||
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.AppPredictor;
|
|||
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.uioverrides.QuickstepLauncher;
|
|||
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 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
private boolean mIsCacheEmpty;
|
||||
private boolean mIsDestroyed = false;
|
||||
|
||||
private HotseatEduController mHotseatEduController;
|
||||
|
||||
|
||||
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
|
||||
|
||||
|
@ -146,11 +148,48 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
if (mAppPredictor != null) {
|
||||
mAppPredictor.destroy();
|
||||
}
|
||||
if (mHotseatEduController != null) {
|
||||
mHotseatEduController.destroy();
|
||||
mHotseatEduController = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -299,10 +334,6 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
mAppPredictor.requestPredictionUpdate();
|
||||
});
|
||||
setPauseUIUpdate(false);
|
||||
if (!isEduSeen()) {
|
||||
mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper,
|
||||
this::createPredictor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -350,9 +381,6 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
|
||||
updateDependencies();
|
||||
fillGapsWithPrediction();
|
||||
if (!isEduSeen() && mHotseatEduController != null) {
|
||||
mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
|
||||
}
|
||||
cachePredictionComponentKeysIfNecessary(componentKeys);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
*/
|
||||
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) {
|
||||
|
@ -168,13 +167,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Prediction controller for hybrid hotseat
|
||||
*/
|
||||
public HotseatPredictionController getHotseatPredictionController() {
|
||||
return mHotseatPredictionController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
|
||||
*/
|
||||
|
@ -195,7 +187,8 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
@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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.os.Bundle;
|
|||
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 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
|||
private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
|
||||
|
||||
private OverviewActionsView mActionsView;
|
||||
protected HotseatPredictionController mHotseatPredictionController;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -305,6 +307,13 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
|||
return mShelfPeekAnim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Prediction controller for hybrid hotseat
|
||||
*/
|
||||
public HotseatPredictionController getHotseatPredictionController() {
|
||||
return mHotseatPredictionController;
|
||||
}
|
||||
|
||||
public void setHintUserWillBeActive() {
|
||||
addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.android.launcher3.BaseQuickstepLauncher;
|
|||
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 @@ public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLaunc
|
|||
});
|
||||
}
|
||||
|
||||
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>() {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -37,13 +37,14 @@ public class OnboardingPrefs<T extends Launcher> {
|
|||
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 @@ public class OnboardingPrefs<T extends Launcher> {
|
|||
HOME_BOUNCE_COUNT,
|
||||
SHELF_BOUNCE_COUNT,
|
||||
ALL_APPS_COUNT,
|
||||
HOTSEAT_DISCOVERY_TIP_COUNT
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EventCountKey {}
|
||||
|
@ -65,6 +67,7 @@ public class OnboardingPrefs<T extends Launcher> {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue