Merge "Removing GridMigrationTask-v1 since it has been disabled for a while" into sc-v2-dev
This commit is contained in:
commit
b2f8f42510
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue