diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java deleted file mode 100644 index 56ce215d96..0000000000 --- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.android.launcher3.model; - - -import static android.database.DatabaseUtils.queryNumEntries; - -import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME; -import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; -import static com.android.launcher3.provider.LauncherDbUtils.tableExists; -import static com.android.launcher3.util.LauncherModelHelper.APP_ICON; -import static com.android.launcher3.util.LauncherModelHelper.DESKTOP; -import static com.android.launcher3.util.LauncherModelHelper.NO__ICON; -import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.content.ContentValues; -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Point; - -import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; -import com.android.launcher3.util.LauncherModelHelper; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -/** - * Unit tests for {@link GridBackupTable} - */ -@RunWith(RobolectricTestRunner.class) -public class GridBackupTableTest { - - private static final int BACKUP_ITEM_COUNT = 12; - - private LauncherModelHelper mModelHelper; - private Context mContext; - private SQLiteDatabase mDb; - - @Before - public void setUp() { - mModelHelper = new LauncherModelHelper(); - mContext = RuntimeEnvironment.application; - mDb = mModelHelper.provider.getDb(); - - setupGridData(); - } - - private void setupGridData() { - mModelHelper.createGrid(new int[][][]{{ - { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT}, - { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON}, - { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT}, - { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON}, - }}); - assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME)); - } - - @Test - public void backupTableCreated() { - GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4); - assertFalse(backupTable.backupOrRestoreAsNeeded()); - Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE); - - assertTrue(tableExists(mDb, BACKUP_TABLE_NAME)); - - // One extra entry for properties - assertEquals(BACKUP_ITEM_COUNT + 1, queryNumEntries(mDb, BACKUP_TABLE_NAME)); - } - - @Test - public void backupTableRestored() { - assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded()); - Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE); - - // Delete entries - mDb.delete(TABLE_NAME, null, null); - assertEquals(0, queryNumEntries(mDb, TABLE_NAME)); - - GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3); - assertTrue(backupTable.backupOrRestoreAsNeeded()); - - // Items have been restored - assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME)); - - Point outSize = new Point(); - assertEquals(4, backupTable.getRestoreHotseatAndGridSize(outSize)); - assertEquals(4, outSize.x); - assertEquals(4, outSize.y); - } - - @Test - public void backupTableRemovedOnAdd() { - assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded()); - Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE); - - assertTrue(tableExists(mDb, BACKUP_TABLE_NAME)); - - mModelHelper.addItem(1, 2, DESKTOP, 1, 1); - assertFalse(tableExists(mDb, BACKUP_TABLE_NAME)); - } - - @Test - public void backupTableRemovedOnDelete() { - assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded()); - Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE); - - assertTrue(tableExists(mDb, BACKUP_TABLE_NAME)); - - mContext.getContentResolver().delete(Favorites.CONTENT_URI, null, null); - assertFalse(tableExists(mDb, BACKUP_TABLE_NAME)); - } - - @Test - public void backupTableRetainedOnUpdate() { - assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded()); - Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE); - - assertTrue(tableExists(mDb, BACKUP_TABLE_NAME)); - - ContentValues values = new ContentValues(); - values.put(Favorites.RANK, 4); - // Something was updated - assertTrue(mContext.getContentResolver() - .update(Favorites.CONTENT_URI, values, null, null) > 0); - - // Backup table remains - assertTrue(tableExists(mDb, BACKUP_TABLE_NAME)); - } -} diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java deleted file mode 100644 index d544a0b5f3..0000000000 --- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java +++ /dev/null @@ -1,373 +0,0 @@ -package com.android.launcher3.model; - -import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds; -import static com.android.launcher3.util.LauncherModelHelper.APP_ICON; -import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT; -import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT; -import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Point; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherSettings; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask; -import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.LauncherModelHelper; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.HashSet; -import java.util.LinkedList; - -/** - * Unit tests for {@link GridSizeMigrationTask} - */ -@RunWith(RobolectricTestRunner.class) -public class GridSizeMigrationTaskTest { - - private LauncherModelHelper mModelHelper; - private Context mContext; - private SQLiteDatabase mDb; - - private HashSet mValidPackages; - private InvariantDeviceProfile mIdp; - - @Before - public void setUp() { - mModelHelper = new LauncherModelHelper(); - mContext = RuntimeEnvironment.application; - mDb = mModelHelper.provider.getDb(); - - mValidPackages = new HashSet<>(); - mValidPackages.add(TEST_PACKAGE); - mIdp = InvariantDeviceProfile.INSTANCE.get(mContext); - } - - @Test - public void testHotseatMigration_apps_dropped() throws Exception { - int[] hotseatItems = { - mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0), - mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0), - -1, - mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0), - mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0), - }; - - mIdp.numDatabaseHotseatIcons = 3; - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3) - .migrateHotseat(); - // First item is dropped as it has the least weight. - verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]); - } - - @Test - public void testHotseatMigration_shortcuts_dropped() throws Exception { - int[] hotseatItems = { - mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0), - mModelHelper.addItem(30, 1, HOTSEAT, 0, 0), - -1, - mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0), - mModelHelper.addItem(10, 4, HOTSEAT, 0, 0), - }; - - mIdp.numDatabaseHotseatIcons = 3; - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3) - .migrateHotseat(); - // First item is dropped as it has the least weight. - verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]); - } - - private void verifyHotseat(int... sortedIds) { - int screenId = 0; - int total = 0; - - for (int id : sortedIds) { - Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, - new String[]{LauncherSettings.Favorites._ID}, - "container=-101 and screen=" + screenId, null, null, null); - - if (id == -1) { - assertEquals(0, c.getCount()); - } else { - assertEquals(1, c.getCount()); - c.moveToNext(); - assertEquals(id, c.getLong(0)); - total ++; - } - c.close(); - - screenId++; - } - - // Verify that not other entry exist in the DB. - Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, - new String[]{LauncherSettings.Favorites._ID}, - "container=-101", null, null, null); - assertEquals(total, c.getCount()); - c.close(); - } - - @Test - public void testWorkspace_empty_row_column_removed() throws Exception { - int[][][] ids = mModelHelper.createGrid(new int[][][]{{ - { 0, 0, -1, 1}, - { 3, 1, -1, 4}, - { -1, -1, -1, -1}, - { 5, 2, -1, 6}, - }}); - - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, - new Point(4, 4), new Point(3, 3)).migrateWorkspace(); - - // Column 2 and row 2 got removed. - verifyWorkspace(new int[][][] {{ - {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, - {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, - {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, - }}); - } - - @Test - public void testWorkspace_new_screen_created() throws Exception { - int[][][] ids = mModelHelper.createGrid(new int[][][]{{ - { 0, 0, 0, 1}, - { 3, 1, 0, 4}, - { -1, -1, -1, -1}, - { 5, 2, -1, 6}, - }}); - - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, - new Point(4, 4), new Point(3, 3)).migrateWorkspace(); - - // Items in the second column get moved to new screen - verifyWorkspace(new int[][][] {{ - {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, - {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, - {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, - }, { - {ids[0][0][2], ids[0][1][2], -1}, - }}); - } - - @Test - public void testWorkspace_items_merged_in_next_screen() throws Exception { - int[][][] ids = mModelHelper.createGrid(new int[][][]{{ - { 0, 0, 0, 1}, - { 3, 1, 0, 4}, - { -1, -1, -1, -1}, - { 5, 2, -1, 6}, - },{ - { 0, 0, -1, 1}, - { 3, 1, -1, 4}, - }}); - - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, - new Point(4, 4), new Point(3, 3)).migrateWorkspace(); - - // Items in the second column of the first screen should get placed on the 3rd - // row of the second screen - verifyWorkspace(new int[][][] {{ - {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, - {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, - {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, - }, { - {ids[1][0][0], ids[1][0][1], ids[1][0][3]}, - {ids[1][1][0], ids[1][1][1], ids[1][1][3]}, - {ids[0][0][2], ids[0][1][2], -1}, - }}); - } - - @Test - public void testWorkspace_items_not_merged_in_next_screen() throws Exception { - // First screen has 2 mItems that need to be moved, but second screen has only one - // empty space after migration (top-left corner) - int[][][] ids = mModelHelper.createGrid(new int[][][]{{ - { 0, 0, 0, 1}, - { 3, 1, 0, 4}, - { -1, -1, -1, -1}, - { 5, 2, -1, 6}, - },{ - { -1, 0, -1, 1}, - { 3, 1, -1, 4}, - { -1, -1, -1, -1}, - { 5, 2, -1, 6}, - }}); - - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, - new Point(4, 4), new Point(3, 3)).migrateWorkspace(); - - // Items in the second column of the first screen should get placed on a new screen. - verifyWorkspace(new int[][][] {{ - {ids[0][0][0], ids[0][0][1], ids[0][0][3]}, - {ids[0][1][0], ids[0][1][1], ids[0][1][3]}, - {ids[0][3][0], ids[0][3][1], ids[0][3][3]}, - }, { - { -1, ids[1][0][1], ids[1][0][3]}, - {ids[1][1][0], ids[1][1][1], ids[1][1][3]}, - {ids[1][3][0], ids[1][3][1], ids[1][3][3]}, - }, { - {ids[0][0][2], ids[0][1][2], -1}, - }}); - } - - @Test - public void testWorkspace_first_row_blocked() throws Exception { - if (!FeatureFlags.QSB_ON_FIRST_SCREEN) { - return; - } - // The first screen has one item on the 4th column which needs moving, as the first row - // will be kept empty. - int[][][] ids = mModelHelper.createGrid(new int[][][]{{ - { -1, -1, -1, -1}, - { 3, 1, 7, 0}, - { 8, 7, 7, -1}, - { 5, 2, 7, -1}, - }}, 0); - - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, - new Point(4, 4), new Point(3, 4)).migrateWorkspace(); - - // Items in the second column of the first screen should get placed on a new screen. - verifyWorkspace(new int[][][] {{ - { -1, -1, -1}, - {ids[0][1][0], ids[0][1][1], ids[0][1][2]}, - {ids[0][2][0], ids[0][2][1], ids[0][2][2]}, - {ids[0][3][0], ids[0][3][1], ids[0][3][2]}, - }, { - {ids[0][1][3]}, - }}); - } - - @Test - public void testWorkspace_items_moved_to_empty_first_row() throws Exception { - if (!FeatureFlags.QSB_ON_FIRST_SCREEN) { - return; - } - // Items will get moved to the next screen to keep the first screen empty. - int[][][] ids = mModelHelper.createGrid(new int[][][]{{ - { -1, -1, -1, -1}, - { 0, 1, 0, 0}, - { 8, 7, 7, -1}, - { 5, 6, 7, -1}, - }}, 0); - - new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, - new Point(4, 4), new Point(3, 3)).migrateWorkspace(); - - // Items in the second column of the first screen should get placed on a new screen. - verifyWorkspace(new int[][][] {{ - { -1, -1, -1}, - {ids[0][2][0], ids[0][2][1], ids[0][2][2]}, - {ids[0][3][0], ids[0][3][1], ids[0][3][2]}, - }, { - {ids[0][1][1], ids[0][1][0], ids[0][1][2]}, - {ids[0][1][3]}, - }}); - } - - /** - * Verifies that the workspace mItems are arranged in the provided order. - * @param ids A 3d array where the first dimension represents the screen, and the rest two - * represent the workspace grid. - */ - private void verifyWorkspace(int[][][] ids) { - IntArray allScreens = getWorkspaceScreenIds(mDb, LauncherSettings.Favorites.TABLE_NAME); - assertEquals(ids.length, allScreens.size()); - int total = 0; - - for (int i = 0; i < ids.length; i++) { - int screenId = allScreens.get(i); - for (int y = 0; y < ids[i].length; y++) { - for (int x = 0; x < ids[i][y].length; x++) { - int id = ids[i][y][x]; - - Cursor c = mContext.getContentResolver().query( - LauncherSettings.Favorites.CONTENT_URI, - new String[]{LauncherSettings.Favorites._ID}, - "container=-100 and screen=" + screenId + - " and cellX=" + x + " and cellY=" + y, null, null, null); - if (id == -1) { - assertEquals(0, c.getCount()); - } else { - assertEquals(1, c.getCount()); - c.moveToNext(); - assertEquals(String.format("Failed to verify item at %d %d, %d", i, y, x), - id, c.getLong(0)); - total++; - } - c.close(); - } - } - } - - // Verify that not other entry exist in the DB. - Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, - new String[]{LauncherSettings.Favorites._ID}, - "container=-100", null, null, null); - assertEquals(total, c.getCount()); - c.close(); - } - - @Test - public void testMultiStepMigration_small_to_large() throws Exception { - MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(); - verifier.migrate(new Point(3, 3), new Point(5, 5)); - verifier.assertCompleted(); - } - - @Test - public void testMultiStepMigration_large_to_small() throws Exception { - MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier( - 5, 5, 4, 4, - 4, 4, 3, 4 - ); - verifier.migrate(new Point(5, 5), new Point(3, 4)); - verifier.assertCompleted(); - } - - @Test - public void testMultiStepMigration_zig_zag() throws Exception { - MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier( - 5, 7, 4, 7, - 4, 7, 3, 7 - ); - verifier.migrate(new Point(5, 5), new Point(3, 7)); - verifier.assertCompleted(); - } - - private static class MultiStepMigrationTaskVerifier extends MultiStepMigrationTask { - - private final LinkedList mPoints; - - public MultiStepMigrationTaskVerifier(int... points) { - super(null, null, null, false); - - mPoints = new LinkedList<>(); - for (int i = 0; i < points.length; i += 2) { - mPoints.add(new Point(points[i], points[i + 1])); - } - } - - @Override - protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception { - assertEquals(sourceSize, mPoints.poll()); - assertEquals(nextSize, mPoints.poll()); - return false; - } - - public void assertCompleted() { - assertTrue(mPoints.isEmpty()); - } - } -} diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index a8ed6bcee2..95a8d81993 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO; import static com.android.launcher3.provider.LauncherDbUtils.copyTable; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import static com.android.launcher3.provider.LauncherDbUtils.tableExists; @@ -433,32 +432,26 @@ public class LauncherProvider extends ContentProvider { return null; } case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: { - if (MULTI_DB_GRID_MIRATION_ALGO.get()) { - Bundle result = new Bundle(); - result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, - prepForMigration( - InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile, - Favorites.TMP_TABLE, - () -> mOpenHelper, - () -> DatabaseHelper.createDatabaseHelper( - getContext(), true /* forMigration */))); - return result; - } - return null; + Bundle result = new Bundle(); + result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, + prepForMigration( + InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile, + Favorites.TMP_TABLE, + () -> mOpenHelper, + () -> DatabaseHelper.createDatabaseHelper( + getContext(), true /* forMigration */))); + return result; } case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: { - if (MULTI_DB_GRID_MIRATION_ALGO.get()) { - Bundle result = new Bundle(); - result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, - prepForMigration( - arg /* dbFile */, - Favorites.PREVIEW_TABLE_NAME, - () -> DatabaseHelper.createDatabaseHelper( - getContext(), arg, true /* forMigration */), - () -> mOpenHelper)); - return result; - } - return null; + Bundle result = new Bundle(); + result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, + prepForMigration( + arg /* dbFile */, + Favorites.PREVIEW_TABLE_NAME, + () -> DatabaseHelper.createDatabaseHelper( + getContext(), arg, true /* forMigration */), + () -> mOpenHelper)); + return result; } case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: { if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null; @@ -654,8 +647,7 @@ public class LauncherProvider extends ContentProvider { static DatabaseHelper createDatabaseHelper(Context context, String dbName, boolean forMigration) { if (dbName == null) { - dbName = MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get( - context).dbFile : LauncherFiles.LAUNCHER_DB; + dbName = InvariantDeviceProfile.INSTANCE.get(context).dbFile; } DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName, forMigration); // Table creation sometimes fails silently, which leads to a crash loop. @@ -666,10 +658,6 @@ public class LauncherProvider extends ContentProvider { // This operation is a no-op if the table already exists. databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true); } - if (!MULTI_DB_GRID_MIRATION_ALGO.get()) { - databaseHelper.mBackupTableExists = tableExists( - databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME); - } databaseHelper.mHotseatRestoreTableExists = tableExists( databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE); @@ -851,11 +839,7 @@ public class LauncherProvider extends ContentProvider { case 25: convertShortcutsToLauncherActivities(db); case 26: - // QSB was moved to the grid. Clear the first row on screen 0. - if (FeatureFlags.QSB_ON_FIRST_SCREEN && - !LauncherDbUtils.prepareScreenZeroToHostQsb(mContext, db)) { - break; - } + // QSB was moved to the grid. Ignore overlapping items case 27: { // Update the favorites table so that the screen ids are ordered based on // workspace page rank. diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 9b71918ef9..33ee95162a 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -144,9 +144,6 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag( "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache"); - public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag( - "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm"); - public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag( "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace"); diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java index d0464a4883..c7448dc00a 100644 --- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java +++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java @@ -16,7 +16,6 @@ package com.android.launcher3.graphics; -import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -47,7 +46,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext; import com.android.launcher3.model.BgDataModel; -import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.GridSizeMigrationTaskV2; import com.android.launcher3.model.LoaderTask; import com.android.launcher3.model.ModelDelegate; @@ -198,16 +196,10 @@ public class PreviewSurfaceRenderer { @WorkerThread private boolean doGridMigrationIfNecessary() { - boolean needsToMigrate = - MULTI_DB_GRID_MIRATION_ALGO.get() - ? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp) - : GridSizeMigrationTask.needsToMigrate(mContext, mIdp); - if (!needsToMigrate) { + if (!GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)) { return false; } - return MULTI_DB_GRID_MIRATION_ALGO.get() - ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp) - : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp); + return GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp); } @UiThread diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java index acfc339268..51cbf4b770 100644 --- a/src/com/android/launcher3/model/GridBackupTable.java +++ b/src/com/android/launcher3/model/GridBackupTable.java @@ -23,14 +23,12 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.graphics.Point; import android.os.Process; import android.util.Log; import androidx.annotation.IntDef; import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.pm.UserCache; /** @@ -84,49 +82,6 @@ public class GridBackupTable { mOldGridY = gridY; } - /** - * Create a backup from current workspace layout if one isn't created already (Note backup - * created this way is always sanitized). Otherwise restore from the backup instead. - */ - public boolean backupOrRestoreAsNeeded() { - // Check if backup table exists - if (!tableExists(mDb, BACKUP_TABLE_NAME)) { - if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED) - .getBoolean(Settings.EXTRA_VALUE, false)) { - // No need to copy if empty DB was created. - return false; - } - doBackup(UserCache.INSTANCE.get(mContext).getSerialNumberForUser( - Process.myUserHandle()), 0); - return false; - } - return restoreIfBackupExists(Favorites.TABLE_NAME); - } - - public boolean restoreToPreviewIfBackupExists() { - if (!tableExists(mDb, BACKUP_TABLE_NAME)) { - return false; - } - - return restoreIfBackupExists(Favorites.PREVIEW_TABLE_NAME); - } - - private boolean restoreIfBackupExists(String toTableName) { - if (loadDBProperties() != STATE_SANITIZED) { - return false; - } - long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser( - Process.myUserHandle()); - copyTable(mDb, BACKUP_TABLE_NAME, toTableName, userSerial); - Log.d(TAG, "Backup table found"); - return true; - } - - public int getRestoreHotseatAndGridSize(Point outGridSize) { - outGridSize.set(mRestoredGridX, mRestoredGridY); - return mRestoredHotseatSize; - } - /** * Creates a new table and populates with copy of Favorites.TABLE_NAME */ diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java deleted file mode 100644 index 7b3e509d52..0000000000 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ /dev/null @@ -1,1098 +0,0 @@ -package com.android.launcher3.model; - -import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT; -import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE; -import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE; -import static com.android.launcher3.Utilities.getPointString; -import static com.android.launcher3.Utilities.parsePoint; -import static com.android.launcher3.provider.LauncherDbUtils.copyTable; - -import android.content.ComponentName; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Point; -import android.os.SystemClock; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.VisibleForTesting; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; -import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; -import com.android.launcher3.Utilities; -import com.android.launcher3.Workspace; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.graphics.LauncherPreviewRenderer; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.pm.InstallSessionHelper; -import com.android.launcher3.provider.LauncherDbUtils; -import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; -import com.android.launcher3.util.GridOccupancy; -import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.IntSparseArrayMap; -import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; -import com.android.launcher3.widget.WidgetManagerHelper; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; - -/** - * This class takes care of shrinking the workspace (by maximum of one row and one column), as a - * result of restoring from a larger device or device density change. - */ -public class GridSizeMigrationTask { - - private static final String TAG = "GridSizeMigrationTask"; - private static final boolean DEBUG = false; - - // These are carefully selected weights for various item types (Math.random?), to allow for - // the least absurd migration experience. - private static final float WT_SHORTCUT = 1; - private static final float WT_APPLICATION = 0.8f; - private static final float WT_WIDGET_MIN = 2; - private static final float WT_WIDGET_FACTOR = 0.6f; - private static final float WT_FOLDER_FACTOR = 0.5f; - - protected final SQLiteDatabase mDb; - protected final Context mContext; - - protected final IntArray mEntryToRemove = new IntArray(); - protected final ArrayList mCarryOver = new ArrayList<>(); - - private final SparseArray mUpdateOperations = new SparseArray<>(); - private final HashSet mValidPackages; - private final String mTableName; - - private final int mSrcX, mSrcY; - private final int mTrgX, mTrgY; - private final boolean mShouldRemoveX, mShouldRemoveY; - - private final int mSrcHotseatSize; - private final int mDestHotseatSize; - - protected GridSizeMigrationTask(Context context, SQLiteDatabase db, - HashSet validPackages, boolean usePreviewTable, Point sourceSize, - Point targetSize) { - mContext = context; - mDb = db; - mValidPackages = validPackages; - mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME; - - mSrcX = sourceSize.x; - mSrcY = sourceSize.y; - - mTrgX = targetSize.x; - mTrgY = targetSize.y; - - mShouldRemoveX = mTrgX < mSrcX; - mShouldRemoveY = mTrgY < mSrcY; - - // Non-used variables - mSrcHotseatSize = mDestHotseatSize = -1; - } - - protected GridSizeMigrationTask(Context context, SQLiteDatabase db, - HashSet validPackages, boolean usePreviewTable, int srcHotseatSize, - int destHotseatSize) { - mContext = context; - mDb = db; - mValidPackages = validPackages; - mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME; - - mSrcHotseatSize = srcHotseatSize; - - mDestHotseatSize = destHotseatSize; - - // Non-used variables - mSrcX = mSrcY = mTrgX = mTrgY = -1; - mShouldRemoveX = mShouldRemoveY = false; - } - - /** - * Applied all the pending DB operations - * - * @return true if any DB operation was commited. - */ - private boolean applyOperations() throws Exception { - // Update items - int updateCount = mUpdateOperations.size(); - for (int i = 0; i < updateCount; i++) { - mDb.update(mTableName, mUpdateOperations.valueAt(i), - "_id=" + mUpdateOperations.keyAt(i), null); - } - - if (!mEntryToRemove.isEmpty()) { - if (DEBUG) { - Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString()); - } - mDb.delete(mTableName, Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), - null); - } - - return updateCount > 0 || !mEntryToRemove.isEmpty(); - } - - /** - * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them - * in the order in the new hotseat while keeping an empty space for all-apps. If the number of - * entries is more than what can fit in the new hotseat, we drop the entries with least weight. - * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION} - * & {@see #WT_FOLDER_FACTOR}. - * - * @return true if any DB change was made - */ - protected boolean migrateHotseat() throws Exception { - ArrayList items = loadHotseatEntries(); - while (items.size() > mDestHotseatSize) { - // Pick the center item by default. - DbEntry toRemove = items.get(items.size() / 2); - - // Find the item with least weight. - for (DbEntry entry : items) { - if (entry.weight < toRemove.weight) { - toRemove = entry; - } - } - - mEntryToRemove.add(toRemove.id); - items.remove(toRemove); - } - - // Update screen IDS - int newScreenId = 0; - for (DbEntry entry : items) { - if (entry.screenId != newScreenId) { - entry.screenId = newScreenId; - - // These values does not affect the item position, but we should set them - // to something other than -1. - entry.cellX = newScreenId; - entry.cellY = 0; - - update(entry); - } - - newScreenId++; - } - - return applyOperations(); - } - - @VisibleForTesting - static IntArray getWorkspaceScreenIds(SQLiteDatabase db, String tableName) { - return LauncherDbUtils.queryIntArray(db, tableName, Favorites.SCREEN, - Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP, - Favorites.SCREEN, Favorites.SCREEN); - } - - /** - * @return true if any DB change was made - */ - protected boolean migrateWorkspace() throws Exception { - IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName); - if (allScreens.isEmpty()) { - throw new Exception("Unable to get workspace screens"); - } - - for (int i = 0; i < allScreens.size(); i++) { - int screenId = allScreens.get(i); - if (DEBUG) { - Log.d(TAG, "Migrating " + screenId); - } - migrateScreen(screenId); - } - - if (!mCarryOver.isEmpty()) { - IntSparseArrayMap itemMap = new IntSparseArrayMap<>(); - for (DbEntry e : mCarryOver) { - itemMap.put(e.id, e); - } - - do { - // Some items are still remaining. Try adding a few new screens. - - // At every iteration, make sure that at least one item is removed from - // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed, - // break the loop and abort migration by throwing an exception. - OptimalPlacementSolution placement = new OptimalPlacementSolution( - new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true); - placement.find(); - if (placement.finalPlacedItems.size() > 0) { - int newScreenId = LauncherSettings.Settings.call( - mContext.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(EXTRA_VALUE); - for (DbEntry item : placement.finalPlacedItems) { - if (!mCarryOver.remove(itemMap.get(item.id))) { - throw new Exception("Unable to find matching items"); - } - item.screenId = newScreenId; - update(item); - } - } else { - throw new Exception("None of the items can be placed on an empty screen"); - } - - } while (!mCarryOver.isEmpty()); - } - return applyOperations(); - } - - /** - * Migrate a particular screen id. - * Strategy: - * 1) For all possible combinations of row and column, pick the one which causes the least - * data loss: {@link #tryRemove(int, int, int, ArrayList, float[])} - * 2) Maintain a list of all lost items before this screen, and add any new item lost from - * this screen to that list as well. - * 3) If all those items from the above list can be placed on this screen, place them - * (otherwise they are placed on a new screen). - */ - protected void migrateScreen(int screenId) { - // If we are migrating the first screen, do not touch the first row. - int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID) - ? 1 : 0; - - ArrayList items = loadWorkspaceEntries(screenId); - - int removedCol = Integer.MAX_VALUE; - int removedRow = Integer.MAX_VALUE; - - // removeWt represents the cost function for loss of items during migration, and moveWt - // represents the cost function for repositioning the items. moveWt is only considered if - // removeWt is same for two different configurations. - // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least - // cost. - float removeWt = Float.MAX_VALUE; - float moveWt = Float.MAX_VALUE; - float[] outLoss = new float[2]; - ArrayList finalItems = null; - - // Try removing all possible combinations - for (int x = 0; x < mSrcX; x++) { - // Try removing the rows first from bottom. This keeps the workspace - // nicely aligned with hotseat. - for (int y = mSrcY - 1; y >= startY; y--) { - // Use a deep copy when trying out a particular combination as it can change - // the underlying object. - ArrayList itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), - outLoss); - - if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] - < moveWt))) { - removeWt = outLoss[0]; - moveWt = outLoss[1]; - removedCol = mShouldRemoveX ? x : removedCol; - removedRow = mShouldRemoveY ? y : removedRow; - finalItems = itemsOnScreen; - } - - // No need to loop over all rows, if a row removal is not needed. - if (!mShouldRemoveY) { - break; - } - } - - if (!mShouldRemoveX) { - break; - } - } - - if (DEBUG) { - Log.d(TAG, String.format("Removing row %d, column %d on screen %d", - removedRow, removedCol, screenId)); - } - - IntSparseArrayMap itemMap = new IntSparseArrayMap<>(); - for (DbEntry e : deepCopy(items)) { - itemMap.put(e.id, e); - } - - for (DbEntry item : finalItems) { - DbEntry org = itemMap.get(item.id); - itemMap.remove(item.id); - - // Check if update is required - if (!item.columnsSame(org)) { - update(item); - } - } - - // The remaining items in {@link #itemMap} are those which didn't get placed. - for (DbEntry item : itemMap) { - mCarryOver.add(item); - } - - if (!mCarryOver.isEmpty() && removeWt == 0) { - // No new items were removed in this step. Try placing all the items on this screen. - GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY); - occupied.markCells(0, 0, mTrgX, startY, true); - for (DbEntry item : finalItems) { - occupied.markCells(item, true); - } - - OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, - deepCopy(mCarryOver), startY, true); - placement.find(); - if (placement.lowestWeightLoss == 0) { - // All items got placed - - for (DbEntry item : placement.finalPlacedItems) { - item.screenId = screenId; - update(item); - } - - mCarryOver.clear(); - } - } - } - - /** - * Updates an item in the DB. - */ - protected void update(DbEntry item) { - ContentValues values = new ContentValues(); - item.addToContentValues(values); - mUpdateOperations.put(item.id, values); - } - - /** - * Tries the remove the provided row and column. - * - * @param items all the items on the screen under operation - * @param outLoss array of size 2. The first entry is filled with weight loss, and the second - * with the overall item movement. - */ - private ArrayList tryRemove(int col, int row, int startY, - ArrayList items, float[] outLoss) { - GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY); - occupied.markCells(0, 0, mTrgX, startY, true); - - col = mShouldRemoveX ? col : Integer.MAX_VALUE; - row = mShouldRemoveY ? row : Integer.MAX_VALUE; - - ArrayList finalItems = new ArrayList<>(); - ArrayList removedItems = new ArrayList<>(); - - for (DbEntry item : items) { - if ((item.cellX <= col && (item.spanX + item.cellX) > col) - || (item.cellY <= row && (item.spanY + item.cellY) > row)) { - removedItems.add(item); - if (item.cellX >= col) item.cellX--; - if (item.cellY >= row) item.cellY--; - } else { - if (item.cellX > col) item.cellX--; - if (item.cellY > row) item.cellY--; - finalItems.add(item); - occupied.markCells(item, true); - } - } - - OptimalPlacementSolution placement = - new OptimalPlacementSolution(occupied, removedItems, startY); - placement.find(); - finalItems.addAll(placement.finalPlacedItems); - outLoss[0] = placement.lowestWeightLoss; - outLoss[1] = placement.lowestMoveCost; - return finalItems; - } - - private class OptimalPlacementSolution { - private final ArrayList itemsToPlace; - private final GridOccupancy occupied; - - // If set to true, item movement are not considered in move cost, leading to a more - // linear placement. - private final boolean ignoreMove; - - // The first row in the grid from where the placement should start. - private final int startY; - - float lowestWeightLoss = Float.MAX_VALUE; - float lowestMoveCost = Float.MAX_VALUE; - ArrayList finalPlacedItems; - - public OptimalPlacementSolution( - GridOccupancy occupied, ArrayList itemsToPlace, int startY) { - this(occupied, itemsToPlace, startY, false); - } - - public OptimalPlacementSolution(GridOccupancy occupied, ArrayList itemsToPlace, - int startY, boolean ignoreMove) { - this.occupied = occupied; - this.itemsToPlace = itemsToPlace; - this.ignoreMove = ignoreMove; - this.startY = startY; - - // Sort the items such that larger widgets appear first followed by 1x1 items - Collections.sort(this.itemsToPlace); - } - - public void find() { - find(0, 0, 0, new ArrayList()); - } - - /** - * Recursively finds a placement for the provided items. - * - * @param index the position in {@link #itemsToPlace} to start looking at. - * @param weightLoss total weight loss upto this point - * @param moveCost total move cost upto this point - * @param itemsPlaced all the items already placed upto this point - */ - public void find(int index, float weightLoss, float moveCost, - ArrayList itemsPlaced) { - if ((weightLoss >= lowestWeightLoss) || - ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) { - // Abort, as we already have a better solution. - return; - - } else if (index >= itemsToPlace.size()) { - // End loop. - lowestWeightLoss = weightLoss; - lowestMoveCost = moveCost; - - // Keep a deep copy of current configuration as it can change during recursion. - finalPlacedItems = deepCopy(itemsPlaced); - return; - } - - DbEntry me = itemsToPlace.get(index); - int myX = me.cellX; - int myY = me.cellY; - - // List of items to pass over if this item was placed. - ArrayList itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1); - itemsIncludingMe.addAll(itemsPlaced); - itemsIncludingMe.add(me); - - if (me.spanX > 1 || me.spanY > 1) { - // If the current item is a widget (and it greater than 1x1), try to place it at - // all possible positions. This is because a widget placed at one position can - // affect the placement of a different widget. - int myW = me.spanX; - int myH = me.spanY; - - for (int y = startY; y < mTrgY; y++) { - for (int x = 0; x < mTrgX; x++) { - float newMoveCost = moveCost; - if (x != myX) { - me.cellX = x; - newMoveCost++; - } - if (y != myY) { - me.cellY = y; - newMoveCost++; - } - if (ignoreMove) { - newMoveCost = moveCost; - } - - if (occupied.isRegionVacant(x, y, myW, myH)) { - // place at this position and continue search. - occupied.markCells(me, true); - find(index + 1, weightLoss, newMoveCost, itemsIncludingMe); - occupied.markCells(me, false); - } - - // Try resizing horizontally - if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) { - me.spanX--; - occupied.markCells(me, true); - // 1 extra move cost - find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe); - occupied.markCells(me, false); - me.spanX++; - } - - // Try resizing vertically - if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) { - me.spanY--; - occupied.markCells(me, true); - // 1 extra move cost - find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe); - occupied.markCells(me, false); - me.spanY++; - } - - // Try resizing horizontally & vertically - if (myH > me.minSpanY && myW > me.minSpanX && - occupied.isRegionVacant(x, y, myW - 1, myH - 1)) { - me.spanX--; - me.spanY--; - occupied.markCells(me, true); - // 2 extra move cost - find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe); - occupied.markCells(me, false); - me.spanX++; - me.spanY++; - } - me.cellX = myX; - me.cellY = myY; - } - } - - // Finally also try a solution when this item is not included. Trying it in the end - // causes it to get skipped in most cases due to higher weight loss, and prevents - // unnecessary deep copies of various configurations. - find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced); - } else { - // Since this is a 1x1 item and all the following items are also 1x1, just place - // it at 'the most appropriate position' and hope for the best. - // The most appropriate position: one with lease straight line distance - int newDistance = Integer.MAX_VALUE; - int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE; - - for (int y = startY; y < mTrgY; y++) { - for (int x = 0; x < mTrgX; x++) { - if (!occupied.cells[x][y]) { - int dist = ignoreMove ? 0 : - ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - - y)); - if (dist < newDistance) { - newX = x; - newY = y; - newDistance = dist; - } - } - } - } - - if (newX < mTrgX && newY < mTrgY) { - float newMoveCost = moveCost; - if (newX != myX) { - me.cellX = newX; - newMoveCost++; - } - if (newY != myY) { - me.cellY = newY; - newMoveCost++; - } - if (ignoreMove) { - newMoveCost = moveCost; - } - occupied.markCells(me, true); - find(index + 1, weightLoss, newMoveCost, itemsIncludingMe); - occupied.markCells(me, false); - me.cellX = myX; - me.cellY = myY; - - // Try to find a solution without this item, only if - // 1) there was at least one space, i.e., we were able to place this item - // 2) if the next item has the same weight (all items are already sorted), as - // if it has lower weight, that solution will automatically get discarded. - // 3) ignoreMove false otherwise, move cost is ignored and the weight will - // anyway be same. - if (index + 1 < itemsToPlace.size() - && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) { - find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced); - } - } else { - // No more space. Jump to the end. - for (int i = index + 1; i < itemsToPlace.size(); i++) { - weightLoss += itemsToPlace.get(i).weight; - } - find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced); - } - } - } - } - - private ArrayList loadHotseatEntries() { - Cursor c = queryWorkspace( - new String[]{ - Favorites._ID, // 0 - Favorites.ITEM_TYPE, // 1 - Favorites.INTENT, // 2 - Favorites.SCREEN}, // 3 - Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT); - - final int indexId = c.getColumnIndexOrThrow(Favorites._ID); - final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); - final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT); - final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN); - - ArrayList entries = new ArrayList<>(); - while (c.moveToNext()) { - DbEntry entry = new DbEntry(); - entry.id = c.getInt(indexId); - entry.itemType = c.getInt(indexItemType); - entry.screenId = c.getInt(indexScreen); - - if (entry.screenId >= mSrcHotseatSize) { - mEntryToRemove.add(entry.id); - continue; - } - - try { - // calculate weight - switch (entry.itemType) { - case Favorites.ITEM_TYPE_SHORTCUT: - case Favorites.ITEM_TYPE_DEEP_SHORTCUT: - case Favorites.ITEM_TYPE_APPLICATION: { - verifyIntent(c.getString(indexIntent)); - entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ? - WT_APPLICATION : WT_SHORTCUT; - break; - } - case Favorites.ITEM_TYPE_FOLDER: { - int total = getFolderItemsCount(entry.id); - if (total == 0) { - throw new Exception("Folder is empty"); - } - entry.weight = WT_FOLDER_FACTOR * total; - break; - } - default: - throw new Exception("Invalid item type"); - } - } catch (Exception e) { - if (DEBUG) { - Log.d(TAG, "Removing item " + entry.id, e); - } - mEntryToRemove.add(entry.id); - continue; - } - entries.add(entry); - } - c.close(); - return entries; - } - - - /** - * Loads entries for a particular screen id. - */ - protected ArrayList loadWorkspaceEntries(int screen) { - Cursor c = queryWorkspace( - new String[]{ - Favorites._ID, // 0 - Favorites.ITEM_TYPE, // 1 - Favorites.CELLX, // 2 - Favorites.CELLY, // 3 - Favorites.SPANX, // 4 - Favorites.SPANY, // 5 - Favorites.INTENT, // 6 - Favorites.APPWIDGET_PROVIDER, // 7 - Favorites.APPWIDGET_ID}, // 8 - Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP - + " AND " + Favorites.SCREEN + " = " + screen); - - final int indexId = c.getColumnIndexOrThrow(Favorites._ID); - final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); - final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX); - final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY); - final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX); - final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY); - final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT); - final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); - final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID); - - ArrayList entries = new ArrayList<>(); - WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(mContext); - while (c.moveToNext()) { - DbEntry entry = new DbEntry(); - entry.id = c.getInt(indexId); - entry.itemType = c.getInt(indexItemType); - entry.cellX = c.getInt(indexCellX); - entry.cellY = c.getInt(indexCellY); - entry.spanX = c.getInt(indexSpanX); - entry.spanY = c.getInt(indexSpanY); - entry.screenId = screen; - - try { - // calculate weight - switch (entry.itemType) { - case Favorites.ITEM_TYPE_SHORTCUT: - case Favorites.ITEM_TYPE_DEEP_SHORTCUT: - case Favorites.ITEM_TYPE_APPLICATION: { - verifyIntent(c.getString(indexIntent)); - entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ? - WT_APPLICATION : WT_SHORTCUT; - break; - } - case Favorites.ITEM_TYPE_APPWIDGET: { - String provider = c.getString(indexAppWidgetProvider); - ComponentName cn = ComponentName.unflattenFromString(provider); - verifyPackage(cn.getPackageName()); - entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR - * entry.spanX * entry.spanY); - - int widgetId = c.getInt(indexAppWidgetId); - LauncherAppWidgetProviderInfo pInfo = - widgetManagerHelper.getLauncherAppWidgetInfo(widgetId); - Point spans = null; - if (pInfo != null) { - spans = pInfo.getMinSpans(); - } - if (spans != null) { - entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX; - entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY; - } else { - // Assume that the widget be resized down to 2x2 - entry.minSpanX = entry.minSpanY = 2; - } - - if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) { - throw new Exception("Widget can't be resized down to fit the grid"); - } - break; - } - case Favorites.ITEM_TYPE_FOLDER: { - int total = getFolderItemsCount(entry.id); - if (total == 0) { - throw new Exception("Folder is empty"); - } - entry.weight = WT_FOLDER_FACTOR * total; - break; - } - default: - throw new Exception("Invalid item type"); - } - } catch (Exception e) { - if (DEBUG) { - Log.d(TAG, "Removing item " + entry.id, e); - } - mEntryToRemove.add(entry.id); - continue; - } - entries.add(entry); - } - c.close(); - return entries; - } - - /** - * @return the number of valid items in the folder. - */ - private int getFolderItemsCount(int folderId) { - Cursor c = queryWorkspace( - new String[]{Favorites._ID, Favorites.INTENT}, - Favorites.CONTAINER + " = " + folderId); - - int total = 0; - while (c.moveToNext()) { - try { - verifyIntent(c.getString(1)); - total++; - } catch (Exception e) { - mEntryToRemove.add(c.getInt(0)); - } - } - c.close(); - return total; - } - - protected Cursor queryWorkspace(String[] columns, String where) { - return mDb.query(mTableName, columns, where, null, null, null, null); - } - - /** - * Verifies if the intent should be restored. - */ - private void verifyIntent(String intentStr) throws Exception { - Intent intent = Intent.parseUri(intentStr, 0); - if (intent.getComponent() != null) { - verifyPackage(intent.getComponent().getPackageName()); - } else if (intent.getPackage() != null) { - // Only verify package if the component was null. - verifyPackage(intent.getPackage()); - } - } - - /** - * Verifies if the package should be restored - */ - private void verifyPackage(String packageName) throws Exception { - if (!mValidPackages.contains(packageName)) { - throw new Exception("Package not available"); - } - } - - protected static class DbEntry extends ItemInfo implements Comparable { - - public float weight; - - public DbEntry() { - } - - public DbEntry copy() { - DbEntry entry = new DbEntry(); - entry.copyFrom(this); - entry.weight = weight; - entry.minSpanX = minSpanX; - entry.minSpanY = minSpanY; - return entry; - } - - /** - * Comparator such that larger widgets come first, followed by all 1x1 items - * based on their weights. - */ - @Override - public int compareTo(DbEntry another) { - if (itemType == Favorites.ITEM_TYPE_APPWIDGET) { - if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) { - return another.spanY * another.spanX - spanX * spanY; - } else { - return -1; - } - } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) { - return 1; - } else { - // Place higher weight before lower weight. - return Float.compare(another.weight, weight); - } - } - - public boolean columnsSame(DbEntry org) { - return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX && - org.spanY == spanY && org.screenId == screenId; - } - - public void addToContentValues(ContentValues values) { - values.put(Favorites.SCREEN, screenId); - values.put(Favorites.CELLX, cellX); - values.put(Favorites.CELLY, cellY); - values.put(Favorites.SPANX, spanX); - values.put(Favorites.SPANY, spanY); - } - } - - private static ArrayList deepCopy(ArrayList src) { - ArrayList dup = new ArrayList<>(src.size()); - for (DbEntry e : src) { - dup.add(e.copy()); - } - return dup; - } - - public static void markForMigration( - Context context, int gridX, int gridY, int hotseatSize) { - Utilities.getPrefs(context).edit() - .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(gridX, gridY)) - .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, hotseatSize) - .apply(); - } - - /** - * Check given a new IDP, if migration is necessary. - */ - public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) { - SharedPreferences prefs = Utilities.getPrefs(context); - String gridSizeString = getPointString(idp.numColumns, idp.numRows); - - return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) - || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1); - } - - /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */ - public static boolean migrateGridIfNeeded(Context context) { - if (context instanceof LauncherPreviewRenderer.PreviewContext) { - return true; - } - return migrateGridIfNeeded(context, null); - } - - /** - * Run the migration algorithm if needed. For preview, we provide the intended idp because it - * has not been changed. If idp is null, we read it from the context, for actual grid migration. - * - * @return false if the migration failed. - */ - public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) { - boolean migrateForPreview = idp != null; - if (!migrateForPreview) { - idp = LauncherAppState.getIDP(context); - } - - if (!needsToMigrate(context, idp)) { - return true; - } - - SharedPreferences prefs = Utilities.getPrefs(context); - String gridSizeString = getPointString(idp.numColumns, idp.numRows); - long migrationStartTime = SystemClock.elapsedRealtime(); - try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call( - context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION) - .getBinder(Settings.EXTRA_VALUE)) { - - int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, - idp.numDatabaseHotseatIcons); - Point sourceSize = parsePoint(prefs.getString( - KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)); - - boolean dbChanged = false; - if (migrateForPreview) { - copyTable(transaction.getDb(), Favorites.TABLE_NAME, transaction.getDb(), - Favorites.PREVIEW_TABLE_NAME, context); - } - - GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(), - srcHotseatCount, sourceSize.x, sourceSize.y); - if (migrateForPreview ? backupTable.restoreToPreviewIfBackupExists() - : backupTable.backupOrRestoreAsNeeded()) { - dbChanged = true; - srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize); - } - - HashSet validPackages = getValidPackages(context); - // Hotseat. - if (srcHotseatCount != idp.numDatabaseHotseatIcons - && new GridSizeMigrationTask(context, transaction.getDb(), validPackages, - migrateForPreview, srcHotseatCount, - idp.numDatabaseHotseatIcons).migrateHotseat()) { - dbChanged = true; - } - - // Grid size - Point targetSize = new Point(idp.numColumns, idp.numRows); - if (new MultiStepMigrationTask(validPackages, context, transaction.getDb(), - migrateForPreview).migrate(sourceSize, targetSize)) { - dbChanged = true; - } - - if (dbChanged) { - // Make sure we haven't removed everything. - final Cursor c = context.getContentResolver().query( - migrateForPreview ? Favorites.PREVIEW_CONTENT_URI : Favorites.CONTENT_URI, - null, null, null, null); - boolean hasData = c.moveToNext(); - c.close(); - if (!hasData) { - throw new Exception("Removed every thing during grid resize"); - } - } - - transaction.commit(); - if (!migrateForPreview) { - Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE); - } - return true; - } catch (Exception e) { - Log.e(TAG, "Error during preview grid migration", e); - - return false; - } finally { - Log.v(TAG, "Preview workspace migration completed in " - + (SystemClock.elapsedRealtime() - migrationStartTime)); - - if (!migrateForPreview) { - // Save current configuration, so that the migration does not run again. - prefs.edit() - .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString) - .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons) - .apply(); - } - } - } - - protected static HashSet getValidPackages(Context context) { - // Initialize list of valid packages. This contain all the packages which are already on - // the device and packages which are being installed. Any item which doesn't belong to - // this set is removed. - // Since the loader removes such items anyway, removing these items here doesn't cause - // any extra data loss and gives us more free space on the grid for better migration. - HashSet validPackages = new HashSet<>(); - for (PackageInfo info : context.getPackageManager() - .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { - validPackages.add(info.packageName); - } - InstallSessionHelper.INSTANCE.get(context) - .getActiveSessions().keySet() - .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName)); - return validPackages; - } - - /** - * Removes any broken item from the hotseat. - * - * @return a map with occupied hotseat position set to non-null value. - */ - public static IntSparseArrayMap removeBrokenHotseatItems(Context context) - throws Exception { - try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call( - context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION) - .getBinder(Settings.EXTRA_VALUE)) { - GridSizeMigrationTask task = new GridSizeMigrationTask( - context, transaction.getDb(), getValidPackages(context), - false /* usePreviewTable */, Integer.MAX_VALUE, Integer.MAX_VALUE); - - // Load all the valid entries - ArrayList items = task.loadHotseatEntries(); - // Delete any entry marked for deletion by above load. - task.applyOperations(); - IntSparseArrayMap positions = new IntSparseArrayMap<>(); - for (DbEntry item : items) { - positions.put(item.screenId, item); - } - transaction.commit(); - return positions; - } - } - - /** - * Task to run grid migration in multiple steps when the size difference is more than 1. - */ - protected static class MultiStepMigrationTask { - private final HashSet mValidPackages; - private final Context mContext; - private final SQLiteDatabase mDb; - private final boolean mUsePreviewTable; - - public MultiStepMigrationTask(HashSet validPackages, Context context, - SQLiteDatabase db, boolean usePreviewTable) { - mValidPackages = validPackages; - mContext = context; - mDb = db; - mUsePreviewTable = usePreviewTable; - } - - public boolean migrate(Point sourceSize, Point targetSize) throws Exception { - boolean dbChanged = false; - if (!targetSize.equals(sourceSize)) { - if (sourceSize.x < targetSize.x) { - // Source is smaller that target, just expand the grid without actual migration. - sourceSize.x = targetSize.x; - } - if (sourceSize.y < targetSize.y) { - // Source is smaller that target, just expand the grid without actual migration. - sourceSize.y = targetSize.y; - } - - // Migrate the workspace grid, such that the points differ by max 1 in x and y - // each on every step. - while (!targetSize.equals(sourceSize)) { - // Get the next size, such that the points differ by max 1 in x and y each - Point nextSize = new Point(sourceSize); - if (targetSize.x < nextSize.x) { - nextSize.x--; - } - if (targetSize.y < nextSize.y) { - nextSize.y--; - } - if (runStepTask(sourceSize, nextSize)) { - dbChanged = true; - } - sourceSize.set(nextSize.x, nextSize.y); - } - } - return dbChanged; - } - - protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception { - return new GridSizeMigrationTask(mContext, mDb, mValidPackages, mUsePreviewTable, - sourceSize, nextSize).migrateWorkspace(); - } - } -} diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 43f9be5f80..1378a1a4c0 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -17,7 +17,6 @@ package com.android.launcher3.model; import static com.android.launcher3.WorkspaceLayoutManager.LEFT_PANEL_ID; -import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; @@ -80,7 +79,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.PackageInstallInfo; import com.android.launcher3.pm.UserCache; -import com.android.launcher3.provider.ImportDataTask; import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; @@ -334,16 +332,7 @@ public class LoaderTask implements Runnable { final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context); boolean clearDb = false; - try { - ImportDataTask.performImportIfPossible(context); - } catch (Exception e) { - // Migration failed. Clear workspace. - clearDb = true; - } - - if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get() - ? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context) - : !GridSizeMigrationTask.migrateGridIfNeeded(context))) { + if (!GridSizeMigrationTaskV2.migrateGridIfNeeded(context)) { // Migration failed. Clear workspace. clearDb = true; } diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java deleted file mode 100644 index c9af2fe6fc..0000000000 --- a/src/com/android/launcher3/provider/ImportDataTask.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2016 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.provider; - -import static com.android.launcher3.Utilities.getDevicePrefs; - -import android.content.ContentProviderOperation; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.os.Process; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.SparseBooleanArray; - -import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback; -import com.android.launcher3.DefaultLayoutParser; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherProvider; -import com.android.launcher3.LauncherSettings; -import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; -import com.android.launcher3.Workspace; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.logging.FileLog; -import com.android.launcher3.model.GridSizeMigrationTask; -import com.android.launcher3.model.data.LauncherAppWidgetInfo; -import com.android.launcher3.pm.UserCache; -import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.IntSparseArrayMap; -import com.android.launcher3.util.PackageManagerHelper; - -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.HashSet; - -/** - * Utility class to import data from another Launcher which is based on Launcher3 schema. - */ -public class ImportDataTask { - - public static final String KEY_DATA_IMPORT_SRC_PKG = "data_import_src_pkg"; - public static final String KEY_DATA_IMPORT_SRC_AUTHORITY = "data_import_src_authority"; - - private static final String TAG = "ImportDataTask"; - private static final int MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION = 6; - // Insert items progressively to avoid OOM exception when loading icons. - private static final int BATCH_INSERT_SIZE = 15; - - private final Context mContext; - - private final Uri mOtherFavoritesUri; - - private int mHotseatSize; - private int mMaxGridSizeX; - private int mMaxGridSizeY; - - private ImportDataTask(Context context, String sourceAuthority) { - mContext = context; - mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME); - } - - public boolean importWorkspace() throws Exception { - FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri); - - mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0; - importWorkspaceItems(); - GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize); - - // Create empty DB flag. - LauncherSettings.Settings.call(mContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); - return true; - } - - /** - * 1) Imports all the workspace entries from the source provider. - * 2) For home screen entries, maps the screen id based on {@param screenIdMap} - * 3) In the end fills any holes in hotseat with items from default hotseat layout. - */ - private void importWorkspaceItems() throws Exception { - String profileId = Long.toString(UserCache.INSTANCE.get(mContext) - .getSerialNumberForUser(Process.myUserHandle())); - - boolean createEmptyRowOnFirstScreen; - if (FeatureFlags.QSB_ON_FIRST_SCREEN) { - try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null, - // get items on the first row of the first screen (min screen id) - "profileId = ? AND container = -100 AND cellY = 0 AND screen = " + - "(SELECT MIN(screen) FROM favorites WHERE container = -100)", - new String[]{profileId}, - null)) { - // First row of first screen is not empty - createEmptyRowOnFirstScreen = c.moveToNext(); - } - } else { - createEmptyRowOnFirstScreen = false; - } - - ArrayList insertOperations = new ArrayList<>(BATCH_INSERT_SIZE); - - // Set of package names present in hotseat - final HashSet hotseatTargetApps = new HashSet<>(); - int maxId = 0; - - // Number of imported items on workspace and hotseat - int totalItemsOnWorkspace = 0; - - try (Cursor c = mContext.getContentResolver() - .query(mOtherFavoritesUri, null, - // Only migrate the primary user - Favorites.PROFILE_ID + " = ?", new String[]{profileId}, - // Get the items sorted by container, so that the folders are loaded - // before the corresponding items. - Favorites.CONTAINER + " , " + Favorites.SCREEN)) { - - // various columns we expect to exist. - final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); - final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT); - final int titleIndex = c.getColumnIndexOrThrow(Favorites.TITLE); - final int containerIndex = c.getColumnIndexOrThrow(Favorites.CONTAINER); - final int itemTypeIndex = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); - final int widgetProviderIndex = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); - final int screenIndex = c.getColumnIndexOrThrow(Favorites.SCREEN); - final int cellXIndex = c.getColumnIndexOrThrow(Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow(Favorites.CELLY); - final int spanXIndex = c.getColumnIndexOrThrow(Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow(Favorites.SPANY); - final int rankIndex = c.getColumnIndexOrThrow(Favorites.RANK); - final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON); - final int iconPackageIndex = c.getColumnIndexOrThrow(Favorites.ICON_PACKAGE); - final int iconResourceIndex = c.getColumnIndexOrThrow(Favorites.ICON_RESOURCE); - - SparseBooleanArray mValidFolders = new SparseBooleanArray(); - ContentValues values = new ContentValues(); - - Integer firstScreenId = null; - while (c.moveToNext()) { - values.clear(); - int id = c.getInt(idIndex); - maxId = Math.max(maxId, id); - int type = c.getInt(itemTypeIndex); - int container = c.getInt(containerIndex); - - int screen = c.getInt(screenIndex); - - int cellX = c.getInt(cellXIndex); - int cellY = c.getInt(cellYIndex); - int spanX = c.getInt(spanXIndex); - int spanY = c.getInt(spanYIndex); - - switch (container) { - case Favorites.CONTAINER_DESKTOP: { - if (screen < Workspace.FIRST_SCREEN_ID) { - FileLog.d(TAG, String.format( - "Skipping item %d, type %d not on a valid screen %d", - id, type, screen)); - continue; - } - if (firstScreenId == null) { - firstScreenId = screen; - } - // Reset the screen to 0-index value - if (createEmptyRowOnFirstScreen && firstScreenId.equals(screen)) { - // Shift items by 1. - cellY++; - // Change the screen id to first screen - screen = Workspace.FIRST_SCREEN_ID; - } - - mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX); - mMaxGridSizeY = Math.max(mMaxGridSizeY, cellY + spanY); - break; - } - case Favorites.CONTAINER_HOTSEAT: { - mHotseatSize = Math.max(mHotseatSize, screen + 1); - break; - } - default: - if (!mValidFolders.get(container)) { - FileLog.d(TAG, String.format("Skipping item %d, type %d not in a valid folder %d", id, type, container)); - continue; - } - } - - Intent intent = null; - switch (type) { - case Favorites.ITEM_TYPE_FOLDER: { - mValidFolders.put(id, true); - // Use a empty intent to indicate a folder. - intent = new Intent(); - break; - } - case Favorites.ITEM_TYPE_APPWIDGET: { - values.put(Favorites.RESTORED, - LauncherAppWidgetInfo.FLAG_ID_NOT_VALID | - LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY | - LauncherAppWidgetInfo.FLAG_UI_NOT_READY); - values.put(Favorites.APPWIDGET_PROVIDER, c.getString(widgetProviderIndex)); - break; - } - case Favorites.ITEM_TYPE_SHORTCUT: - case Favorites.ITEM_TYPE_APPLICATION: { - intent = Intent.parseUri(c.getString(intentIndex), 0); - if (PackageManagerHelper.isLauncherAppTarget(intent)) { - type = Favorites.ITEM_TYPE_APPLICATION; - } else { - values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex)); - values.put(Favorites.ICON_RESOURCE, c.getString(iconResourceIndex)); - } - values.put(Favorites.ICON, c.getBlob(iconIndex)); - values.put(Favorites.INTENT, intent.toUri(0)); - values.put(Favorites.RANK, c.getInt(rankIndex)); - - values.put(Favorites.RESTORED, 1); - break; - } - default: - FileLog.d(TAG, String.format("Skipping item %d, not a valid type %d", id, type)); - continue; - } - - if (container == Favorites.CONTAINER_HOTSEAT) { - if (intent == null) { - FileLog.d(TAG, String.format("Skipping item %d, null intent on hotseat", id)); - continue; - } - if (intent.getComponent() != null) { - intent.setPackage(intent.getComponent().getPackageName()); - } - hotseatTargetApps.add(getPackage(intent)); - } - - values.put(Favorites._ID, id); - values.put(Favorites.ITEM_TYPE, type); - values.put(Favorites.CONTAINER, container); - values.put(Favorites.SCREEN, screen); - values.put(Favorites.CELLX, cellX); - values.put(Favorites.CELLY, cellY); - values.put(Favorites.SPANX, spanX); - values.put(Favorites.SPANY, spanY); - values.put(Favorites.TITLE, c.getString(titleIndex)); - insertOperations.add(ContentProviderOperation - .newInsert(Favorites.CONTENT_URI).withValues(values).build()); - if (container < 0) { - totalItemsOnWorkspace++; - } - - if (insertOperations.size() >= BATCH_INSERT_SIZE) { - mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, - insertOperations); - insertOperations.clear(); - } - } - } - FileLog.d(TAG, totalItemsOnWorkspace + " items imported from external source"); - if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) { - throw new Exception("Insufficient data"); - } - if (!insertOperations.isEmpty()) { - mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, - insertOperations); - insertOperations.clear(); - } - - IntSparseArrayMap hotseatItems = GridSizeMigrationTask - .removeBrokenHotseatItems(mContext); - int myHotseatCount = LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons; - if (hotseatItems.size() < myHotseatCount) { - // Insufficient hotseat items. Add a few more. - HotseatParserCallback parserCallback = new HotseatParserCallback( - hotseatTargetApps, hotseatItems, insertOperations, maxId + 1, myHotseatCount); - new HotseatLayoutParser(mContext, - parserCallback).loadLayout(null, new IntArray()); - mHotseatSize = hotseatItems.keyAt(hotseatItems.size() - 1) + 1; - - if (!insertOperations.isEmpty()) { - mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, - insertOperations); - } - } - } - - private static String getPackage(Intent intent) { - return intent.getComponent() != null ? intent.getComponent().getPackageName() - : intent.getPackage(); - } - - /** - * Performs data import if possible. - * @return true on successful data import, false if it was not available - * @throws Exception if the import failed - */ - public static boolean performImportIfPossible(Context context) throws Exception { - SharedPreferences devicePrefs = getDevicePrefs(context); - String sourcePackage = devicePrefs.getString(KEY_DATA_IMPORT_SRC_PKG, ""); - String sourceAuthority = devicePrefs.getString(KEY_DATA_IMPORT_SRC_AUTHORITY, ""); - - if (TextUtils.isEmpty(sourcePackage) || TextUtils.isEmpty(sourceAuthority)) { - return false; - } - - // Synchronously clear the migration flags. This ensures that we do not try migration - // again and thus prevents potential crash loops due to migration failure. - devicePrefs.edit().remove(KEY_DATA_IMPORT_SRC_PKG).remove(KEY_DATA_IMPORT_SRC_AUTHORITY).commit(); - - if (!Settings.call(context.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED) - .getBoolean(Settings.EXTRA_VALUE, false)) { - // Only migration if a new DB was created. - return false; - } - - for (ProviderInfo info : context.getPackageManager().queryContentProviders( - null, context.getApplicationInfo().uid, 0)) { - - if (sourcePackage.equals(info.packageName)) { - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - // Only migrate if the source launcher is also on system image. - return false; - } - - // Wait until we found a provider with matching authority. - if (sourceAuthority.equals(info.authority)) { - if (TextUtils.isEmpty(info.readPermission) || - context.checkPermission(info.readPermission, Process.myPid(), - Process.myUid()) == PackageManager.PERMISSION_GRANTED) { - // All checks passed, run the import task. - return new ImportDataTask(context, sourceAuthority).importWorkspace(); - } - } - } - } - return false; - } - - /** - * Extension of {@link DefaultLayoutParser} which only allows icons and shortcuts. - */ - private static class HotseatLayoutParser extends DefaultLayoutParser { - public HotseatLayoutParser(Context context, LayoutParserCallback callback) { - super(context, null, callback, context.getResources(), - LauncherAppState.getIDP(context).defaultLayoutId); - } - - @Override - protected ArrayMap getLayoutElementsMap() { - // Only allow shortcut parsers - ArrayMap parsers = new ArrayMap<>(); - parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser()); - parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes)); - parsers.put(TAG_RESOLVE, new ResolveParser()); - return parsers; - } - } - - /** - * {@link LayoutParserCallback} which adds items in empty hotseat spots. - */ - private static class HotseatParserCallback implements LayoutParserCallback { - private final HashSet mExistingApps; - private final IntSparseArrayMap mExistingItems; - private final ArrayList mOutOps; - private final int mRequiredSize; - private int mStartItemId; - - HotseatParserCallback( - HashSet existingApps, IntSparseArrayMap existingItems, - ArrayList outOps, int startItemId, int requiredSize) { - mExistingApps = existingApps; - mExistingItems = existingItems; - mOutOps = outOps; - mRequiredSize = requiredSize; - mStartItemId = startItemId; - } - - @Override - public int generateNewItemId() { - return mStartItemId++; - } - - @Override - public int insertAndCheck(SQLiteDatabase db, ContentValues values) { - if (mExistingItems.size() >= mRequiredSize) { - // No need to add more items. - return 0; - } - if (!Integer.valueOf(Favorites.CONTAINER_HOTSEAT) - .equals(values.getAsInteger(Favorites.CONTAINER))) { - // Ignore items which are not for hotseat. - return 0; - } - - Intent intent; - try { - intent = Intent.parseUri(values.getAsString(Favorites.INTENT), 0); - } catch (URISyntaxException e) { - return 0; - } - String pkg = getPackage(intent); - if (pkg == null || mExistingApps.contains(pkg)) { - // The item does not target an app or is already in hotseat. - return 0; - } - mExistingApps.add(pkg); - - // find next vacant spot. - int screen = 0; - while (mExistingItems.get(screen) != null) { - screen++; - } - mExistingItems.put(screen, intent); - values.put(Favorites.SCREEN, screen); - mOutOps.add(ContentProviderOperation.newInsert(Favorites.CONTENT_URI).withValues(values).build()); - return 0; - } - } -} diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java index 7e05a5a78d..6855bb1e69 100644 --- a/src/com/android/launcher3/provider/LauncherDbUtils.java +++ b/src/com/android/launcher3/provider/LauncherDbUtils.java @@ -16,84 +16,21 @@ package com.android.launcher3.provider; -import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.os.Binder; import android.os.Process; -import android.util.Log; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.pm.UserCache; import com.android.launcher3.util.IntArray; -import java.util.Locale; - /** * A set of utility methods for Launcher DB used for DB updates and migration. */ public class LauncherDbUtils { - private static final String TAG = "LauncherDbUtils"; - - /** - * Makes the first screen as screen 0 (if screen 0 already exists, - * renames it to some other number). - * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear - * the first row. The items in the first screen are moved and resized but the carry-forward - * items are simply deleted. - */ - public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) { - try (SQLiteTransaction t = new SQLiteTransaction(db)) { - // Get the first screen - final int firstScreenId; - try (Cursor c = db.rawQuery(String.format(Locale.ENGLISH, - "SELECT MIN(%1$s) from %2$s where %3$s = %4$d", - Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER, - Favorites.CONTAINER_DESKTOP), null)) { - - if (!c.moveToNext()) { - // No update needed - t.commit(); - return true; - } - - firstScreenId = c.getInt(0); - } - - if (firstScreenId != 0) { - // Rename the first screen to 0. - renameScreen(db, firstScreenId, 0); - } - - // Check if the first row is empty - if (DatabaseUtils.queryNumEntries(db, Favorites.TABLE_NAME, - "container = -100 and screen = 0 and cellY = 0") == 0) { - // First row is empty, no need to migrate. - t.commit(); - return true; - } - - new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db) - .migrateScreen0(); - t.commit(); - return true; - } catch (Exception e) { - Log.e(TAG, "Failed to update workspace size", e); - return false; - } - } - - private static void renameScreen(SQLiteDatabase db, int oldScreen, int newScreen) { - String[] whereParams = new String[] { Integer.toString(oldScreen) }; - ContentValues values = new ContentValues(); - values.put(Favorites.SCREEN, newScreen); - db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams); - } - public static IntArray queryIntArray(SQLiteDatabase db, String tableName, String columnName, String selection, String groupBy, String orderBy) { IntArray out = new IntArray(); diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java deleted file mode 100644 index c0111b9388..0000000000 --- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2016 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.provider; - -import android.content.ContentValues; -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Point; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.Utilities; -import com.android.launcher3.Workspace; -import com.android.launcher3.model.GridSizeMigrationTask; -import com.android.launcher3.util.IntSparseArrayMap; - -import java.util.ArrayList; - -/** - * An extension of {@link GridSizeMigrationTask} which migrates only one screen and - * deletes all carry-forward items. - */ -public class LossyScreenMigrationTask extends GridSizeMigrationTask { - - private final IntSparseArrayMap mOriginalItems; - private final IntSparseArrayMap mUpdates; - - protected LossyScreenMigrationTask( - Context context, InvariantDeviceProfile idp, SQLiteDatabase db) { - // Decrease the rows count by 1 - super(context, db, getValidPackages(context), false /* usePreviewTable */, - new Point(idp.numColumns, idp.numRows + 1), - new Point(idp.numColumns, idp.numRows)); - - mOriginalItems = new IntSparseArrayMap<>(); - mUpdates = new IntSparseArrayMap<>(); - } - - @Override - protected void update(DbEntry item) { - mUpdates.put(item.id, item.copy()); - } - - @Override - protected ArrayList loadWorkspaceEntries(int screen) { - ArrayList result = super.loadWorkspaceEntries(screen); - for (DbEntry entry : result) { - mOriginalItems.put(entry.id, entry.copy()); - - // Shift all items by 1 in y direction and mark them for update. - entry.cellY++; - mUpdates.put(entry.id, entry.copy()); - } - - return result; - } - - public void migrateScreen0() { - migrateScreen(Workspace.FIRST_SCREEN_ID); - - ContentValues tempValues = new ContentValues(); - for (DbEntry update : mUpdates) { - DbEntry org = mOriginalItems.get(update.id); - - if (org.cellX != update.cellX || org.cellY != update.cellY - || org.spanX != update.spanX || org.spanY != update.spanY) { - tempValues.clear(); - update.addToContentValues(tempValues); - mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?", - new String[] {Integer.toString(update.id)}); - } - } - - // Delete any carry over items as we are only migration a single screen. - for (DbEntry entry : mCarryOver) { - mEntryToRemove.add(entry.id); - } - - if (!mEntryToRemove.isEmpty()) { - mDb.delete(Favorites.TABLE_NAME, - Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), null); - } - } -}