Bye bye workspace screens table
Removing a separate table for workspace screens. List of screens are automatically parsed using the items in the favorites DB. Order of the screen based on the screen id and rearranging screens is no longer supported. In case the screens need to be rearranged, all the items in the favorites db will need to be updated with new screen ids. This makes backing up the DB (in the same database) easier as only one table needs to be duplicates. Change-Id: I8ba947a898f637d780e2f49925e78604263126e8
This commit is contained in:
parent
415f173331
commit
c5939393a9
|
@ -2,8 +2,12 @@
|
||||||
// Note: Comments are not supported in JSON schema, but android parser is lenient.
|
// Note: Comments are not supported in JSON schema, but android parser is lenient.
|
||||||
|
|
||||||
// Maximum DB version supported by this schema
|
// Maximum DB version supported by this schema
|
||||||
"version" : 27,
|
"version" : 28,
|
||||||
|
|
||||||
|
"downgrade_to_27" : [
|
||||||
|
"CREATE TABLE workspaceScreens (_id INTEGER PRIMARY KEY,screenRank INTEGER,modified INTEGER NOT NULL DEFAULT 0)",
|
||||||
|
"insert into workspaceScreens (_id, screenRank) select screen as _id, screen as screenRank from favorites where container = -100 group by screen order by screen"
|
||||||
|
],
|
||||||
// Downgrade from 27 to 26. Empty array indicates, the DB is compatible
|
// Downgrade from 27 to 26. Empty array indicates, the DB is compatible
|
||||||
"downgrade_to_26" : [],
|
"downgrade_to_26" : [],
|
||||||
"downgrade_to_25" : [],
|
"downgrade_to_25" : [],
|
||||||
|
|
|
@ -18,7 +18,9 @@ import android.util.Pair;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.ShortcutInfo;
|
import com.android.launcher3.ShortcutInfo;
|
||||||
|
import com.android.launcher3.util.ContentWriter;
|
||||||
import com.android.launcher3.util.GridOccupancy;
|
import com.android.launcher3.util.GridOccupancy;
|
||||||
import com.android.launcher3.util.IntArray;
|
import com.android.launcher3.util.IntArray;
|
||||||
import com.android.launcher3.util.IntSparseArrayMap;
|
import com.android.launcher3.util.IntSparseArrayMap;
|
||||||
|
@ -31,6 +33,8 @@ import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link AddWorkspaceItemsTask}
|
* Tests for {@link AddWorkspaceItemsTask}
|
||||||
|
@ -60,15 +64,11 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
for (ItemInfo item : items) {
|
for (ItemInfo item : items) {
|
||||||
list.add(Pair.create(item, null));
|
list.add(Pair.create(item, null));
|
||||||
}
|
}
|
||||||
return new AddWorkspaceItemsTask(list) {
|
return new AddWorkspaceItemsTask(list);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateScreens(Context context, IntArray workspaceScreens) { }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFindSpaceForItem_prefers_second() {
|
public void testFindSpaceForItem_prefers_second() throws Exception {
|
||||||
// First screen has only one hole of size 1
|
// First screen has only one hole of size 1
|
||||||
int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
||||||
|
|
||||||
|
@ -93,7 +93,6 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
public void testFindSpaceForItem_adds_new_screen() throws Exception {
|
public void testFindSpaceForItem_adds_new_screen() throws Exception {
|
||||||
// First screen has 2 holes of sizes 3x2 and 2x3
|
// First screen has 2 holes of sizes 3x2 and 2x3
|
||||||
setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
|
setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
|
||||||
commitScreensToDb();
|
|
||||||
|
|
||||||
IntArray oldScreens = existingScreens.clone();
|
IntArray oldScreens = existingScreens.clone();
|
||||||
int[] spaceFound = newTask()
|
int[] spaceFound = newTask()
|
||||||
|
@ -109,7 +108,6 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
|
|
||||||
// Setup a screen with a hole
|
// Setup a screen with a hole
|
||||||
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
||||||
commitScreensToDb();
|
|
||||||
|
|
||||||
// Nothing was added
|
// Nothing was added
|
||||||
assertTrue(executeTaskForTest(newTask(info)).isEmpty());
|
assertTrue(executeTaskForTest(newTask(info)).isEmpty());
|
||||||
|
@ -125,7 +123,6 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
|
|
||||||
// Setup a screen with a hole
|
// Setup a screen with a hole
|
||||||
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
|
||||||
commitScreensToDb();
|
|
||||||
|
|
||||||
executeTaskForTest(newTask(info, info2)).get(0).run();
|
executeTaskForTest(newTask(info, info2)).get(0).run();
|
||||||
ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
|
ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
|
||||||
|
@ -141,7 +138,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
assertTrue(animated.getValue().contains(info2));
|
assertTrue(animated.getValue().contains(info2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) {
|
private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) throws Exception {
|
||||||
GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows);
|
GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows);
|
||||||
occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true);
|
occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true);
|
||||||
for (Rect r : holes) {
|
for (Rect r : holes) {
|
||||||
|
@ -151,6 +148,7 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
existingScreens.add(screenId);
|
existingScreens.add(screenId);
|
||||||
screenOccupancy.append(screenId, occupancy);
|
screenOccupancy.append(screenId, occupancy);
|
||||||
|
|
||||||
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
for (int x = 0; x < idp.numColumns; x++) {
|
for (int x = 0; x < idp.numColumns; x++) {
|
||||||
for (int y = 0; y < idp.numRows; y++) {
|
for (int y = 0; y < idp.numRows; y++) {
|
||||||
if (!occupancy.cells[x][y]) {
|
if (!occupancy.cells[x][y]) {
|
||||||
|
@ -165,27 +163,19 @@ public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
|
||||||
info.cellY = y;
|
info.cellY = y;
|
||||||
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||||
bgDataModel.addItem(targetContext, info, false);
|
bgDataModel.addItem(targetContext, info, false);
|
||||||
|
|
||||||
|
executor.execute(() -> {
|
||||||
|
ContentWriter writer = new ContentWriter(targetContext);
|
||||||
|
info.writeToValues(writer);
|
||||||
|
writer.put(Favorites._ID, info.id);
|
||||||
|
targetContext.getContentResolver().insert(Favorites.CONTENT_URI,
|
||||||
|
writer.getValues(targetContext));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executor.submit(() -> null).get();
|
||||||
|
executor.shutdown();
|
||||||
return startId;
|
return startId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void commitScreensToDb() throws Exception {
|
|
||||||
LauncherSettings.Settings.call(targetContext.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
|
||||||
|
|
||||||
Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
|
|
||||||
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
|
||||||
// Clear the table
|
|
||||||
ops.add(ContentProviderOperation.newDelete(uri).build());
|
|
||||||
int count = existingScreens.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
ContentValues v = new ContentValues();
|
|
||||||
int screenId = existingScreens.get(i);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
||||||
ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
|
|
||||||
}
|
|
||||||
targetContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,17 @@ import static junit.framework.Assert.assertFalse;
|
||||||
import static junit.framework.Assert.assertNotSame;
|
import static junit.framework.Assert.assertNotSame;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherProvider.DatabaseHelper;
|
import com.android.launcher3.LauncherProvider.DatabaseHelper;
|
||||||
|
@ -37,14 +40,15 @@ import com.android.launcher3.R;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link DbDowngradeHelper}
|
* Tests for {@link DbDowngradeHelper}
|
||||||
*/
|
*/
|
||||||
@SmallTest
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class DbDowngradeHelperTest {
|
public class DbDowngradeHelperTest {
|
||||||
|
|
||||||
private static final String SCHEMA_FILE = "test_schema.json";
|
private static final String SCHEMA_FILE = "test_schema.json";
|
||||||
|
@ -56,35 +60,47 @@ public class DbDowngradeHelperTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
mContext = InstrumentationRegistry.getTargetContext();
|
mContext = RuntimeEnvironment.application;
|
||||||
mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE);
|
mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE);
|
||||||
mDbFile = mContext.getDatabasePath(DB_FILE);
|
mDbFile = mContext.getDatabasePath(DB_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDowngradeSchemaMatchesVersion() throws Exception {
|
||||||
|
mSchemaFile.delete();
|
||||||
|
assertFalse(mSchemaFile.exists());
|
||||||
|
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 0, mContext);
|
||||||
|
assertEquals(LauncherProvider.SCHEMA_VERSION, DbDowngradeHelper.parse(mSchemaFile).version);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateSchemaFile() throws Exception {
|
public void testUpdateSchemaFile() throws Exception {
|
||||||
Context myContext = InstrumentationRegistry.getContext();
|
// Setup mock resources
|
||||||
int testResId = myContext.getResources().getIdentifier(
|
Resources res = spy(mContext.getResources());
|
||||||
"db_schema_v10", "raw", myContext.getPackageName());
|
doAnswer(i ->this.getClass().getResourceAsStream("/db_schema_v10.json"))
|
||||||
|
.when(res).openRawResource(eq(R.raw.downgrade_schema));
|
||||||
|
Context context = spy(mContext);
|
||||||
|
when(context.getResources()).thenReturn(res);
|
||||||
|
|
||||||
mSchemaFile.delete();
|
mSchemaFile.delete();
|
||||||
assertFalse(mSchemaFile.exists());
|
assertFalse(mSchemaFile.exists());
|
||||||
|
|
||||||
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, myContext, testResId);
|
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context);
|
||||||
assertTrue(mSchemaFile.exists());
|
assertTrue(mSchemaFile.exists());
|
||||||
assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version);
|
assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version);
|
||||||
|
|
||||||
// Schema is updated on version upgrade
|
// Schema is updated on version upgrade
|
||||||
assertTrue(mSchemaFile.setLastModified(0));
|
assertTrue(mSchemaFile.setLastModified(0));
|
||||||
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, myContext, testResId);
|
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, context);
|
||||||
assertNotSame(0, mSchemaFile.lastModified());
|
assertNotSame(0, mSchemaFile.lastModified());
|
||||||
|
|
||||||
// Schema is not updated when version is same
|
// Schema is not updated when version is same
|
||||||
assertTrue(mSchemaFile.setLastModified(0));
|
assertTrue(mSchemaFile.setLastModified(0));
|
||||||
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, myContext, testResId);
|
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context);
|
||||||
assertEquals(0, mSchemaFile.lastModified());
|
assertEquals(0, mSchemaFile.lastModified());
|
||||||
|
|
||||||
// Schema is not updated on version downgrade
|
// Schema is not updated on version downgrade
|
||||||
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, myContext, testResId);
|
DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, context);
|
||||||
assertEquals(0, mSchemaFile.lastModified());
|
assertEquals(0, mSchemaFile.lastModified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,8 +159,7 @@ public class DbDowngradeHelperTest {
|
||||||
mSchemaFile.delete();
|
mSchemaFile.delete();
|
||||||
mDbFile.delete();
|
mDbFile.delete();
|
||||||
|
|
||||||
DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext,
|
DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext);
|
||||||
R.raw.downgrade_schema);
|
|
||||||
|
|
||||||
DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) {
|
DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) {
|
||||||
@Override
|
@Override
|
|
@ -1,5 +1,7 @@
|
||||||
package com.android.launcher3.model;
|
package com.android.launcher3.model;
|
||||||
|
|
||||||
|
import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@ -10,7 +12,6 @@ import android.database.Cursor;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
|
||||||
import com.android.launcher3.InvariantDeviceProfile;
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.LauncherModel;
|
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.config.FlagOverrideRule;
|
import com.android.launcher3.config.FlagOverrideRule;
|
||||||
|
@ -55,7 +56,6 @@ public class GridSizeMigrationTaskTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
|
||||||
mValidPackages = new HashSet<>();
|
mValidPackages = new HashSet<>();
|
||||||
mValidPackages.add(TEST_PACKAGE);
|
mValidPackages.add(TEST_PACKAGE);
|
||||||
mIdp = new InvariantDeviceProfile();
|
mIdp = new InvariantDeviceProfile();
|
||||||
|
@ -307,11 +307,6 @@ public class GridSizeMigrationTaskTest {
|
||||||
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
||||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||||
|
|
||||||
ContentValues v = new ContentValues();
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
||||||
mContext.getContentResolver().insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
|
|
||||||
|
|
||||||
ids[i] = new int[typeArray[i].length][];
|
ids[i] = new int[typeArray[i].length][];
|
||||||
for (int y = 0; y < typeArray[i].length; y++) {
|
for (int y = 0; y < typeArray[i].length; y++) {
|
||||||
ids[i][y] = new int[typeArray[i][y].length];
|
ids[i][y] = new int[typeArray[i][y].length];
|
||||||
|
@ -326,7 +321,6 @@ public class GridSizeMigrationTaskTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntArray allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
|
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +330,7 @@ public class GridSizeMigrationTaskTest {
|
||||||
* represent the workspace grid.
|
* represent the workspace grid.
|
||||||
*/
|
*/
|
||||||
private void verifyWorkspace(int[][][] ids) {
|
private void verifyWorkspace(int[][][] ids) {
|
||||||
IntArray allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
|
IntArray allScreens = getWorkspaceScreenIds(mContext);
|
||||||
assertEquals(ids.length, allScreens.size());
|
assertEquals(ids.length, allScreens.size());
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
|
|
|
@ -1747,7 +1747,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||||
orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
|
orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
|
||||||
orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID);
|
orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID);
|
||||||
orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
|
orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
|
||||||
LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
|
|
||||||
} else if (!FeatureFlags.QSB_ON_FIRST_SCREEN.get()
|
} else if (!FeatureFlags.QSB_ON_FIRST_SCREEN.get()
|
||||||
&& orderedScreenIds.isEmpty()) {
|
&& orderedScreenIds.isEmpty()) {
|
||||||
// If there are no screens, we need to have an empty screen
|
// If there are no screens, we need to have an empty screen
|
||||||
|
|
|
@ -20,12 +20,8 @@ import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
|
||||||
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
|
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentProviderOperation;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
@ -38,8 +34,8 @@ import android.util.Pair;
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
|
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
import com.android.launcher3.icons.LauncherIcons;
|
|
||||||
import com.android.launcher3.icons.IconCache;
|
import com.android.launcher3.icons.IconCache;
|
||||||
|
import com.android.launcher3.icons.LauncherIcons;
|
||||||
import com.android.launcher3.model.AddWorkspaceItemsTask;
|
import com.android.launcher3.model.AddWorkspaceItemsTask;
|
||||||
import com.android.launcher3.model.BaseModelUpdateTask;
|
import com.android.launcher3.model.BaseModelUpdateTask;
|
||||||
import com.android.launcher3.model.BgDataModel;
|
import com.android.launcher3.model.BgDataModel;
|
||||||
|
@ -51,7 +47,6 @@ import com.android.launcher3.model.PackageInstallStateChangedTask;
|
||||||
import com.android.launcher3.model.PackageUpdatedTask;
|
import com.android.launcher3.model.PackageUpdatedTask;
|
||||||
import com.android.launcher3.model.ShortcutsChangedTask;
|
import com.android.launcher3.model.ShortcutsChangedTask;
|
||||||
import com.android.launcher3.model.UserLockStateChangedTask;
|
import com.android.launcher3.model.UserLockStateChangedTask;
|
||||||
import com.android.launcher3.provider.LauncherDbUtils;
|
|
||||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||||
import com.android.launcher3.util.ComponentKey;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
|
@ -70,7 +65,6 @@ import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
@ -268,53 +262,6 @@ public class LauncherModel extends BroadcastReceiver
|
||||||
runOnWorkerThread(r);
|
runOnWorkerThread(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the order of the workspace screens in the database. The array list contains
|
|
||||||
* a list of screen ids in the order that they should appear.
|
|
||||||
*/
|
|
||||||
public static void updateWorkspaceScreenOrder(Context context, IntArray screens) {
|
|
||||||
final ContentResolver cr = context.getContentResolver();
|
|
||||||
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
|
|
||||||
|
|
||||||
// Create a copy with only non-negative values
|
|
||||||
final IntArray screensCopy = new IntArray();
|
|
||||||
for (int i = 0; i < screens.size(); i++) {
|
|
||||||
int id = screens.get(i);
|
|
||||||
if (id >= 0) {
|
|
||||||
screensCopy.add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Runnable r = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
|
||||||
// Clear the table
|
|
||||||
ops.add(ContentProviderOperation.newDelete(uri).build());
|
|
||||||
int count = screensCopy.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
ContentValues v = new ContentValues();
|
|
||||||
int screenId = screensCopy.get(i);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
||||||
ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
cr.applyBatch(LauncherProvider.AUTHORITY, ops);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (sBgDataModel) {
|
|
||||||
sBgDataModel.workspaceScreens.clear();
|
|
||||||
sBgDataModel.workspaceScreens.addAll(screensCopy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
runOnWorkerThread(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this as the current Launcher activity object for the loader.
|
* Set this as the current Launcher activity object for the loader.
|
||||||
*/
|
*/
|
||||||
|
@ -519,18 +466,6 @@ public class LauncherModel extends BroadcastReceiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the workspace screen ids in an ordered list.
|
|
||||||
*/
|
|
||||||
public static IntArray loadWorkspaceScreensDb(Context context) {
|
|
||||||
final ContentResolver contentResolver = context.getContentResolver();
|
|
||||||
final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
|
|
||||||
|
|
||||||
// Get screens ordered by rank.
|
|
||||||
return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query(
|
|
||||||
screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
|
public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
|
||||||
enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,6 +32,7 @@ import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteQueryBuilder;
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
|
@ -45,12 +46,12 @@ import android.os.Message;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
|
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
|
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.logging.FileLog;
|
import com.android.launcher3.logging.FileLog;
|
||||||
|
@ -70,6 +71,7 @@ import java.io.PrintWriter;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class LauncherProvider extends ContentProvider {
|
public class LauncherProvider extends ContentProvider {
|
||||||
private static final String TAG = "LauncherProvider";
|
private static final String TAG = "LauncherProvider";
|
||||||
|
@ -79,8 +81,9 @@ public class LauncherProvider extends ContentProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the schema of the database. Changes in scheme need not be backwards compatible.
|
* Represents the schema of the database. Changes in scheme need not be backwards compatible.
|
||||||
|
* When increasing the scheme version, ensure that downgrade_schema.json is updated
|
||||||
*/
|
*/
|
||||||
public static final int SCHEMA_VERSION = 27;
|
public static final int SCHEMA_VERSION = 28;
|
||||||
|
|
||||||
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
|
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
|
||||||
|
|
||||||
|
@ -175,10 +178,10 @@ public class LauncherProvider extends ContentProvider {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
throw new RuntimeException("Error: attempting to insert null values");
|
throw new RuntimeException("Error: attempting to insert null values");
|
||||||
}
|
}
|
||||||
if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
|
if (!values.containsKey(LauncherSettings.Favorites._ID)) {
|
||||||
throw new RuntimeException("Error: attempting to add item without specifying an id");
|
throw new RuntimeException("Error: attempting to add item without specifying an id");
|
||||||
}
|
}
|
||||||
helper.checkId(table, values);
|
helper.checkId(values);
|
||||||
return (int) db.insert(table, nullColumnHack, values);
|
return (int) db.insert(table, nullColumnHack, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,24 +265,7 @@ public class LauncherProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add screen id if not present
|
return true;
|
||||||
int screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
|
|
||||||
SQLiteStatement stmp = null;
|
|
||||||
try {
|
|
||||||
stmp = mOpenHelper.getWritableDatabase().compileStatement(
|
|
||||||
"INSERT OR IGNORE INTO workspaceScreens (_id, screenRank) " +
|
|
||||||
"select ?, (ifnull(MAX(screenRank), -1)+1) from workspaceScreens");
|
|
||||||
stmp.bindLong(1, screenId);
|
|
||||||
|
|
||||||
ContentValues valuesInserted = new ContentValues();
|
|
||||||
valuesInserted.put(LauncherSettings.BaseLauncherColumns._ID, stmp.executeInsert());
|
|
||||||
mOpenHelper.checkId(WorkspaceScreens.TABLE_NAME, valuesInserted);
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
Utilities.closeSilently(stmp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -404,7 +390,6 @@ public class LauncherProvider extends ContentProvider {
|
||||||
* @return Ids of deleted folders.
|
* @return Ids of deleted folders.
|
||||||
*/
|
*/
|
||||||
private IntArray deleteEmptyFolders() {
|
private IntArray deleteEmptyFolders() {
|
||||||
IntArray folderIds = new IntArray();
|
|
||||||
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
||||||
// Select folders whose id do not match any container value.
|
// Select folders whose id do not match any container value.
|
||||||
|
@ -413,21 +398,19 @@ public class LauncherProvider extends ContentProvider {
|
||||||
+ LauncherSettings.Favorites._ID + " NOT IN (SELECT " +
|
+ LauncherSettings.Favorites._ID + " NOT IN (SELECT " +
|
||||||
LauncherSettings.Favorites.CONTAINER + " FROM "
|
LauncherSettings.Favorites.CONTAINER + " FROM "
|
||||||
+ Favorites.TABLE_NAME + ")";
|
+ Favorites.TABLE_NAME + ")";
|
||||||
try (Cursor c = db.query(Favorites.TABLE_NAME,
|
|
||||||
new String[] {LauncherSettings.Favorites._ID},
|
IntArray folderIds = LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME,
|
||||||
selection, null, null, null, null)) {
|
Favorites._ID, selection, null, null);
|
||||||
LauncherDbUtils.iterateCursor(c, 0, folderIds);
|
|
||||||
}
|
|
||||||
if (!folderIds.isEmpty()) {
|
if (!folderIds.isEmpty()) {
|
||||||
db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
|
db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
|
||||||
LauncherSettings.Favorites._ID, folderIds), null);
|
LauncherSettings.Favorites._ID, folderIds), null);
|
||||||
}
|
}
|
||||||
t.commit();
|
t.commit();
|
||||||
|
return folderIds;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
Log.e(TAG, ex.getMessage(), ex);
|
Log.e(TAG, ex.getMessage(), ex);
|
||||||
folderIds.clear();
|
return new IntArray();
|
||||||
}
|
}
|
||||||
return folderIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -438,7 +421,7 @@ public class LauncherProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Thunk static void addModifiedTime(ContentValues values) {
|
@Thunk static void addModifiedTime(ContentValues values) {
|
||||||
values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
|
values.put(LauncherSettings.Favorites.MODIFIED, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearFlagEmptyDbCreated() {
|
private void clearFlagEmptyDbCreated() {
|
||||||
|
@ -551,11 +534,10 @@ public class LauncherProvider extends ContentProvider {
|
||||||
// Table creation sometimes fails silently, which leads to a crash loop.
|
// Table creation sometimes fails silently, which leads to a crash loop.
|
||||||
// This way, we will try to create a table every time after crash, so the device
|
// This way, we will try to create a table every time after crash, so the device
|
||||||
// would eventually be able to recover.
|
// would eventually be able to recover.
|
||||||
if (!tableExists(Favorites.TABLE_NAME) || !tableExists(WorkspaceScreens.TABLE_NAME)) {
|
if (!tableExists(Favorites.TABLE_NAME)) {
|
||||||
Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
|
Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
|
||||||
// This operation is a no-op if the table already exists.
|
// This operation is a no-op if the table already exists.
|
||||||
addFavoritesTable(getWritableDatabase(), true);
|
addFavoritesTable(getWritableDatabase(), true);
|
||||||
addWorkspacesTable(getWritableDatabase(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initIds();
|
initIds();
|
||||||
|
@ -602,7 +584,6 @@ public class LauncherProvider extends ContentProvider {
|
||||||
mMaxScreenId = 0;
|
mMaxScreenId = 0;
|
||||||
|
|
||||||
addFavoritesTable(db, false);
|
addFavoritesTable(db, false);
|
||||||
addWorkspacesTable(db, false);
|
|
||||||
|
|
||||||
// Fresh and clean launcher DB.
|
// Fresh and clean launcher DB.
|
||||||
mMaxItemId = initializeMaxItemId(db);
|
mMaxItemId = initializeMaxItemId(db);
|
||||||
|
@ -633,46 +614,6 @@ public class LauncherProvider extends ContentProvider {
|
||||||
Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
|
Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addWorkspacesTable(SQLiteDatabase db, boolean optional) {
|
|
||||||
String ifNotExists = optional ? " IF NOT EXISTS " : "";
|
|
||||||
db.execSQL("CREATE TABLE " + ifNotExists + WorkspaceScreens.TABLE_NAME + " (" +
|
|
||||||
LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," +
|
|
||||||
LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
|
|
||||||
LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
|
|
||||||
");");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeOrphanedItems(SQLiteDatabase db) {
|
|
||||||
// Delete items directly on the workspace who's screen id doesn't exist
|
|
||||||
// "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
|
|
||||||
// AND container = -100"
|
|
||||||
String removeOrphanedDesktopItems = "DELETE FROM " + Favorites.TABLE_NAME +
|
|
||||||
" WHERE " +
|
|
||||||
LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
|
|
||||||
LauncherSettings.WorkspaceScreens._ID + " FROM " + WorkspaceScreens.TABLE_NAME + ")" +
|
|
||||||
" AND " +
|
|
||||||
LauncherSettings.Favorites.CONTAINER + " = " +
|
|
||||||
LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
|
||||||
db.execSQL(removeOrphanedDesktopItems);
|
|
||||||
|
|
||||||
// Delete items contained in folders which no longer exist (after above statement)
|
|
||||||
// "DELETE FROM favorites WHERE container <> -100 AND container <> -101 AND container
|
|
||||||
// NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
|
|
||||||
String removeOrphanedFolderItems = "DELETE FROM " + Favorites.TABLE_NAME +
|
|
||||||
" WHERE " +
|
|
||||||
LauncherSettings.Favorites.CONTAINER + " <> " +
|
|
||||||
LauncherSettings.Favorites.CONTAINER_DESKTOP +
|
|
||||||
" AND "
|
|
||||||
+ LauncherSettings.Favorites.CONTAINER + " <> " +
|
|
||||||
LauncherSettings.Favorites.CONTAINER_HOTSEAT +
|
|
||||||
" AND "
|
|
||||||
+ LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
|
|
||||||
LauncherSettings.Favorites._ID + " FROM " + Favorites.TABLE_NAME +
|
|
||||||
" WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
|
|
||||||
LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
|
|
||||||
db.execSQL(removeOrphanedFolderItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(SQLiteDatabase db) {
|
public void onOpen(SQLiteDatabase db) {
|
||||||
super.onOpen(db);
|
super.onOpen(db);
|
||||||
|
@ -681,8 +622,7 @@ public class LauncherProvider extends ContentProvider {
|
||||||
if (!schemaFile.exists()) {
|
if (!schemaFile.exists()) {
|
||||||
handleOneTimeDataUpgrade(db);
|
handleOneTimeDataUpgrade(db);
|
||||||
}
|
}
|
||||||
DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext,
|
DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext);
|
||||||
R.raw.downgrade_schema);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -709,12 +649,8 @@ public class LauncherProvider extends ContentProvider {
|
||||||
switch (oldVersion) {
|
switch (oldVersion) {
|
||||||
// The version cannot be lower that 12, as Launcher3 never supported a lower
|
// The version cannot be lower that 12, as Launcher3 never supported a lower
|
||||||
// version of the DB.
|
// version of the DB.
|
||||||
case 12: {
|
case 12:
|
||||||
// With the new shrink-wrapped and re-orderable workspaces, it makes sense
|
// No-op
|
||||||
// to persist workspace screens and their relative order.
|
|
||||||
mMaxScreenId = 0;
|
|
||||||
addWorkspacesTable(db, false);
|
|
||||||
}
|
|
||||||
case 13: {
|
case 13: {
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
||||||
// Insert new column for holding widget provider name
|
// Insert new column for holding widget provider name
|
||||||
|
@ -728,15 +664,7 @@ public class LauncherProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 14: {
|
case 14: {
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
if (!addIntegerColumn(db, Favorites.MODIFIED, 0)) {
|
||||||
// Insert new column for holding update timestamp
|
|
||||||
db.execSQL("ALTER TABLE favorites " +
|
|
||||||
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
|
|
||||||
db.execSQL("ALTER TABLE workspaceScreens " +
|
|
||||||
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
|
|
||||||
t.commit();
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
Log.e(TAG, ex.getMessage(), ex);
|
|
||||||
// Old version remains, which means we wipe old data
|
// Old version remains, which means we wipe old data
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -747,22 +675,15 @@ public class LauncherProvider extends ContentProvider {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 16: {
|
case 16:
|
||||||
// No-op
|
// No-op
|
||||||
}
|
case 17:
|
||||||
case 17: {
|
// No-op
|
||||||
|
case 18:
|
||||||
// No-op
|
// No-op
|
||||||
}
|
|
||||||
case 18: {
|
|
||||||
// Due to a data loss bug, some users may have items associated with screen ids
|
|
||||||
// which no longer exist. Since this can cause other problems, and since the user
|
|
||||||
// will never see these items anyway, we use database upgrade as an opportunity to
|
|
||||||
// clean things up.
|
|
||||||
removeOrphanedItems(db);
|
|
||||||
}
|
|
||||||
case 19: {
|
case 19: {
|
||||||
// Add userId column
|
// Add userId column
|
||||||
if (!addProfileColumn(db)) {
|
if (!addIntegerColumn(db, Favorites.PROFILE_ID, getDefaultUserSerial())) {
|
||||||
// Old version remains, which means we wipe old data
|
// Old version remains, which means we wipe old data
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -772,10 +693,7 @@ public class LauncherProvider extends ContentProvider {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 21:
|
case 21:
|
||||||
// Recreate workspace table with screen id a primary key
|
// No-op
|
||||||
if (!recreateWorkspaceTable(db)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 22: {
|
case 22: {
|
||||||
if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) {
|
if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) {
|
||||||
// Old version remains, which means we wipe old data
|
// Old version remains, which means we wipe old data
|
||||||
|
@ -794,7 +712,30 @@ public class LauncherProvider extends ContentProvider {
|
||||||
!LauncherDbUtils.prepareScreenZeroToHostQsb(mContext, db)) {
|
!LauncherDbUtils.prepareScreenZeroToHostQsb(mContext, db)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 27:
|
case 27: {
|
||||||
|
// Update the favorites table so that the screen ids are ordered based on
|
||||||
|
// workspace page rank.
|
||||||
|
IntArray finalScreens = LauncherDbUtils.queryIntArray(db, "workspaceScreens",
|
||||||
|
BaseColumns._ID, null, null, "screenRank");
|
||||||
|
int[] original = finalScreens.toArray();
|
||||||
|
Arrays.sort(original);
|
||||||
|
String updatemap = "";
|
||||||
|
for (int i = 0; i < original.length; i++) {
|
||||||
|
if (finalScreens.get(i) != original[i]) {
|
||||||
|
updatemap += String.format(Locale.ENGLISH, " WHEN %1$s=%2$d THEN %3$d",
|
||||||
|
Favorites.SCREEN, finalScreens.get(i), original[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!TextUtils.isEmpty(updatemap)) {
|
||||||
|
String query = String.format(Locale.ENGLISH,
|
||||||
|
"UPDATE %1$s SET %2$s=CASE %3$s ELSE %2$s END WHERE %4$s = %5$d",
|
||||||
|
Favorites.TABLE_NAME, Favorites.SCREEN, updatemap,
|
||||||
|
Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP);
|
||||||
|
db.execSQL(query);
|
||||||
|
}
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS workspaceScreens");
|
||||||
|
}
|
||||||
|
case 28:
|
||||||
// DB Upgraded successfully
|
// DB Upgraded successfully
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -822,7 +763,7 @@ public class LauncherProvider extends ContentProvider {
|
||||||
public void createEmptyDB(SQLiteDatabase db) {
|
public void createEmptyDB(SQLiteDatabase db) {
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
|
db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
|
db.execSQL("DROP TABLE IF EXISTS workspaceScreens");
|
||||||
onCreate(db);
|
onCreate(db);
|
||||||
t.commit();
|
t.commit();
|
||||||
}
|
}
|
||||||
|
@ -845,17 +786,9 @@ public class LauncherProvider extends ContentProvider {
|
||||||
Log.e(TAG, "getAppWidgetIds not supported", e);
|
Log.e(TAG, "getAppWidgetIds not supported", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final IntSet validWidgets = new IntSet();
|
final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(db,
|
||||||
try (Cursor c = db.query(Favorites.TABLE_NAME,
|
Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
|
||||||
new String[] {Favorites.APPWIDGET_ID },
|
"itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
|
||||||
"itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null)) {
|
|
||||||
while (c.moveToNext()) {
|
|
||||||
validWidgets.add(c.getInt(0));
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
Log.w(TAG, "Error getting widgets list", ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int widgetId : allWidgets) {
|
for (int widgetId : allWidgets) {
|
||||||
if (!validWidgets.contains(widgetId)) {
|
if (!validWidgets.contains(widgetId)) {
|
||||||
try {
|
try {
|
||||||
|
@ -910,46 +843,6 @@ public class LauncherProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreates workspace table and migrates data to the new table.
|
|
||||||
*/
|
|
||||||
public boolean recreateWorkspaceTable(SQLiteDatabase db) {
|
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
|
||||||
final IntArray sortedIDs;
|
|
||||||
|
|
||||||
try (Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
|
|
||||||
new String[] {LauncherSettings.WorkspaceScreens._ID},
|
|
||||||
null, null, null, null,
|
|
||||||
LauncherSettings.WorkspaceScreens.SCREEN_RANK)) {
|
|
||||||
// Use LinkedHashSet so that ordering is preserved
|
|
||||||
sortedIDs = LauncherDbUtils.getScreenIdsFromCursor(c);
|
|
||||||
}
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
|
|
||||||
addWorkspacesTable(db, false);
|
|
||||||
|
|
||||||
// Add all screen ids back
|
|
||||||
int total = sortedIDs.size();
|
|
||||||
for (int i = 0; i < total; i++) {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i));
|
|
||||||
values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
||||||
addModifiedTime(values);
|
|
||||||
db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values);
|
|
||||||
}
|
|
||||||
t.commit();
|
|
||||||
|
|
||||||
mMaxScreenId = 0;
|
|
||||||
for (int i = 0; i < sortedIDs.size(); i++) {
|
|
||||||
mMaxScreenId = Math.max(mMaxScreenId, sortedIDs.get(i));
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
// Old version remains, which means we wipe old data
|
|
||||||
Log.e(TAG, ex.getMessage(), ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
|
@Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
||||||
if (addRankColumn) {
|
if (addRankColumn) {
|
||||||
|
@ -979,10 +872,6 @@ public class LauncherProvider extends ContentProvider {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addProfileColumn(SQLiteDatabase db) {
|
|
||||||
return addIntegerColumn(db, Favorites.PROFILE_ID, getDefaultUserSerial());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
|
private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
||||||
db.execSQL("ALTER TABLE favorites ADD COLUMN "
|
db.execSQL("ALTER TABLE favorites ADD COLUMN "
|
||||||
|
@ -1018,17 +907,20 @@ public class LauncherProvider extends ContentProvider {
|
||||||
return dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, values);
|
return dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkId(String table, ContentValues values) {
|
public void checkId(ContentValues values) {
|
||||||
int id = values.getAsInteger(LauncherSettings.BaseLauncherColumns._ID);
|
int id = values.getAsInteger(Favorites._ID);
|
||||||
if (WorkspaceScreens.TABLE_NAME.equals(table)) {
|
mMaxItemId = Math.max(id, mMaxItemId);
|
||||||
mMaxScreenId = Math.max(id, mMaxScreenId);
|
|
||||||
} else {
|
Integer screen = values.getAsInteger(Favorites.SCREEN);
|
||||||
mMaxItemId = Math.max(id, mMaxItemId);
|
Integer container = values.getAsInteger(Favorites.CONTAINER);
|
||||||
|
if (screen != null && container != null
|
||||||
|
&& container.intValue() == Favorites.CONTAINER_DESKTOP) {
|
||||||
|
mMaxScreenId = Math.max(screen, mMaxScreenId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int initializeMaxItemId(SQLiteDatabase db) {
|
private int initializeMaxItemId(SQLiteDatabase db) {
|
||||||
return getMaxId(db, Favorites.TABLE_NAME);
|
return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s", Favorites._ID, Favorites.TABLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a new ID to use for an workspace screen in your database. This method
|
// Generates a new ID to use for an workspace screen in your database. This method
|
||||||
|
@ -1045,34 +937,18 @@ public class LauncherProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int initializeMaxScreenId(SQLiteDatabase db) {
|
private int initializeMaxScreenId(SQLiteDatabase db) {
|
||||||
return getMaxId(db, WorkspaceScreens.TABLE_NAME);
|
return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d",
|
||||||
|
Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
|
||||||
|
Favorites.CONTAINER_DESKTOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
|
@Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
|
||||||
IntArray screenIds = new IntArray();
|
|
||||||
// TODO: Use multiple loaders with fall-back and transaction.
|
// TODO: Use multiple loaders with fall-back and transaction.
|
||||||
int count = loader.loadLayout(db, screenIds);
|
int count = loader.loadLayout(db, new IntArray());
|
||||||
|
|
||||||
// Add the screens specified by the items above
|
|
||||||
int[] sortedScreenIds = screenIds.toArray();
|
|
||||||
Arrays.sort(sortedScreenIds);
|
|
||||||
int rank = 0;
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
for (int id : sortedScreenIds) {
|
|
||||||
values.clear();
|
|
||||||
values.put(LauncherSettings.WorkspaceScreens._ID, id);
|
|
||||||
values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
|
|
||||||
if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
|
|
||||||
throw new RuntimeException("Failed initialize screen table"
|
|
||||||
+ "from default layout");
|
|
||||||
}
|
|
||||||
rank++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the max ids are initialized
|
// Ensure that the max ids are initialized
|
||||||
mMaxItemId = initializeMaxItemId(db);
|
mMaxItemId = initializeMaxItemId(db);
|
||||||
mMaxScreenId = initializeMaxScreenId(db);
|
mMaxScreenId = initializeMaxScreenId(db);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1080,22 +956,14 @@ public class LauncherProvider extends ContentProvider {
|
||||||
/**
|
/**
|
||||||
* @return the max _id in the provided table.
|
* @return the max _id in the provided table.
|
||||||
*/
|
*/
|
||||||
@Thunk static int getMaxId(SQLiteDatabase db, String table) {
|
@Thunk static int getMaxId(SQLiteDatabase db, String query, Object... args) {
|
||||||
Cursor c = db.rawQuery("SELECT MAX(_id) FROM " + table, null);
|
int max = (int) DatabaseUtils.longForQuery(db,
|
||||||
// get the result
|
String.format(Locale.ENGLISH, query, args),
|
||||||
int id = -1;
|
null);
|
||||||
if (c != null && c.moveToNext()) {
|
if (max < 0) {
|
||||||
id = c.getInt(0);
|
throw new RuntimeException("Error: could not query max id");
|
||||||
}
|
}
|
||||||
if (c != null) {
|
return max;
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == -1) {
|
|
||||||
throw new RuntimeException("Error: could not query max id in " + table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class SqlArguments {
|
static class SqlArguments {
|
||||||
|
|
|
@ -26,16 +26,17 @@ import android.provider.BaseColumns;
|
||||||
* Settings related utilities.
|
* Settings related utilities.
|
||||||
*/
|
*/
|
||||||
public class LauncherSettings {
|
public class LauncherSettings {
|
||||||
/** Columns required on table staht will be subject to backup and restore. */
|
|
||||||
static interface ChangeLogColumns extends BaseColumns {
|
/**
|
||||||
|
* Favorites.
|
||||||
|
*/
|
||||||
|
public static final class Favorites implements BaseColumns {
|
||||||
/**
|
/**
|
||||||
* The time of the last update to this row.
|
* The time of the last update to this row.
|
||||||
* <P>Type: INTEGER</P>
|
* <P>Type: INTEGER</P>
|
||||||
*/
|
*/
|
||||||
public static final String MODIFIED = "modified";
|
public static final String MODIFIED = "modified";
|
||||||
}
|
|
||||||
|
|
||||||
static public interface BaseLauncherColumns extends ChangeLogColumns {
|
|
||||||
/**
|
/**
|
||||||
* Descriptive name of the gesture that can be displayed to the user.
|
* Descriptive name of the gesture that can be displayed to the user.
|
||||||
* <P>Type: TEXT</P>
|
* <P>Type: TEXT</P>
|
||||||
|
@ -84,34 +85,6 @@ public class LauncherSettings {
|
||||||
* <P>Type: BLOB</P>
|
* <P>Type: BLOB</P>
|
||||||
*/
|
*/
|
||||||
public static final String ICON = "icon";
|
public static final String ICON = "icon";
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workspace Screens.
|
|
||||||
*
|
|
||||||
* Tracks the order of workspace screens.
|
|
||||||
*/
|
|
||||||
public static final class WorkspaceScreens implements ChangeLogColumns {
|
|
||||||
|
|
||||||
public static final String TABLE_NAME = "workspaceScreens";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The content:// style URL for this table
|
|
||||||
*/
|
|
||||||
public static final Uri CONTENT_URI = Uri.parse("content://" +
|
|
||||||
LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The rank of this screen -- ie. how it is ordered relative to the other screens.
|
|
||||||
* <P>Type: INTEGER</P>
|
|
||||||
*/
|
|
||||||
public static final String SCREEN_RANK = "screenRank";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Favorites.
|
|
||||||
*/
|
|
||||||
public static final class Favorites implements BaseLauncherColumns {
|
|
||||||
|
|
||||||
public static final String TABLE_NAME = "favorites";
|
public static final String TABLE_NAME = "favorites";
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
|
||||||
Intent intent = null;
|
Intent intent = null;
|
||||||
UserHandle user = null;
|
UserHandle user = null;
|
||||||
if (item != null &&
|
if (item != null &&
|
||||||
item.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
|
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
|
||||||
intent = item.getIntent();
|
intent = item.getIntent();
|
||||||
user = item.user;
|
user = item.user;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class ShortcutInfo extends ItemInfoWithIcon {
|
||||||
private int mInstallProgress;
|
private int mInstallProgress;
|
||||||
|
|
||||||
public ShortcutInfo() {
|
public ShortcutInfo() {
|
||||||
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
|
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShortcutInfo(ShortcutInfo info) {
|
public ShortcutInfo(ShortcutInfo info) {
|
||||||
|
@ -114,24 +114,23 @@ public class ShortcutInfo extends ItemInfoWithIcon {
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
|
public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
|
||||||
user = shortcutInfo.getUserHandle();
|
user = shortcutInfo.getUserHandle();
|
||||||
itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||||
updateFromDeepShortcutInfo(shortcutInfo, context);
|
updateFromDeepShortcutInfo(shortcutInfo, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAddToDatabase(ContentWriter writer) {
|
public void onAddToDatabase(ContentWriter writer) {
|
||||||
super.onAddToDatabase(writer);
|
super.onAddToDatabase(writer);
|
||||||
writer.put(LauncherSettings.BaseLauncherColumns.TITLE, title)
|
writer.put(Favorites.TITLE, title)
|
||||||
.put(LauncherSettings.BaseLauncherColumns.INTENT, getIntent())
|
.put(Favorites.INTENT, getIntent())
|
||||||
.put(LauncherSettings.Favorites.RESTORED, status);
|
.put(Favorites.RESTORED, status);
|
||||||
|
|
||||||
if (!usingLowResIcon()) {
|
if (!usingLowResIcon()) {
|
||||||
writer.putIcon(iconBitmap, user);
|
writer.putIcon(iconBitmap, user);
|
||||||
}
|
}
|
||||||
if (iconResource != null) {
|
if (iconResource != null) {
|
||||||
writer.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, iconResource.packageName)
|
writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)
|
||||||
.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE,
|
.put(Favorites.ICON_RESOURCE, iconResource.resourceName);
|
||||||
iconResource.resourceName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +188,7 @@ public class ShortcutInfo extends ItemInfoWithIcon {
|
||||||
@Override
|
@Override
|
||||||
public ComponentName getTargetComponent() {
|
public ComponentName getTargetComponent() {
|
||||||
ComponentName cn = super.getTargetComponent();
|
ComponentName cn = super.getTargetComponent();
|
||||||
if (cn == null && (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||||
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
|
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
|
||||||
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
|
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
|
||||||
// a packageName. In that case create a dummy componentName instead of adding additional
|
// a packageName. In that case create a dummy componentName instead of adding additional
|
||||||
|
|
|
@ -619,9 +619,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
// if this is the last screen, convert it to the empty screen
|
// if this is the last screen, convert it to the empty screen
|
||||||
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
|
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
|
||||||
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
|
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
|
||||||
|
|
||||||
// Update the model if we have changed any screens
|
|
||||||
LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,9 +725,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
mWorkspaceScreens.put(newId, cl);
|
mWorkspaceScreens.put(newId, cl);
|
||||||
mScreenOrder.add(newId);
|
mScreenOrder.add(newId);
|
||||||
|
|
||||||
// Update the model for the new screen
|
|
||||||
LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
|
|
||||||
|
|
||||||
return newId;
|
return newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,13 +822,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!removeScreens.isEmpty()) {
|
|
||||||
// Update the model if we have changed any screens
|
|
||||||
mLauncher.getModelWriter().enqueueDeleteRunnable(
|
|
||||||
() -> LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pageShift >= 0) {
|
if (pageShift >= 0) {
|
||||||
setCurrentPage(currentPage - pageShift);
|
setCurrentPage(currentPage - pageShift);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package com.android.launcher3.model;
|
package com.android.launcher3.model;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.LongSparseArray;
|
import android.util.LongSparseArray;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.android.launcher3.AllAppsList;
|
import com.android.launcher3.AllAppsList;
|
||||||
import com.android.launcher3.AppInfo;
|
import com.android.launcher3.AppInfo;
|
||||||
import com.android.launcher3.FolderInfo;
|
import com.android.launcher3.FolderInfo;
|
||||||
|
@ -27,7 +27,6 @@ import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||||
import com.android.launcher3.LauncherModel;
|
|
||||||
import com.android.launcher3.LauncherModel.CallbackTask;
|
import com.android.launcher3.LauncherModel.CallbackTask;
|
||||||
import com.android.launcher3.LauncherModel.Callbacks;
|
import com.android.launcher3.LauncherModel.Callbacks;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
|
@ -58,16 +57,12 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
||||||
if (mItemList.isEmpty()) {
|
if (mItemList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Context context = app.getContext();
|
|
||||||
|
|
||||||
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
|
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
|
||||||
final IntArray addedWorkspaceScreensFinal = new IntArray();
|
final IntArray addedWorkspaceScreensFinal = new IntArray();
|
||||||
|
|
||||||
// Get the list of workspace screens. We need to append to this list and
|
|
||||||
// can not use sBgWorkspaceScreens because loadWorkspace() may not have been
|
|
||||||
// called.
|
|
||||||
IntArray workspaceScreens = LauncherModel.loadWorkspaceScreensDb(context);
|
|
||||||
synchronized(dataModel) {
|
synchronized(dataModel) {
|
||||||
|
IntArray workspaceScreens = dataModel.workspaceScreens.clone();
|
||||||
|
|
||||||
List<ItemInfo> filteredItems = new ArrayList<>();
|
List<ItemInfo> filteredItems = new ArrayList<>();
|
||||||
for (Pair<ItemInfo, Object> entry : mItemList) {
|
for (Pair<ItemInfo, Object> entry : mItemList) {
|
||||||
|
@ -116,9 +111,6 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the workspace screens
|
|
||||||
updateScreens(context, workspaceScreens);
|
|
||||||
|
|
||||||
if (!addedItemsFinal.isEmpty()) {
|
if (!addedItemsFinal.isEmpty()) {
|
||||||
scheduleCallbackTask(new CallbackTask() {
|
scheduleCallbackTask(new CallbackTask() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -143,10 +135,6 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateScreens(Context context, IntArray workspaceScreens) {
|
|
||||||
LauncherModel.updateWorkspaceScreenOrder(context, workspaceScreens);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the shortcuts already exists on the workspace. This must be called after
|
* Returns true if the shortcuts already exists on the workspace. This must be called after
|
||||||
* the workspace has been loaded. We identify a shortcut by its intent.
|
* the workspace has been loaded. We identify a shortcut by its intent.
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.database.sqlite.SQLiteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
|
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
|
||||||
import com.android.launcher3.util.IOUtils;
|
import com.android.launcher3.util.IOUtils;
|
||||||
|
|
||||||
|
@ -87,8 +88,7 @@ public class DbDowngradeHelper {
|
||||||
return helper;
|
return helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateSchemaFile(File schemaFile, int expectedVersion,
|
public static void updateSchemaFile(File schemaFile, int expectedVersion, Context context) {
|
||||||
Context context, int schemaResId) {
|
|
||||||
try {
|
try {
|
||||||
if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) {
|
if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) {
|
||||||
return;
|
return;
|
||||||
|
@ -99,7 +99,7 @@ public class DbDowngradeHelper {
|
||||||
|
|
||||||
// Write the updated schema
|
// Write the updated schema
|
||||||
try (FileOutputStream fos = new FileOutputStream(schemaFile);
|
try (FileOutputStream fos = new FileOutputStream(schemaFile);
|
||||||
InputStream in = context.getResources().openRawResource(schemaResId)) {
|
InputStream in = context.getResources().openRawResource(R.raw.downgrade_schema)) {
|
||||||
IOUtils.copy(in, fos);
|
IOUtils.copy(in, fos);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Error writing schema file", e);
|
Log.e(TAG, "Error writing schema file", e);
|
||||||
|
|
|
@ -13,14 +13,12 @@ import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.launcher3.InvariantDeviceProfile;
|
import com.android.launcher3.InvariantDeviceProfile;
|
||||||
import com.android.launcher3.ItemInfo;
|
import com.android.launcher3.ItemInfo;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||||
import com.android.launcher3.LauncherModel;
|
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
|
@ -31,12 +29,14 @@ import com.android.launcher3.compat.PackageInstallerCompat;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.util.GridOccupancy;
|
import com.android.launcher3.util.GridOccupancy;
|
||||||
import com.android.launcher3.util.IntArray;
|
import com.android.launcher3.util.IntArray;
|
||||||
|
import com.android.launcher3.util.IntSet;
|
||||||
import com.android.launcher3.util.IntSparseArrayMap;
|
import com.android.launcher3.util.IntSparseArrayMap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
|
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
|
||||||
|
@ -180,11 +180,25 @@ public class GridSizeMigrationTask {
|
||||||
return applyOperations();
|
return applyOperations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static IntArray getWorkspaceScreenIds(Context context) {
|
||||||
|
IntSet set = new IntSet();
|
||||||
|
try (Cursor c = context.getContentResolver().query(Favorites.CONTENT_URI,
|
||||||
|
new String[] {Favorites.SCREEN},
|
||||||
|
Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
|
||||||
|
null, Favorites.SCREEN)) {
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
set.add(c.getInt(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set.getArray();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if any DB change was made
|
* @return true if any DB change was made
|
||||||
*/
|
*/
|
||||||
protected boolean migrateWorkspace() throws Exception {
|
protected boolean migrateWorkspace() throws Exception {
|
||||||
IntArray allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
|
IntArray allScreens = getWorkspaceScreenIds(mContext);
|
||||||
if (allScreens.isEmpty()) {
|
if (allScreens.isEmpty()) {
|
||||||
throw new Exception("Unable to get workspace screens");
|
throw new Exception("Unable to get workspace screens");
|
||||||
}
|
}
|
||||||
|
@ -216,9 +230,8 @@ public class GridSizeMigrationTask {
|
||||||
int newScreenId = LauncherSettings.Settings.call(
|
int newScreenId = LauncherSettings.Settings.call(
|
||||||
mContext.getContentResolver(),
|
mContext.getContentResolver(),
|
||||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
|
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
|
||||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
||||||
|
|
||||||
allScreens.add(newScreenId);
|
|
||||||
for (DbEntry item : placement.finalPlacedItems) {
|
for (DbEntry item : placement.finalPlacedItems) {
|
||||||
if (!mCarryOver.remove(itemMap.get(item.id))) {
|
if (!mCarryOver.remove(itemMap.get(item.id))) {
|
||||||
throw new Exception("Unable to find matching items");
|
throw new Exception("Unable to find matching items");
|
||||||
|
@ -231,19 +244,6 @@ public class GridSizeMigrationTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (!mCarryOver.isEmpty());
|
} while (!mCarryOver.isEmpty());
|
||||||
|
|
||||||
// Update screens
|
|
||||||
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
|
|
||||||
mUpdateOperations.add(ContentProviderOperation.newDelete(uri).build());
|
|
||||||
int count = allScreens.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
ContentValues v = new ContentValues();
|
|
||||||
int screenId = allScreens.get(i);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
||||||
mUpdateOperations.add(ContentProviderOperation.newInsert(uri).withValues(
|
|
||||||
v).build());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return applyOperations();
|
return applyOperations();
|
||||||
}
|
}
|
||||||
|
|
|
@ -380,7 +380,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
* otherwise marks it for deletion.
|
* otherwise marks it for deletion.
|
||||||
*/
|
*/
|
||||||
public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
|
public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
|
||||||
if (checkItemPlacement(info, dataModel.workspaceScreens)) {
|
if (checkItemPlacement(info)) {
|
||||||
dataModel.addItem(mContext, info, false);
|
dataModel.addItem(mContext, info, false);
|
||||||
} else {
|
} else {
|
||||||
markDeleted("Item position overlap");
|
markDeleted("Item position overlap");
|
||||||
|
@ -390,7 +390,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
/**
|
/**
|
||||||
* check & update map of what's occupied; used to discard overlapping/invalid items
|
* check & update map of what's occupied; used to discard overlapping/invalid items
|
||||||
*/
|
*/
|
||||||
protected boolean checkItemPlacement(ItemInfo item, IntArray workspaceScreens) {
|
protected boolean checkItemPlacement(ItemInfo item) {
|
||||||
int containerIndex = item.screenId;
|
int containerIndex = item.screenId;
|
||||||
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
||||||
final GridOccupancy hotseatOccupancy =
|
final GridOccupancy hotseatOccupancy =
|
||||||
|
@ -420,12 +420,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||||
occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
|
occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
} else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
||||||
if (!workspaceScreens.contains(item.screenId)) {
|
|
||||||
// The item has an invalid screen id.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Skip further checking if it is not the hotseat or workspace container
|
// Skip further checking if it is not the hotseat or workspace container
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||||
import com.android.launcher3.util.ComponentKey;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.launcher3.util.IntArray;
|
import com.android.launcher3.util.IntArray;
|
||||||
|
import com.android.launcher3.util.IntSet;
|
||||||
import com.android.launcher3.util.LooperIdleLock;
|
import com.android.launcher3.util.LooperIdleLock;
|
||||||
import com.android.launcher3.util.MultiHashMap;
|
import com.android.launcher3.util.MultiHashMap;
|
||||||
import com.android.launcher3.util.PackageManagerHelper;
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
|
@ -293,7 +294,6 @@ public class LoaderTask implements Runnable {
|
||||||
final HashMap<String, SessionInfo> installingPkgs =
|
final HashMap<String, SessionInfo> installingPkgs =
|
||||||
mPackageInstaller.updateAndGetActiveSessionCache();
|
mPackageInstaller.updateAndGetActiveSessionCache();
|
||||||
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
|
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
|
||||||
mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
|
|
||||||
|
|
||||||
Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
|
Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
|
||||||
final LoaderCursor c = new LoaderCursor(contentResolver.query(
|
final LoaderCursor c = new LoaderCursor(contentResolver.query(
|
||||||
|
@ -780,21 +780,15 @@ public class LoaderTask implements Runnable {
|
||||||
new Handler(LauncherModel.getWorkerLooper()));
|
new Handler(LauncherModel.getWorkerLooper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any empty screens
|
// Initialize the screens array. Using an InstSet ensures that the screen ids
|
||||||
IntArray unusedScreens = mBgDataModel.workspaceScreens.clone();
|
// are sorted.
|
||||||
|
IntSet screenSet = new IntSet();
|
||||||
for (ItemInfo item: mBgDataModel.itemsIdMap) {
|
for (ItemInfo item: mBgDataModel.itemsIdMap) {
|
||||||
int screenId = item.screenId;
|
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
||||||
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
|
screenSet.add(item.screenId);
|
||||||
unusedScreens.contains(screenId)) {
|
|
||||||
unusedScreens.removeValue(screenId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mBgDataModel.workspaceScreens.addAll(screenSet.getArray());
|
||||||
// If there are any empty screens remove them, and update.
|
|
||||||
if (unusedScreens.size() != 0) {
|
|
||||||
mBgDataModel.workspaceScreens.removeAllValues(unusedScreens);
|
|
||||||
LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -333,7 +333,7 @@ public class ModelWriter {
|
||||||
* {@link #commitDelete()} is called (or abandoned if {@link #abortDelete()} is called).
|
* {@link #commitDelete()} is called (or abandoned if {@link #abortDelete()} is called).
|
||||||
* Otherwise, we run the Runnable immediately.
|
* Otherwise, we run the Runnable immediately.
|
||||||
*/
|
*/
|
||||||
public void enqueueDeleteRunnable(Runnable r) {
|
private void enqueueDeleteRunnable(Runnable r) {
|
||||||
if (mPreparingToUndo) {
|
if (mPreparingToUndo) {
|
||||||
mDeleteRunnables.add(r);
|
mDeleteRunnables.add(r);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -33,7 +33,6 @@ import android.os.Process;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
import android.util.SparseIntArray;
|
|
||||||
|
|
||||||
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
|
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
|
||||||
import com.android.launcher3.DefaultLayoutParser;
|
import com.android.launcher3.DefaultLayoutParser;
|
||||||
|
@ -43,7 +42,6 @@ import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.LauncherSettings.Settings;
|
import com.android.launcher3.LauncherSettings.Settings;
|
||||||
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
|
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.Workspace;
|
import com.android.launcher3.Workspace;
|
||||||
import com.android.launcher3.compat.UserManagerCompat;
|
import com.android.launcher3.compat.UserManagerCompat;
|
||||||
|
@ -72,7 +70,6 @@ public class ImportDataTask {
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
private final Uri mOtherScreensUri;
|
|
||||||
private final Uri mOtherFavoritesUri;
|
private final Uri mOtherFavoritesUri;
|
||||||
|
|
||||||
private int mHotseatSize;
|
private int mHotseatSize;
|
||||||
|
@ -81,41 +78,14 @@ public class ImportDataTask {
|
||||||
|
|
||||||
private ImportDataTask(Context context, String sourceAuthority) {
|
private ImportDataTask(Context context, String sourceAuthority) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mOtherScreensUri = Uri.parse("content://" +
|
|
||||||
sourceAuthority + "/" + WorkspaceScreens.TABLE_NAME);
|
|
||||||
mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME);
|
mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean importWorkspace() throws Exception {
|
public boolean importWorkspace() throws Exception {
|
||||||
IntArray allScreens = LauncherDbUtils.getScreenIdsFromCursor(
|
|
||||||
mContext.getContentResolver().query(mOtherScreensUri, null, null, null,
|
|
||||||
LauncherSettings.WorkspaceScreens.SCREEN_RANK));
|
|
||||||
FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
|
FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
|
||||||
|
|
||||||
// During import we reset the screen IDs to 0-indexed values.
|
|
||||||
if (allScreens.isEmpty()) {
|
|
||||||
// No thing to migrate
|
|
||||||
FileLog.e(TAG, "No data found to import");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0;
|
mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0;
|
||||||
|
importWorkspaceItems();
|
||||||
// Build screen update
|
|
||||||
ArrayList<ContentProviderOperation> screenOps = new ArrayList<>();
|
|
||||||
int count = allScreens.size();
|
|
||||||
SparseIntArray screenIdMap = new SparseIntArray(count);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
ContentValues v = new ContentValues();
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens._ID, i);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
||||||
screenIdMap.put(allScreens.get(i), i);
|
|
||||||
screenOps.add(ContentProviderOperation.newInsert(
|
|
||||||
LauncherSettings.WorkspaceScreens.CONTENT_URI).withValues(v).build());
|
|
||||||
}
|
|
||||||
mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, screenOps);
|
|
||||||
importWorkspaceItems(allScreens.get(0), screenIdMap);
|
|
||||||
|
|
||||||
GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
|
GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
|
||||||
|
|
||||||
// Create empty DB flag.
|
// Create empty DB flag.
|
||||||
|
@ -129,17 +99,17 @@ public class ImportDataTask {
|
||||||
* 2) For home screen entries, maps the screen id based on {@param screenIdMap}
|
* 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.
|
* 3) In the end fills any holes in hotseat with items from default hotseat layout.
|
||||||
*/
|
*/
|
||||||
private void importWorkspaceItems(
|
private void importWorkspaceItems() throws Exception {
|
||||||
int firstScreenId, SparseIntArray screenIdMap) throws Exception {
|
|
||||||
String profileId = Long.toString(UserManagerCompat.getInstance(mContext)
|
String profileId = Long.toString(UserManagerCompat.getInstance(mContext)
|
||||||
.getSerialNumberForUser(Process.myUserHandle()));
|
.getSerialNumberForUser(Process.myUserHandle()));
|
||||||
|
|
||||||
boolean createEmptyRowOnFirstScreen;
|
boolean createEmptyRowOnFirstScreen;
|
||||||
if (FeatureFlags.QSB_ON_FIRST_SCREEN.get()) {
|
if (FeatureFlags.QSB_ON_FIRST_SCREEN.get()) {
|
||||||
try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
|
try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
|
||||||
// get items on the first row of the first screen
|
// get items on the first row of the first screen (min screen id)
|
||||||
"profileId = ? AND container = -100 AND screen = ? AND cellY = 0",
|
"profileId = ? AND container = -100 AND cellY = 0 AND screen = " +
|
||||||
new String[]{profileId, Integer.toString(firstScreenId)},
|
"(SELECT MIN(screen) FROM favorites WHERE container = -100)",
|
||||||
|
new String[]{profileId},
|
||||||
null)) {
|
null)) {
|
||||||
// First row of first screen is not empty
|
// First row of first screen is not empty
|
||||||
createEmptyRowOnFirstScreen = c.moveToNext();
|
createEmptyRowOnFirstScreen = c.moveToNext();
|
||||||
|
@ -163,7 +133,7 @@ public class ImportDataTask {
|
||||||
Favorites.PROFILE_ID + " = ?", new String[]{profileId},
|
Favorites.PROFILE_ID + " = ?", new String[]{profileId},
|
||||||
// Get the items sorted by container, so that the folders are loaded
|
// Get the items sorted by container, so that the folders are loaded
|
||||||
// before the corresponding items.
|
// before the corresponding items.
|
||||||
Favorites.CONTAINER)) {
|
Favorites.CONTAINER + " , " + Favorites.SCREEN)) {
|
||||||
|
|
||||||
// various columns we expect to exist.
|
// various columns we expect to exist.
|
||||||
final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
|
final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
|
||||||
|
@ -185,6 +155,7 @@ public class ImportDataTask {
|
||||||
SparseBooleanArray mValidFolders = new SparseBooleanArray();
|
SparseBooleanArray mValidFolders = new SparseBooleanArray();
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
|
Integer firstScreenId = null;
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
values.clear();
|
values.clear();
|
||||||
int id = c.getInt(idIndex);
|
int id = c.getInt(idIndex);
|
||||||
|
@ -201,16 +172,21 @@ public class ImportDataTask {
|
||||||
|
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case Favorites.CONTAINER_DESKTOP: {
|
case Favorites.CONTAINER_DESKTOP: {
|
||||||
Integer newScreenId = screenIdMap.get(screen);
|
if (screen < Workspace.FIRST_SCREEN_ID) {
|
||||||
if (newScreenId == null) {
|
FileLog.d(TAG, String.format(
|
||||||
FileLog.d(TAG, String.format("Skipping item %d, type %d not on a valid screen %d", id, type, screen));
|
"Skipping item %d, type %d not on a valid screen %d",
|
||||||
|
id, type, screen));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (firstScreenId == null) {
|
||||||
|
firstScreenId = screen;
|
||||||
|
}
|
||||||
// Reset the screen to 0-index value
|
// Reset the screen to 0-index value
|
||||||
screen = newScreenId;
|
if (createEmptyRowOnFirstScreen && firstScreenId.equals(screen)) {
|
||||||
if (createEmptyRowOnFirstScreen && screen == Workspace.FIRST_SCREEN_ID) {
|
|
||||||
// Shift items by 1.
|
// Shift items by 1.
|
||||||
cellY++;
|
cellY++;
|
||||||
|
// Change the screen id to first screen
|
||||||
|
screen = Workspace.FIRST_SCREEN_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX);
|
mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX);
|
||||||
|
@ -218,7 +194,7 @@ public class ImportDataTask {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Favorites.CONTAINER_HOTSEAT: {
|
case Favorites.CONTAINER_HOTSEAT: {
|
||||||
mHotseatSize = Math.max(mHotseatSize, (int) screen + 1);
|
mHotseatSize = Math.max(mHotseatSize, screen + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -25,11 +25,9 @@ import android.util.Log;
|
||||||
|
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.LauncherSettings.Favorites;
|
import com.android.launcher3.LauncherSettings.Favorites;
|
||||||
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
|
|
||||||
import com.android.launcher3.util.IntArray;
|
import com.android.launcher3.util.IntArray;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Locale;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of utility methods for Launcher DB used for DB updates and migration.
|
* A set of utility methods for Launcher DB used for DB updates and migration.
|
||||||
|
@ -47,26 +45,25 @@ public class LauncherDbUtils {
|
||||||
*/
|
*/
|
||||||
public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
|
public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
|
||||||
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
|
||||||
// Get the existing screens
|
// Get the first screen
|
||||||
IntArray screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME,
|
final int firstScreenId;
|
||||||
null, null, null, null, null, WorkspaceScreens.SCREEN_RANK));
|
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 (screenIds.isEmpty()) {
|
if (!c.moveToNext()) {
|
||||||
// No update needed
|
// No update needed
|
||||||
t.commit();
|
t.commit();
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
if (screenIds.get(0) != 0) {
|
|
||||||
// First screen is not 0, we need to rename screens
|
|
||||||
if (screenIds.contains(0)) {
|
|
||||||
// There is already a screen 0. First rename it to a different screen.
|
|
||||||
int newScreenId = 1;
|
|
||||||
while (screenIds.contains(newScreenId)) newScreenId++;
|
|
||||||
renameScreen(db, 0, newScreenId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstScreenId = c.getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstScreenId != 0) {
|
||||||
// Rename the first screen to 0.
|
// Rename the first screen to 0.
|
||||||
renameScreen(db, screenIds.get(0), 0);
|
renameScreen(db, firstScreenId, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the first row is empty
|
// Check if the first row is empty
|
||||||
|
@ -89,31 +86,19 @@ public class LauncherDbUtils {
|
||||||
|
|
||||||
private static void renameScreen(SQLiteDatabase db, int oldScreen, int newScreen) {
|
private static void renameScreen(SQLiteDatabase db, int oldScreen, int newScreen) {
|
||||||
String[] whereParams = new String[] { Integer.toString(oldScreen) };
|
String[] whereParams = new String[] { Integer.toString(oldScreen) };
|
||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(WorkspaceScreens._ID, newScreen);
|
|
||||||
db.update(WorkspaceScreens.TABLE_NAME, values, "_id = ?", whereParams);
|
|
||||||
|
|
||||||
values.clear();
|
|
||||||
values.put(Favorites.SCREEN, newScreen);
|
values.put(Favorites.SCREEN, newScreen);
|
||||||
db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
|
db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static IntArray queryIntArray(SQLiteDatabase db, String tableName, String columnName,
|
||||||
* Parses the cursor containing workspace screens table and returns the list of screen IDs
|
String selection, String groupBy, String orderBy) {
|
||||||
*/
|
IntArray out = new IntArray();
|
||||||
public static IntArray getScreenIdsFromCursor(Cursor sc) {
|
try (Cursor c = db.query(tableName, new String[] { columnName }, selection, null,
|
||||||
try {
|
groupBy, null, orderBy)) {
|
||||||
return iterateCursor(sc,
|
while (c.moveToNext()) {
|
||||||
sc.getColumnIndexOrThrow(WorkspaceScreens._ID), new IntArray());
|
out.add(c.getInt(0));
|
||||||
} finally {
|
}
|
||||||
sc.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntArray iterateCursor(Cursor c, int columnIndex, IntArray out) {
|
|
||||||
while (c.moveToNext()) {
|
|
||||||
out.add(c.getInt(columnIndex));
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,4 +48,15 @@ public class IntSet {
|
||||||
public int size() {
|
public int size() {
|
||||||
return mArray.size();
|
return mArray.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntArray getArray() {
|
||||||
|
return mArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntSet wrap(IntArray array) {
|
||||||
|
IntSet set = new IntSet();
|
||||||
|
set.mArray.addAll(array);
|
||||||
|
Arrays.sort(set.mArray.mValues, 0, set.mArray.mSize);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,12 @@ import com.android.launcher3.ShortcutInfo;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
import com.android.launcher3.util.IntArray;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static com.android.launcher3.LauncherSettings.BaseLauncherColumns.INTENT;
|
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
|
import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
|
import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
|
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
|
||||||
|
@ -148,81 +144,57 @@ public class LoaderCursorTest {
|
||||||
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
|
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkItemPlacement_wrongWorkspaceScreen() {
|
|
||||||
IntArray workspaceScreens = IntArray.wrap(1, 3);
|
|
||||||
mIDP.numRows = 4;
|
|
||||||
mIDP.numColumns = 4;
|
|
||||||
mIDP.numHotseatIcons = 3;
|
|
||||||
|
|
||||||
// Item on unknown screen are not placed
|
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 4), workspaceScreens));
|
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 5), workspaceScreens));
|
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), workspaceScreens));
|
|
||||||
|
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), workspaceScreens));
|
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 3), workspaceScreens));
|
|
||||||
|
|
||||||
}
|
|
||||||
@Test
|
@Test
|
||||||
public void checkItemPlacement_outsideBounds() {
|
public void checkItemPlacement_outsideBounds() {
|
||||||
IntArray workspaceScreens = IntArray.wrap(1, 2);
|
|
||||||
mIDP.numRows = 4;
|
mIDP.numRows = 4;
|
||||||
mIDP.numColumns = 4;
|
mIDP.numColumns = 4;
|
||||||
mIDP.numHotseatIcons = 3;
|
mIDP.numHotseatIcons = 3;
|
||||||
|
|
||||||
// Item outside screen bounds are not placed
|
// Item outside screen bounds are not placed
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
assertFalse(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1), workspaceScreens));
|
newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkItemPlacement_overlappingItems() {
|
public void checkItemPlacement_overlappingItems() {
|
||||||
IntArray workspaceScreens = IntArray.wrap(1, 2);
|
|
||||||
mIDP.numRows = 4;
|
mIDP.numRows = 4;
|
||||||
mIDP.numColumns = 4;
|
mIDP.numColumns = 4;
|
||||||
mIDP.numHotseatIcons = 3;
|
mIDP.numHotseatIcons = 3;
|
||||||
|
|
||||||
// Overlapping items are not placed
|
// Overlapping items are not placed
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
assertTrue(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), workspaceScreens));
|
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1)));
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
assertFalse(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1), workspaceScreens));
|
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1)));
|
||||||
|
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
assertTrue(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), workspaceScreens));
|
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2)));
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
assertFalse(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2), workspaceScreens));
|
newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2)));
|
||||||
|
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
assertTrue(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1), workspaceScreens));
|
newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1)));
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
assertTrue(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1), workspaceScreens));
|
newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1)));
|
||||||
|
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
assertFalse(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1), workspaceScreens));
|
newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkItemPlacement_hotseat() {
|
public void checkItemPlacement_hotseat() {
|
||||||
IntArray workspaceScreens = new IntArray();
|
|
||||||
mIDP.numRows = 4;
|
mIDP.numRows = 4;
|
||||||
mIDP.numColumns = 4;
|
mIDP.numColumns = 4;
|
||||||
mIDP.numHotseatIcons = 3;
|
mIDP.numHotseatIcons = 3;
|
||||||
|
|
||||||
// Hotseat items are only placed based on screenId
|
// Hotseat items are only placed based on screenId
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
assertTrue(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1), workspaceScreens));
|
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1)));
|
||||||
assertTrue(mLoaderCursor.checkItemPlacement(
|
assertTrue(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2), workspaceScreens));
|
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2)));
|
||||||
|
|
||||||
assertFalse(mLoaderCursor.checkItemPlacement(
|
assertFalse(mLoaderCursor.checkItemPlacement(
|
||||||
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3), workspaceScreens));
|
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
|
private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
|
||||||
|
|
|
@ -297,10 +297,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||||
if (screenId > Workspace.FIRST_SCREEN_ID) {
|
if (screenId > Workspace.FIRST_SCREEN_ID) {
|
||||||
screenId = Workspace.FIRST_SCREEN_ID;
|
screenId = Workspace.FIRST_SCREEN_ID;
|
||||||
}
|
}
|
||||||
ContentValues v = new ContentValues();
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
|
||||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, 0);
|
|
||||||
mResolver.insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
|
|
||||||
|
|
||||||
// Insert the item
|
// Insert the item
|
||||||
ContentWriter writer = new ContentWriter(mTargetContext);
|
ContentWriter writer = new ContentWriter(mTargetContext);
|
||||||
|
|
Loading…
Reference in New Issue