Restore hotseat when user turns off suggestions immediately after migration
- Creates a backup table `hybrid_hotseat_restore` and copies current workspace layout before hotseat migration
- restores to back up when user turns off suggestions and launcher receives empty predicted items
- deletes hybrid_hotseat_restore table if there's a layout change
Test: Manual
Bug: 157688471
Change-Id: Iaf7ddb33799493d36dbcd12408b57224162221d9
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 7c4f3ec..f1ce72e 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
@@ -52,6 +52,7 @@
private final Launcher mLauncher;
private final Hotseat mHotseat;
+ private final HotseatRestoreHelper mRestoreHelper;
private List<WorkspaceItemInfo> mPredictedApps;
private HotseatEduDialog mActiveDialog;
@@ -59,9 +60,10 @@
private IntArray mNewScreens = null;
private Runnable mOnOnboardingComplete;
- HotseatEduController(Launcher launcher, Runnable runnable) {
+ HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) {
mLauncher = launcher;
mHotseat = launcher.getHotseat();
+ mRestoreHelper = restoreHelper;
mOnOnboardingComplete = runnable;
}
@@ -69,11 +71,14 @@
* Checks what type of migration should be used and migrates hotseat
*/
void migrate() {
+ 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)));
}
/**
@@ -84,7 +89,6 @@
*/
private int migrateToFolder() {
ArrayDeque<FolderInfo> folders = new ArrayDeque<>();
-
ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
//separate folders and items that can get in folders
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 fa137f8..12d537c 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
@@ -16,7 +16,8 @@
package com.android.launcher3.hybridhotseat;
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.content.Context;
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 05bcb57..725f516 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
@@ -92,6 +92,8 @@
private Launcher mLauncher;
private final Hotseat mHotseat;
+ private final HotseatRestoreHelper mRestoreHelper;
+
private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
private DynamicItemCache mDynamicItemCache;
@@ -129,6 +131,7 @@
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
launcher.getDeviceProfile().inv.addOnChangeListener(this);
mHotseat.addOnAttachStateChangeListener(this);
+ mRestoreHelper = new HotseatRestoreHelper(mLauncher);
if (mHotseat.isAttachedToWindow()) {
onViewAttachedToWindow(mHotseat);
}
@@ -297,7 +300,8 @@
});
setPauseUIUpdate(false);
if (!isEduSeen()) {
- mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
+ mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper,
+ this::createPredictor);
}
}
@@ -320,9 +324,11 @@
updateDependencies();
bindItems(items, false, null);
}
-
private void setPredictedApps(List<AppTarget> appTargets) {
mComponentKeyMappers.clear();
+ if (appTargets.isEmpty() && mRestoreHelper.shouldRestoreToBackup()) {
+ mRestoreHelper.restoreBackup();
+ }
StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
ArrayList<ComponentKey> componentKeys = new ArrayList<>();
for (AppTarget appTarget : appTargets) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
new file mode 100644
index 0000000..c95ff7a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.hybridhotseat;
+
+import static com.android.launcher3.LauncherSettings.Favorites.HYBRID_HOTSEAT_BACKUP_TABLE;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.GridBackupTable;
+import com.android.launcher3.provider.LauncherDbUtils;
+
+/**
+ * A helper class to manage migration revert restoration for hybrid hotseat
+ */
+public class HotseatRestoreHelper {
+ private final Launcher mLauncher;
+ private boolean mBackupExists;
+
+ HotseatRestoreHelper(Launcher context) {
+ mLauncher = context;
+ setupBackupTable();
+ }
+
+ /**
+ * Creates a snapshot backup of Favorite table for future restoration use.
+ */
+ public synchronized void createBackup() {
+ try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
+ LauncherSettings.Settings.call(
+ mLauncher.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
+ .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
+ InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
+ GridBackupTable backupTable = new GridBackupTable(mLauncher,
+ transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+ idp.numRows);
+ backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
+ transaction.commit();
+ LauncherSettings.Settings.call(mLauncher.getContentResolver(),
+ LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE);
+ mBackupExists = true;
+ }
+ }
+
+ /**
+ * Finds and restores a previously saved snapshow of Favorites table
+ */
+ public void restoreBackup() {
+ try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
+ LauncherSettings.Settings.call(
+ mLauncher.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
+ .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
+ if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) {
+ mBackupExists = false;
+ return;
+ }
+ InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
+ GridBackupTable backupTable = new GridBackupTable(mLauncher,
+ transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+ idp.numRows);
+ backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
+ transaction.commit();
+ mBackupExists = false;
+ mLauncher.getModel().forceReload();
+ }
+ }
+
+ /**
+ * Returns if prediction controller should attempt restoring a backup
+ */
+ public synchronized boolean shouldRestoreToBackup() {
+ return mBackupExists;
+ }
+
+ private synchronized void setupBackupTable() {
+ try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
+ LauncherSettings.Settings.call(
+ mLauncher.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
+ .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
+ mBackupExists = tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE);
+ }
+ }
+}
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index be1d47b..ac7ff2a 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -83,6 +83,8 @@
<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 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>
<!-- 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/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 48b97fa..e8b5568 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -411,6 +411,11 @@
Favorites.BACKUP_TABLE_NAME);
return null;
}
+ case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: {
+ mOpenHelper.mHotseatRestoreTableExists = tableExists(
+ mOpenHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
+ return null;
+ }
case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: {
final long ts = System.currentTimeMillis();
if (ts - mLastRestoreTimestamp > RESTORE_BACKUP_TABLE_DELAY) {
@@ -609,6 +614,7 @@
private int mMaxItemId = -1;
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
+ private boolean mHotseatRestoreTableExists;
static DatabaseHelper createDatabaseHelper(Context context, boolean forMigration) {
return createDatabaseHelper(context, null, forMigration);
@@ -633,6 +639,8 @@
databaseHelper.mBackupTableExists = tableExists(
databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
}
+ databaseHelper.mHotseatRestoreTableExists = tableExists(
+ databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
databaseHelper.initIds();
return databaseHelper;
@@ -679,6 +687,10 @@
dropTable(db, Favorites.BACKUP_TABLE_NAME);
mBackupTableExists = false;
}
+ if (mHotseatRestoreTableExists) {
+ dropTable(db, Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
+ mHotseatRestoreTableExists = false;
+ }
}
/**
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 6789072..c30c401 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -100,6 +100,11 @@
public static final String BACKUP_TABLE_NAME = "favorites_bakup";
/**
+ * Backup table created when user hotseat is moved to workspace for hybrid hotseat
+ */
+ public static final String HYBRID_HOTSEAT_BACKUP_TABLE = "hotseat_restore_backup";
+
+ /**
* Temporary table used specifically for grid migrations during wallpaper preview
*/
public static final String PREVIEW_TABLE_NAME = "favorites_preview";
@@ -332,6 +337,8 @@
public static final String METHOD_REFRESH_BACKUP_TABLE = "refresh_backup_table";
+ public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table";
+
public static final String METHOD_RESTORE_BACKUP_TABLE = "restore_backup_table";
public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index 4a1bc4d..acfc339 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -128,6 +128,32 @@
}
/**
+ * Creates a new table and populates with copy of Favorites.TABLE_NAME
+ */
+ public void createCustomBackupTable(String tableName) {
+ long profileId = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
+ Process.myUserHandle());
+ copyTable(mDb, Favorites.TABLE_NAME, tableName, profileId);
+ encodeDBProperties(0);
+ }
+
+ /**
+ *
+ * Restores the contents of a custom table to Favorites.TABLE_NAME
+ */
+
+ public void restoreFromCustomBackupTable(String tableName, boolean dropAfterUse) {
+ if (!tableExists(mDb, tableName)) {
+ return;
+ }
+ long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
+ Process.myUserHandle());
+ copyTable(mDb, tableName, Favorites.TABLE_NAME, userSerial);
+ if (dropAfterUse) {
+ dropTable(mDb, tableName);
+ }
+ }
+ /**
* Copy valid grid entries from one table to another.
*/
private static void copyTable(SQLiteDatabase db, String from, String to, long userSerial) {