Merge "Removing GridMigrationTask-v1 since it has been disabled for a while" into sc-v2-dev

This commit is contained in:
TreeHugger Robot 2021-07-29 18:37:15 +00:00 committed by Android (Google) Code Review
commit b2f8f42510
11 changed files with 23 additions and 2311 deletions

View File

@ -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));
}
}

View File

@ -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<String> 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<Point> 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());
}
}
}

View File

@ -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.

View File

@ -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");

View File

@ -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

View File

@ -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
*/

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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<ContentProviderOperation> insertOperations = new ArrayList<>(BATCH_INSERT_SIZE);
// Set of package names present in hotseat
final HashSet<String> 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<Object> 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<String, TagParser> getLayoutElementsMap() {
// Only allow shortcut parsers
ArrayMap<String, TagParser> 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<String> mExistingApps;
private final IntSparseArrayMap<Object> mExistingItems;
private final ArrayList<ContentProviderOperation> mOutOps;
private final int mRequiredSize;
private int mStartItemId;
HotseatParserCallback(
HashSet<String> existingApps, IntSparseArrayMap<Object> existingItems,
ArrayList<ContentProviderOperation> 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;
}
}
}

View File

@ -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();

View File

@ -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<DbEntry> mOriginalItems;
private final IntSparseArrayMap<DbEntry> 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<DbEntry> loadWorkspaceEntries(int screen) {
ArrayList<DbEntry> 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);
}
}
}