Merge changes I51e949fa,Ic5202caf,I1111fa15,I11ffa080,I8d2f5f59 into rvc-dev am: 76dd667310 am: f1995e0261

Change-Id: Ieae4df4d0dda7b884b241c48b18541f255e8c62a
This commit is contained in:
Samuel Fufa
2020-03-26 22:09:32 +00:00
committed by Automerger Merge Worker
5 changed files with 107 additions and 27 deletions
@@ -26,6 +26,7 @@ import android.view.View;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import com.android.launcher3.ArrowTipView;
import com.android.launcher3.CellLayout; import com.android.launcher3.CellLayout;
import com.android.launcher3.FolderInfo; import com.android.launcher3.FolderInfo;
import com.android.launcher3.Hotseat; import com.android.launcher3.Hotseat;
@@ -42,6 +43,7 @@ import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.launcher3.views.Snackbar;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
@@ -56,7 +58,11 @@ public class HotseatEduController {
private static final String NOTIFICATION_CHANNEL_ID = "launcher_onboarding"; private static final String NOTIFICATION_CHANNEL_ID = "launcher_onboarding";
private static final int ONBOARDING_NOTIFICATION_ID = 7641; private static final int ONBOARDING_NOTIFICATION_ID = 7641;
private static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
private final Launcher mLauncher; private final Launcher mLauncher;
private final Hotseat mHotseat;
private final NotificationManager mNotificationManager; private final NotificationManager mNotificationManager;
private final Notification mNotification; private final Notification mNotification;
private List<WorkspaceItemInfo> mPredictedApps; private List<WorkspaceItemInfo> mPredictedApps;
@@ -68,6 +74,7 @@ public class HotseatEduController {
HotseatEduController(Launcher launcher, Runnable runnable) { HotseatEduController(Launcher launcher, Runnable runnable) {
mLauncher = launcher; mLauncher = launcher;
mHotseat = launcher.getHotseat();
mOnOnboardingComplete = runnable; mOnOnboardingComplete = runnable;
mNotificationManager = mLauncher.getSystemService(NotificationManager.class); mNotificationManager = mLauncher.getSystemService(NotificationManager.class);
createNotificationChannel(); createNotificationChannel();
@@ -98,7 +105,7 @@ public class HotseatEduController {
//separate folders and items that can get in folders //separate folders and items that can get in folders
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) { for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
View view = mLauncher.getHotseat().getChildAt(i, 0); View view = mHotseat.getChildAt(i, 0);
if (view == null) continue; if (view == null) continue;
ItemInfo info = (ItemInfo) view.getTag(); ItemInfo info = (ItemInfo) view.getTag();
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -178,7 +185,6 @@ public class HotseatEduController {
*/ */
private int migrateHotseatWhole() { private int migrateHotseatWhole() {
Workspace workspace = mLauncher.getWorkspace(); Workspace workspace = mLauncher.getWorkspace();
Hotseat hotseatVG = mLauncher.getHotseat();
int pageId = -1; int pageId = -1;
int toRow = 0; int toRow = 0;
@@ -196,7 +202,7 @@ public class HotseatEduController {
.getInt(LauncherSettings.Settings.EXTRA_VALUE); .getInt(LauncherSettings.Settings.EXTRA_VALUE);
} }
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) { for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
View child = hotseatVG.getChildAt(i, 0); View child = mHotseat.getChildAt(i, 0);
if (child == null || child.getTag() == null) continue; if (child == null || child.getTag() == null) continue;
ItemInfo tag = (ItemInfo) child.getTag(); ItemInfo tag = (ItemInfo) child.getTag();
mLauncher.getModelWriter().moveItemInDatabase(tag, mLauncher.getModelWriter().moveItemInDatabase(tag,
@@ -211,8 +217,8 @@ public class HotseatEduController {
mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID); mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID);
} }
void finishOnboarding() { void moveHotseatItems() {
mLauncher.getHotseat().removeAllViewsInLayout(); mHotseat.removeAllViewsInLayout();
if (!mNewItems.isEmpty()) { if (!mNewItems.isEmpty()) {
int lastPage = mNewItems.get(mNewItems.size() - 1).screenId; int lastPage = mNewItems.get(mNewItems.size() - 1).screenId;
ArrayList<ItemInfo> animated = new ArrayList<>(); ArrayList<ItemInfo> animated = new ArrayList<>();
@@ -227,17 +233,34 @@ public class HotseatEduController {
} }
mLauncher.bindAppsAdded(mNewScreens, nonAnimated, animated); mLauncher.bindAppsAdded(mNewScreens, nonAnimated, animated);
} }
}
void finishOnboarding() {
mOnOnboardingComplete.run(); mOnOnboardingComplete.run();
destroy(); destroy();
mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply(); 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)));
} else {
new ArrowTipView(mLauncher).show(
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
}
}
void setPredictedApps(List<WorkspaceItemInfo> predictedApps) { void setPredictedApps(List<WorkspaceItemInfo> predictedApps) {
mPredictedApps = predictedApps; mPredictedApps = predictedApps;
if (!mPredictedApps.isEmpty() if (!mPredictedApps.isEmpty()
&& mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) { && mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
mNotificationManager.notify(ONBOARDING_NOTIFICATION_ID, mNotification); mNotificationManager.notify(ONBOARDING_NOTIFICATION_ID, mNotification);
} }
else {
removeNotification();
}
} }
private void createNotificationChannel() { private void createNotificationChannel() {
@@ -275,6 +298,17 @@ public class HotseatEduController {
} }
} }
void showEdu() {
// 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),
mHotseat.getTop());
finishOnboarding();
} else {
showDialog();
}
}
void showDialog() { void showDialog() {
if (mPredictedApps == null || mPredictedApps.isEmpty()) { if (mPredictedApps == null || mPredictedApps.isEmpty()) {
return; return;
@@ -291,7 +325,7 @@ public class HotseatEduController {
ActivityTracker.SchedulerCallback<QuickstepLauncher> { ActivityTracker.SchedulerCallback<QuickstepLauncher> {
@Override @Override
public boolean init(QuickstepLauncher activity, boolean alreadyOnHome) { public boolean init(QuickstepLauncher activity, boolean alreadyOnHome) {
activity.getHotseatPredictionController().showEduDialog(); activity.getHotseatPredictionController().showEdu();
return true; return true;
} }
} }
@@ -16,7 +16,8 @@
package com.android.launcher3.hybridhotseat; package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED; import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
.HYBRID_HOTSEAT_CANCELED;
import android.animation.PropertyValuesHolder; import android.animation.PropertyValuesHolder;
import android.content.Context; import android.content.Context;
@@ -27,9 +28,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.ArrowTipView;
import com.android.launcher3.CellLayout; import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable; import com.android.launcher3.Insettable;
@@ -108,18 +107,16 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
private void onAccept(View v) { private void onAccept(View v) {
mHotseatEduController.migrate(); mHotseatEduController.migrate();
handleClose(true); handleClose(true);
mHotseatEduController.moveHotseatItems();
mHotseatEduController.finishOnboarding(); mHotseatEduController.finishOnboarding();
//TODO: pass actual page index here. //TODO: pass actual page index here.
// Temporarily we're passing 1 for folder migration and 2 for page migration // Temporarily we're passing 1 for folder migration and 2 for page migration
logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2); logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
int toastStringRes = !FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()
? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
} }
private void onDismiss(View v) { private void onDismiss(View v) {
int top = mLauncher.getHotseat().getTop(); mHotseatEduController.showDimissTip();
new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_no_migration), top);
mHotseatEduController.finishOnboarding(); mHotseatEduController.finishOnboarding();
logUserAction(false, -1); logUserAction(false, -1);
handleClose(true); handleClose(true);
@@ -165,6 +162,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
target.rank = MIGRATION_EXPERIMENT_IDENTIFIER; target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
// encoding migration type on pageIndex // encoding migration type on pageIndex
target.pageIndex = pageIndex; target.pageIndex = pageIndex;
target.cardinality = HotseatPredictionController.MAX_ITEMS_FOR_MIGRATION;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null); UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
} }
@@ -16,6 +16,9 @@
package com.android.launcher3.hybridhotseat; package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; 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.Animator;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
@@ -28,6 +31,7 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId; import android.app.prediction.AppTargetId;
import android.content.ComponentName; import android.content.ComponentName;
import android.os.Bundle; import android.os.Bundle;
import android.provider.DeviceConfig;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -48,6 +52,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace; import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsStore;
@@ -58,9 +63,11 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.DeviceFlag;
import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -84,6 +91,9 @@ public class HotseatPredictionController implements DragController.DragListener,
private static final String TAG = "PredictiveHotseat"; private static final String TAG = "PredictiveHotseat";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
public static final int MAX_ITEMS_FOR_MIGRATION = DeviceConfig.getInt(
DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5);
//TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543) //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
private static final int APPTARGET_ACTION_UNPIN = 4; private static final int APPTARGET_ACTION_UNPIN = 4;
@@ -112,8 +122,8 @@ public class HotseatPredictionController implements DragController.DragListener,
private HotseatEduController mHotseatEduController; private HotseatEduController mHotseatEduController;
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
private final View.OnLongClickListener mPredictionLongClickListener = v -> { private final View.OnLongClickListener mPredictionLongClickListener = v -> {
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false; if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
@@ -146,12 +156,12 @@ public class HotseatPredictionController implements DragController.DragListener,
} }
/** /**
* Transitions to NORMAL workspace mode and shows edu dialog * Transitions to NORMAL workspace mode and shows edu
*/ */
public void showEduDialog() { public void showEdu() {
if (mHotseatEduController == null) return; if (mHotseatEduController == null) return;
mLauncher.getStateManager().goToState(LauncherState.NORMAL, true, mLauncher.getStateManager().goToState(LauncherState.NORMAL, true,
() -> mHotseatEduController.showDialog()); () -> mHotseatEduController.showEdu());
} }
@Override @Override
@@ -277,7 +287,7 @@ public class HotseatPredictionController implements DragController.DragListener,
mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(), mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
this::setPredictedApps); this::setPredictedApps);
setPauseUIUpdate(false); setPauseUIUpdate(false);
performBetaCheck();
if (!isReady()) { if (!isReady()) {
mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor); mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
} }
@@ -325,7 +335,7 @@ public class HotseatPredictionController implements DragController.DragListener,
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache)); mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
} }
predictionLog.append("]"); predictionLog.append("]");
if (false) FileLog.d(TAG, predictionLog.toString()); if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
updateDependencies(); updateDependencies();
if (isReady()) { if (isReady()) {
fillGapsWithPrediction(); fillGapsWithPrediction();
@@ -589,6 +599,39 @@ public class HotseatPredictionController implements DragController.DragListener,
} }
} }
private void performBetaCheck() {
if (isReady()) return;
int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount();
// -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
> MAX_ITEMS_FOR_MIGRATION) {
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 = MAX_ITEMS_FOR_MIGRATION;
target.pageIndex = (workspaceItemCount * 1000) + hotseatItemsCount;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null);
}
}
/** /**
* Fill in predicted_rank field based on app prediction. * Fill in predicted_rank field based on app prediction.
* Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
@@ -184,8 +184,8 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
onStateOrResumeChanged(); onStateOrResumeChanged();
} }
if ((changeBits & ACTIVITY_STATE_STARTED) != 0 && mHotseatPredictionController != null if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
&& (getActivityFlags() & ACTIVITY_STATE_USER_ACTIVE) == 0) { || (changeBits & getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
mHotseatPredictionController.setPauseUIUpdate(false); mHotseatPredictionController.setPauseUIUpdate(false);
} }
} }
+10 -5
View File
@@ -78,16 +78,21 @@
<string name="hotseat_edu_message_migrate">Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen. </string> <string name="hotseat_edu_message_migrate">Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen. </string>
<string name="hotseat_edu_message_migrate_alt">Easily access your most-used apps, right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move to a new folder.</string> <string name="hotseat_edu_message_migrate_alt">Easily access your most-used apps, right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move to a new folder.</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string name="hotseat_items_migrated">Your hotseat items have been moved up to your homescreen</string>
<string name="hotseat_items_migrated_alt">Your hotseat items have been moved to a folder</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string name="hotseat_no_migration">Drag apps off the bottom row to see app suggestions</string>
<!-- Button text to opt in for fully predicted hotseat --> <!-- Button text to opt in for fully predicted hotseat -->
<string name="hotseat_edu_accept">Get app suggestions</string> <string name="hotseat_edu_accept">Get app suggestions</string>
<!-- Button text to dismiss opt in for fully predicted hotseat --> <!-- Button text to dismiss opt in for fully predicted hotseat -->
<string name="hotseat_edu_dismiss">No thanks</string> <string name="hotseat_edu_dismiss">No thanks</string>
<!-- action shown to turn of predictions after onboarding -->
<string name="hotseat_turn_off">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>
<!-- tip shown if user declines migration and has some open spots for prediction -->
<string name="hotseat_tip_no_empty_slots">Drag apps off the bottom row to get app suggestions</string>
<!-- tip shown if user declines migration and has no open spots for prediction -->
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space.</string>
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] --> <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
<string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string> <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>