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 7c4f3ecd01..f1ce72e708 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 @@ public class HotseatEduController { private final Launcher mLauncher; private final Hotseat mHotseat; + private final HotseatRestoreHelper mRestoreHelper; private List mPredictedApps; private HotseatEduDialog mActiveDialog; @@ -59,9 +60,10 @@ public class HotseatEduController { 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 @@ public class HotseatEduController { * 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 @@ public class HotseatEduController { */ private int migrateToFolder() { ArrayDeque folders = new ArrayDeque<>(); - ArrayList 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 fa137f814c..12d537cb28 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 05bcb57adc..725f516335 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 @@ public class HotseatPredictionController implements DragController.DragListener, private Launcher mLauncher; private final Hotseat mHotseat; + private final HotseatRestoreHelper mRestoreHelper; + private List mComponentKeyMappers = new ArrayList<>(); private DynamicItemCache mDynamicItemCache; @@ -129,6 +131,7 @@ public class HotseatPredictionController implements DragController.DragListener, 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 @@ public class HotseatPredictionController implements DragController.DragListener, }); setPauseUIUpdate(false); if (!isEduSeen()) { - mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor); + mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper, + this::createPredictor); } } @@ -320,9 +324,11 @@ public class HotseatPredictionController implements DragController.DragListener, updateDependencies(); bindItems(items, false, null); } - private void setPredictedApps(List appTargets) { mComponentKeyMappers.clear(); + if (appTargets.isEmpty() && mRestoreHelper.shouldRestoreToBackup()) { + mRestoreHelper.restoreBackup(); + } StringBuilder predictionLog = new StringBuilder("predictedApps: [\n"); ArrayList 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 0000000000..c95ff7a2d1 --- /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 77345a0468..35ff7929d1 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -83,6 +83,8 @@ Drag apps off the bottom row to get app suggestions App suggestions added to empty space + + App suggestions Enabled Predicted app: %1$s diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 48b97fafd3..e8b5568783 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -411,6 +411,11 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 @@ public class LauncherProvider extends ContentProvider { 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 2cb3910793..2a5ee001d5 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -99,6 +99,11 @@ public class LauncherSettings { */ 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 */ @@ -333,6 +338,8 @@ public class LauncherSettings { 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 4a1bc4df88..acfc339268 100644 --- a/src/com/android/launcher3/model/GridBackupTable.java +++ b/src/com/android/launcher3/model/GridBackupTable.java @@ -127,6 +127,32 @@ public class GridBackupTable { return mRestoredHotseatSize; } + /** + * 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. */