The new grid migration algorithm
go/grid-migration-v2 When changing grid from option 1 to option 2, we calculate the diff and add the icons that are in option 1 but not option 2, to option 2's workspace, according to the reading order. Test: manual and unit tests Fixes: 144052802 Change-Id: Id01f69e90ce656a9b7c9051fed499807ee9ac0f7
This commit is contained in:
parent
6d8203ef44
commit
f601872a0a
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.TMP_CONTENT_URI;
|
||||
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
|
||||
import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
|
||||
import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
|
||||
import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
|
||||
import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
|
||||
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Point;
|
||||
import android.os.Process;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.util.LauncherModelHelper;
|
||||
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
/** Unit tests for {@link GridSizeMigrationTaskV2} */
|
||||
@RunWith(LauncherRoboTestRunner.class)
|
||||
public class GridSizeMigrationTaskV2Test {
|
||||
|
||||
private LauncherModelHelper mModelHelper;
|
||||
private Context mContext;
|
||||
private SQLiteDatabase mDb;
|
||||
|
||||
private HashSet<String> mValidPackages;
|
||||
private InvariantDeviceProfile mIdp;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mModelHelper = new LauncherModelHelper();
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mDb = mModelHelper.provider.getDb();
|
||||
|
||||
mValidPackages = new HashSet<>();
|
||||
mValidPackages.add(TEST_PACKAGE);
|
||||
mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
|
||||
|
||||
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
|
||||
Process.myUserHandle());
|
||||
dropTable(mDb, LauncherSettings.Favorites.TMP_TABLE);
|
||||
LauncherSettings.Favorites.addTableToDb(mDb, userSerial, false,
|
||||
LauncherSettings.Favorites.TMP_TABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigration() {
|
||||
final String testPackage1 = "com.android.launcher3.validpackage1";
|
||||
final String testPackage2 = "com.android.launcher3.validpackage2";
|
||||
final String testPackage3 = "com.android.launcher3.validpackage3";
|
||||
final String testPackage4 = "com.android.launcher3.validpackage4";
|
||||
final String testPackage5 = "com.android.launcher3.validpackage5";
|
||||
final String testPackage7 = "com.android.launcher3.validpackage7";
|
||||
|
||||
mValidPackages.add(testPackage1);
|
||||
mValidPackages.add(testPackage2);
|
||||
mValidPackages.add(testPackage3);
|
||||
mValidPackages.add(testPackage4);
|
||||
mValidPackages.add(testPackage5);
|
||||
mValidPackages.add(testPackage7);
|
||||
|
||||
int[] srcHotseatItems = {
|
||||
mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
|
||||
mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
|
||||
-1,
|
||||
mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
|
||||
mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
|
||||
};
|
||||
mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage5, 5, TMP_CONTENT_URI);
|
||||
|
||||
int[] destHotseatItems = {
|
||||
-1,
|
||||
mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2),
|
||||
-1,
|
||||
};
|
||||
mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
|
||||
|
||||
mIdp.numHotseatIcons = 3;
|
||||
mIdp.numColumns = 3;
|
||||
mIdp.numRows = 3;
|
||||
GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
|
||||
LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages, 5);
|
||||
GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
|
||||
LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages, 3);
|
||||
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
|
||||
destReader, 3, new Point(mIdp.numColumns, mIdp.numRows));
|
||||
task.migrate();
|
||||
|
||||
Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
|
||||
new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
|
||||
"container=" + CONTAINER_HOTSEAT, null, null, null);
|
||||
assertEquals(c.getCount(), 3);
|
||||
int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
|
||||
int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
|
||||
c.moveToNext();
|
||||
assertEquals(c.getInt(screenIndex), 1);
|
||||
assertTrue(c.getString(intentIndex).contains(testPackage2));
|
||||
c.moveToNext();
|
||||
assertEquals(c.getInt(screenIndex), 0);
|
||||
assertTrue(c.getString(intentIndex).contains(testPackage1));
|
||||
c.moveToNext();
|
||||
assertEquals(c.getInt(screenIndex), 2);
|
||||
assertTrue(c.getString(intentIndex).contains(testPackage3));
|
||||
c.close();
|
||||
|
||||
c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
|
||||
new String[]{LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
|
||||
LauncherSettings.Favorites.INTENT},
|
||||
"container=" + CONTAINER_DESKTOP, null, null, null);
|
||||
assertEquals(c.getCount(), 2);
|
||||
intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
|
||||
int cellXIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLX);
|
||||
int cellYIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLY);
|
||||
|
||||
c.moveToNext();
|
||||
assertTrue(c.getString(intentIndex).contains(testPackage7));
|
||||
c.moveToNext();
|
||||
assertTrue(c.getString(intentIndex).contains(testPackage5));
|
||||
assertEquals(c.getInt(cellXIndex), 0);
|
||||
assertEquals(c.getInt(cellYIndex), 2);
|
||||
}
|
||||
}
|
|
@ -230,7 +230,21 @@ public class LauncherModelHelper {
|
|||
}
|
||||
|
||||
public int addItem(int type, int screen, int container, int x, int y) {
|
||||
return addItem(type, screen, container, x, y, mDefaultProfileId);
|
||||
return addItem(type, screen, container, x, y, mDefaultProfileId, TEST_PACKAGE);
|
||||
}
|
||||
|
||||
public int addItem(int type, int screen, int container, int x, int y, long profileId) {
|
||||
return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE);
|
||||
}
|
||||
|
||||
public int addItem(int type, int screen, int container, int x, int y, String packageName) {
|
||||
return addItem(type, screen, container, x, y, mDefaultProfileId, packageName);
|
||||
}
|
||||
|
||||
public int addItem(int type, int screen, int container, int x, int y, String packageName,
|
||||
int id, Uri contentUri) {
|
||||
addItem(type, screen, container, x, y, mDefaultProfileId, packageName, id, contentUri);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,11 +252,19 @@ public class LauncherModelHelper {
|
|||
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
|
||||
* folder (where the type represents the number of items in the folder).
|
||||
*/
|
||||
public int addItem(int type, int screen, int container, int x, int y, long profileId) {
|
||||
public int addItem(int type, int screen, int container, int x, int y, long profileId,
|
||||
String packageName) {
|
||||
Context context = RuntimeEnvironment.application;
|
||||
int id = LauncherSettings.Settings.call(context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
|
||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
||||
addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void addItem(int type, int screen, int container, int x, int y, long profileId,
|
||||
String packageName, int id, Uri contentUri) {
|
||||
Context context = RuntimeEnvironment.application;
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(LauncherSettings.Favorites._ID, id);
|
||||
|
@ -257,7 +279,7 @@ public class LauncherModelHelper {
|
|||
if (type == APP_ICON || type == SHORTCUT) {
|
||||
values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
|
||||
values.put(LauncherSettings.Favorites.INTENT,
|
||||
new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
|
||||
new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
|
||||
} else {
|
||||
values.put(LauncherSettings.Favorites.ITEM_TYPE,
|
||||
LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
|
||||
|
@ -267,8 +289,7 @@ public class LauncherModelHelper {
|
|||
}
|
||||
}
|
||||
|
||||
context.getContentResolver().insert(CONTENT_URI, values);
|
||||
return id;
|
||||
context.getContentResolver().insert(contentUri, values);
|
||||
}
|
||||
|
||||
public int[][][] createGrid(int[][][] typeArray) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
|
||||
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
|
||||
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
|
||||
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
@ -164,8 +165,11 @@ public class LauncherProvider extends ContentProvider {
|
|||
return false;
|
||||
}
|
||||
|
||||
mOpenHelper.close();
|
||||
DatabaseHelper oldHelper = mOpenHelper;
|
||||
mOpenHelper = new DatabaseHelper(getContext());
|
||||
copyTable(oldHelper.getReadableDatabase(), Favorites.TABLE_NAME,
|
||||
mOpenHelper.getWritableDatabase(), Favorites.TMP_TABLE, getContext());
|
||||
oldHelper.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,11 @@ public class LauncherSettings {
|
|||
*/
|
||||
public static final String PREVIEW_TABLE_NAME = "favorites_preview";
|
||||
|
||||
/**
|
||||
* Temporary table used specifically for multi-db grid migrations
|
||||
*/
|
||||
public static final String TMP_TABLE = "favorites_tmp";
|
||||
|
||||
/**
|
||||
* The content:// style URL for "favorites" table
|
||||
*/
|
||||
|
@ -114,6 +119,12 @@ public class LauncherSettings {
|
|||
public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
|
||||
+ LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
|
||||
|
||||
/**
|
||||
* The content:// style URL for "favorites_tmp" table
|
||||
*/
|
||||
public static final Uri TMP_CONTENT_URI = Uri.parse("content://"
|
||||
+ LauncherProvider.AUTHORITY + "/" + TMP_TABLE);
|
||||
|
||||
/**
|
||||
* The content:// style URL for a given row, identified by its id.
|
||||
*
|
||||
|
|
|
@ -936,8 +936,8 @@ public class GridSizeMigrationTask {
|
|||
|
||||
boolean dbChanged = false;
|
||||
if (migrateForPreview) {
|
||||
copyTable(transaction.getDb(), Favorites.TABLE_NAME, Favorites.PREVIEW_TABLE_NAME,
|
||||
context);
|
||||
copyTable(transaction.getDb(), Favorites.TABLE_NAME, transaction.getDb(),
|
||||
Favorites.PREVIEW_TABLE_NAME, context);
|
||||
}
|
||||
|
||||
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
|
||||
|
@ -950,10 +950,11 @@ public class GridSizeMigrationTask {
|
|||
|
||||
HashSet<String> validPackages = getValidPackages(context);
|
||||
// Hotseat.
|
||||
if (srcHotseatCount != idp.numHotseatIcons) {
|
||||
// Migrate hotseat.
|
||||
dbChanged = new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
|
||||
migrateForPreview, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
|
||||
if (srcHotseatCount != idp.numHotseatIcons
|
||||
&& new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
|
||||
migrateForPreview, srcHotseatCount,
|
||||
idp.numHotseatIcons).migrateHotseat()) {
|
||||
dbChanged = true;
|
||||
}
|
||||
|
||||
// Grid size
|
||||
|
|
|
@ -16,9 +16,46 @@
|
|||
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import static com.android.launcher3.Utilities.getPointString;
|
||||
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Point;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.graphics.LauncherPreviewRenderer;
|
||||
import com.android.launcher3.pm.InstallSessionHelper;
|
||||
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
|
||||
import com.android.launcher3.util.GridOccupancy;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.widget.WidgetManagerHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
|
||||
|
@ -26,15 +63,64 @@ import com.android.launcher3.InvariantDeviceProfile;
|
|||
*/
|
||||
public class GridSizeMigrationTaskV2 {
|
||||
|
||||
private GridSizeMigrationTaskV2(Context context) {
|
||||
public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
|
||||
public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
|
||||
|
||||
private static final String TAG = "GridSizeMigrationTaskV2";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private final Context mContext;
|
||||
private final SQLiteDatabase mDb;
|
||||
private final DbReader mSrcReader;
|
||||
private final DbReader mDestReader;
|
||||
|
||||
private final List<DbEntry> mHotseatItems;
|
||||
private final List<DbEntry> mWorkspaceItems;
|
||||
|
||||
private final List<DbEntry> mHotseatDiff;
|
||||
private final List<DbEntry> mWorkspaceDiff;
|
||||
|
||||
private final int mDestHotseatSize;
|
||||
private final int mTrgX, mTrgY;
|
||||
|
||||
@VisibleForTesting
|
||||
protected GridSizeMigrationTaskV2(Context context, SQLiteDatabase db, DbReader srcReader,
|
||||
DbReader destReader, int destHotseatSize, Point targetSize) {
|
||||
mContext = context;
|
||||
mDb = db;
|
||||
mSrcReader = srcReader;
|
||||
mDestReader = destReader;
|
||||
|
||||
mHotseatItems = destReader.loadHotseatEntries();
|
||||
mWorkspaceItems = destReader.loadAllWorkspaceEntries();
|
||||
|
||||
mHotseatDiff = calcDiff(mSrcReader.loadHotseatEntries(), mHotseatItems);
|
||||
mWorkspaceDiff = calcDiff(mSrcReader.loadAllWorkspaceEntries(), mWorkspaceItems);
|
||||
mDestHotseatSize = destHotseatSize;
|
||||
|
||||
mTrgX = targetSize.x;
|
||||
mTrgY = targetSize.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check given a new IDP, if migration is necessary.
|
||||
*/
|
||||
public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
|
||||
|
||||
return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
|
||||
|| idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
|
||||
idp.numHotseatIcons);
|
||||
}
|
||||
|
||||
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
|
||||
public static boolean migrateGridIfNeeded(Context context) {
|
||||
// To be implemented.
|
||||
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
|
||||
return true;
|
||||
}
|
||||
return migrateGridIfNeeded(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migration algorithm if needed. For preview, we provide the intended idp because it
|
||||
|
@ -43,7 +129,608 @@ public class GridSizeMigrationTaskV2 {
|
|||
* @return false if the migration failed.
|
||||
*/
|
||||
public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
|
||||
// To be implemented.
|
||||
boolean migrateForPreview = idp != null;
|
||||
if (!migrateForPreview) {
|
||||
idp = LauncherAppState.getIDP(context);
|
||||
}
|
||||
|
||||
if (!needsToMigrate(context, idp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
|
||||
|
||||
if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
|
||||
&& idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
|
||||
idp.numHotseatIcons)) {
|
||||
// Skip if workspace and hotseat sizes have not changed.
|
||||
return true;
|
||||
}
|
||||
|
||||
HashSet<String> validPackages = getValidPackages(context);
|
||||
int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
|
||||
|
||||
if (!LauncherSettings.Settings.call(
|
||||
context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
|
||||
LauncherSettings.Settings.EXTRA_VALUE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long migrationStartTime = System.currentTimeMillis();
|
||||
try (SQLiteTransaction t = (SQLiteTransaction) LauncherSettings.Settings.call(
|
||||
context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
|
||||
LauncherSettings.Settings.EXTRA_VALUE)) {
|
||||
|
||||
DbReader srcReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TMP_TABLE,
|
||||
context, validPackages, srcHotseatCount);
|
||||
DbReader destReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TABLE_NAME,
|
||||
context, validPackages, idp.numHotseatIcons);
|
||||
|
||||
Point targetSize = new Point(idp.numColumns, idp.numRows);
|
||||
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
|
||||
srcReader, destReader, idp.numHotseatIcons, targetSize);
|
||||
task.migrate();
|
||||
|
||||
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
|
||||
|
||||
t.commit();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during grid migration", e);
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
Log.v(TAG, "Workspace migration completed in "
|
||||
+ (System.currentTimeMillis() - migrationStartTime));
|
||||
|
||||
// Save current configuration, so that the migration does not run again.
|
||||
prefs.edit()
|
||||
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
|
||||
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean migrate() {
|
||||
if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Migrate hotseat
|
||||
HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
|
||||
mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
|
||||
hotseatSolution.find();
|
||||
|
||||
// Sort the items by the reading order.
|
||||
Collections.sort(mWorkspaceDiff);
|
||||
|
||||
// Migrate workspace.
|
||||
for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Migrating " + screenId);
|
||||
}
|
||||
List<DbEntry> entries = mDestReader.loadWorkspaceEntries(screenId);
|
||||
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
|
||||
mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
|
||||
workspaceSolution.find();
|
||||
if (mWorkspaceDiff.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int screenId = mDestReader.mLastScreenId + 1;
|
||||
while (!mWorkspaceDiff.isEmpty()) {
|
||||
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
|
||||
mContext, new ArrayList<>(), screenId, mTrgX, mTrgY, mWorkspaceDiff);
|
||||
workspaceSolution.find();
|
||||
screenId++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return what's in the src but not in the dest */
|
||||
private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
|
||||
Set<String> destSet = dest.parallelStream().map(DbEntry::getIntentStr).collect(
|
||||
Collectors.toSet());
|
||||
List<DbEntry> diff = new ArrayList<>();
|
||||
for (DbEntry entry : src) {
|
||||
if (!destSet.contains(entry.mIntent)) {
|
||||
diff.add(entry);
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
private static void insertEntryInDb(SQLiteDatabase db, Context context,
|
||||
ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry) {
|
||||
int id = -1;
|
||||
switch (entry.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
||||
for (DbEntry e : entriesFromSrcDb) {
|
||||
if (TextUtils.equals(e.mIntent, entry.mIntent)) {
|
||||
id = e.id;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
|
||||
for (DbEntry e : entriesFromSrcDb) {
|
||||
if (e.mFolderItems.size() == entry.mFolderItems.size()
|
||||
&& e.mFolderItems.containsAll(entry.mFolderItems)) {
|
||||
id = e.id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
|
||||
for (DbEntry e : entriesFromSrcDb) {
|
||||
if (TextUtils.equals(e.mProvider, entry.mProvider)) {
|
||||
id = e.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Cursor c = db.query(LauncherSettings.Favorites.TMP_TABLE, null,
|
||||
LauncherSettings.Favorites._ID + " = '" + id + "'", null, null, null, null);
|
||||
|
||||
while (c.moveToNext()) {
|
||||
ContentValues values = new ContentValues();
|
||||
DatabaseUtils.cursorRowToContentValues(c, values);
|
||||
entry.updateContentValues(values);
|
||||
values.put(LauncherSettings.Favorites._ID,
|
||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
|
||||
LauncherSettings.Settings.EXTRA_VALUE));
|
||||
db.insert(LauncherSettings.Favorites.TABLE_NAME, null, values);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
|
||||
private static void removeEntryFromDb(SQLiteDatabase db, IntArray entryId) {
|
||||
db.delete(LauncherSettings.Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
|
||||
LauncherSettings.Favorites._ID, entryId), null);
|
||||
}
|
||||
|
||||
private static HashSet<String> getValidPackages(Context context) {
|
||||
// Initialize list of valid packages. This contain all the packages which are already on
|
||||
// the device and packages which are being installed. Any item which doesn't belong to
|
||||
// this set is removed.
|
||||
// Since the loader removes such items anyway, removing these items here doesn't cause
|
||||
// any extra data loss and gives us more free space on the grid for better migration.
|
||||
HashSet<String> validPackages = new HashSet<>();
|
||||
for (PackageInfo info : context.getPackageManager()
|
||||
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
|
||||
validPackages.add(info.packageName);
|
||||
}
|
||||
InstallSessionHelper.INSTANCE.get(context)
|
||||
.getActiveSessions().keySet()
|
||||
.forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
|
||||
return validPackages;
|
||||
}
|
||||
|
||||
protected static class GridPlacementSolution {
|
||||
|
||||
private final SQLiteDatabase mDb;
|
||||
private final DbReader mSrcReader;
|
||||
private final Context mContext;
|
||||
private final GridOccupancy mOccupied;
|
||||
private final int mScreenId;
|
||||
private final int mTrgX;
|
||||
private final int mTrgY;
|
||||
private final List<DbEntry> mItemsToPlace;
|
||||
|
||||
private int mNextStartX;
|
||||
private int mNextStartY;
|
||||
|
||||
GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
|
||||
List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
|
||||
int trgY, List<DbEntry> itemsToPlace) {
|
||||
mDb = db;
|
||||
mSrcReader = srcReader;
|
||||
mContext = context;
|
||||
mOccupied = new GridOccupancy(trgX, trgY);
|
||||
mScreenId = screenId;
|
||||
mTrgX = trgX;
|
||||
mTrgY = trgY;
|
||||
mNextStartX = 0;
|
||||
mNextStartY = mTrgY - 1;
|
||||
for (DbEntry entry : placedWorkspaceItems) {
|
||||
mOccupied.markCells(entry, true);
|
||||
}
|
||||
mItemsToPlace = itemsToPlace;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
Iterator<DbEntry> iterator = mItemsToPlace.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final DbEntry entry = iterator.next();
|
||||
if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
if (findPlacement(entry)) {
|
||||
insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean findPlacement(DbEntry entry) {
|
||||
for (int y = mNextStartY; y >= 0; y--) {
|
||||
for (int x = mNextStartX; x < mTrgX; x++) {
|
||||
boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
|
||||
boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
|
||||
entry.minSpanY);
|
||||
if (minFits) {
|
||||
entry.spanX = entry.minSpanX;
|
||||
entry.spanY = entry.minSpanY;
|
||||
}
|
||||
if (fits || minFits) {
|
||||
entry.screenId = mScreenId;
|
||||
entry.cellX = x;
|
||||
entry.cellY = y;
|
||||
mOccupied.markCells(entry, true);
|
||||
mNextStartX = x + entry.spanX;
|
||||
mNextStartY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class HotseatPlacementSolution {
|
||||
|
||||
private final SQLiteDatabase mDb;
|
||||
private final DbReader mSrcReader;
|
||||
private final Context mContext;
|
||||
private final HotseatOccupancy mOccupied;
|
||||
private final List<DbEntry> mItemsToPlace;
|
||||
|
||||
HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
|
||||
int hotseatSize, List<DbEntry> placedHotseatItems, List<DbEntry> itemsToPlace) {
|
||||
mDb = db;
|
||||
mSrcReader = srcReader;
|
||||
mContext = context;
|
||||
mOccupied = new HotseatOccupancy(hotseatSize);
|
||||
for (DbEntry entry : placedHotseatItems) {
|
||||
mOccupied.markCells(entry, true);
|
||||
}
|
||||
mItemsToPlace = itemsToPlace;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
for (int i = 0; i < mOccupied.mCells.length; i++) {
|
||||
if (!mOccupied.mCells[i] && !mItemsToPlace.isEmpty()) {
|
||||
DbEntry entry = mItemsToPlace.remove(0);
|
||||
entry.screenId = i;
|
||||
// These values does not affect the item position, but we should set them
|
||||
// to something other than -1.
|
||||
entry.cellX = i;
|
||||
entry.cellY = 0;
|
||||
insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry);
|
||||
mOccupied.markCells(entry, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class HotseatOccupancy {
|
||||
|
||||
private final boolean[] mCells;
|
||||
|
||||
private HotseatOccupancy(int hotseatSize) {
|
||||
mCells = new boolean[hotseatSize];
|
||||
}
|
||||
|
||||
private void markCells(ItemInfo item, boolean value) {
|
||||
mCells[item.screenId] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DbReader {
|
||||
|
||||
private final SQLiteDatabase mDb;
|
||||
private final String mTableName;
|
||||
private final Context mContext;
|
||||
private final HashSet<String> mValidPackages;
|
||||
private final int mHotseatSize;
|
||||
private int mLastScreenId = -1;
|
||||
|
||||
private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
|
||||
private final ArrayList<DbEntry> mWorkspaceEntries = new ArrayList<>();
|
||||
|
||||
DbReader(SQLiteDatabase db, String tableName, Context context,
|
||||
HashSet<String> validPackages, int hotseatSize) {
|
||||
mDb = db;
|
||||
mTableName = tableName;
|
||||
mContext = context;
|
||||
mValidPackages = validPackages;
|
||||
mHotseatSize = hotseatSize;
|
||||
}
|
||||
|
||||
protected ArrayList<DbEntry> loadHotseatEntries() {
|
||||
Cursor c = queryWorkspace(
|
||||
new String[]{
|
||||
LauncherSettings.Favorites._ID, // 0
|
||||
LauncherSettings.Favorites.ITEM_TYPE, // 1
|
||||
LauncherSettings.Favorites.INTENT, // 2
|
||||
LauncherSettings.Favorites.SCREEN}, // 3
|
||||
LauncherSettings.Favorites.CONTAINER + " = "
|
||||
+ LauncherSettings.Favorites.CONTAINER_HOTSEAT);
|
||||
|
||||
final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
|
||||
final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
|
||||
final int indexIntent = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
|
||||
final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
|
||||
|
||||
IntArray entriesToRemove = new IntArray();
|
||||
while (c.moveToNext()) {
|
||||
DbEntry entry = new DbEntry();
|
||||
entry.id = c.getInt(indexId);
|
||||
entry.itemType = c.getInt(indexItemType);
|
||||
entry.screenId = c.getInt(indexScreen);
|
||||
|
||||
if (entry.screenId >= mHotseatSize) {
|
||||
entriesToRemove.add(entry.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// calculate weight
|
||||
switch (entry.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
||||
entry.mIntent = c.getString(indexIntent);
|
||||
verifyIntent(c.getString(indexIntent));
|
||||
break;
|
||||
}
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
|
||||
int total = getFolderItemsCount(entry);
|
||||
if (total == 0) {
|
||||
throw new Exception("Folder is empty");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Exception("Invalid item type");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Removing item " + entry.id, e);
|
||||
}
|
||||
entriesToRemove.add(entry.id);
|
||||
continue;
|
||||
}
|
||||
mHotseatEntries.add(entry);
|
||||
}
|
||||
removeEntryFromDb(mDb, entriesToRemove);
|
||||
c.close();
|
||||
return mHotseatEntries;
|
||||
}
|
||||
|
||||
protected ArrayList<DbEntry> loadAllWorkspaceEntries() {
|
||||
Cursor c = queryWorkspace(
|
||||
new String[]{
|
||||
LauncherSettings.Favorites._ID, // 0
|
||||
LauncherSettings.Favorites.ITEM_TYPE, // 1
|
||||
LauncherSettings.Favorites.SCREEN, // 2
|
||||
LauncherSettings.Favorites.CELLX, // 3
|
||||
LauncherSettings.Favorites.CELLY, // 4
|
||||
LauncherSettings.Favorites.SPANX, // 5
|
||||
LauncherSettings.Favorites.SPANY, // 6
|
||||
LauncherSettings.Favorites.INTENT, // 7
|
||||
LauncherSettings.Favorites.APPWIDGET_PROVIDER, // 8
|
||||
LauncherSettings.Favorites.APPWIDGET_ID}, // 9
|
||||
LauncherSettings.Favorites.CONTAINER + " = "
|
||||
+ LauncherSettings.Favorites.CONTAINER_DESKTOP);
|
||||
return loadWorkspaceEntries(c);
|
||||
}
|
||||
|
||||
protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
|
||||
Cursor c = queryWorkspace(
|
||||
new String[]{
|
||||
LauncherSettings.Favorites._ID, // 0
|
||||
LauncherSettings.Favorites.ITEM_TYPE, // 1
|
||||
LauncherSettings.Favorites.SCREEN, // 2
|
||||
LauncherSettings.Favorites.CELLX, // 3
|
||||
LauncherSettings.Favorites.CELLY, // 4
|
||||
LauncherSettings.Favorites.SPANX, // 5
|
||||
LauncherSettings.Favorites.SPANY, // 6
|
||||
LauncherSettings.Favorites.INTENT, // 7
|
||||
LauncherSettings.Favorites.APPWIDGET_PROVIDER, // 8
|
||||
LauncherSettings.Favorites.APPWIDGET_ID}, // 9
|
||||
LauncherSettings.Favorites.CONTAINER + " = "
|
||||
+ LauncherSettings.Favorites.CONTAINER_DESKTOP
|
||||
+ " AND " + LauncherSettings.Favorites.SCREEN + " = " + screen);
|
||||
return loadWorkspaceEntries(c);
|
||||
}
|
||||
|
||||
private ArrayList<DbEntry> loadWorkspaceEntries(Cursor c) {
|
||||
final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
|
||||
final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
|
||||
final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
|
||||
final int indexCellX = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
|
||||
final int indexCellY = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
|
||||
final int indexSpanX = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
|
||||
final int indexSpanY = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
|
||||
final int indexIntent = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
|
||||
final int indexAppWidgetProvider = c.getColumnIndexOrThrow(
|
||||
LauncherSettings.Favorites.APPWIDGET_PROVIDER);
|
||||
final int indexAppWidgetId = c.getColumnIndexOrThrow(
|
||||
LauncherSettings.Favorites.APPWIDGET_ID);
|
||||
|
||||
IntArray entriesToRemove = new IntArray();
|
||||
WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(mContext);
|
||||
while (c.moveToNext()) {
|
||||
DbEntry entry = new DbEntry();
|
||||
entry.id = c.getInt(indexId);
|
||||
entry.itemType = c.getInt(indexItemType);
|
||||
entry.screenId = c.getInt(indexScreen);
|
||||
mLastScreenId = Math.max(mLastScreenId, entry.screenId);
|
||||
entry.cellX = c.getInt(indexCellX);
|
||||
entry.cellY = c.getInt(indexCellY);
|
||||
entry.spanX = c.getInt(indexSpanX);
|
||||
entry.spanY = c.getInt(indexSpanY);
|
||||
|
||||
try {
|
||||
// calculate weight
|
||||
switch (entry.itemType) {
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
||||
entry.mIntent = c.getString(indexIntent);
|
||||
verifyIntent(entry.mIntent);
|
||||
break;
|
||||
}
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
|
||||
entry.mProvider = c.getString(indexAppWidgetProvider);
|
||||
ComponentName cn = ComponentName.unflattenFromString(entry.mProvider);
|
||||
verifyPackage(cn.getPackageName());
|
||||
|
||||
int widgetId = c.getInt(indexAppWidgetId);
|
||||
LauncherAppWidgetProviderInfo pInfo =
|
||||
widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
|
||||
Point spans = null;
|
||||
if (pInfo != null) {
|
||||
spans = pInfo.getMinSpans();
|
||||
}
|
||||
if (spans != null) {
|
||||
entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
|
||||
entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
|
||||
} else {
|
||||
// Assume that the widget be resized down to 2x2
|
||||
entry.minSpanX = entry.minSpanY = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
|
||||
int total = getFolderItemsCount(entry);
|
||||
if (total == 0) {
|
||||
throw new Exception("Folder is empty");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Exception("Invalid item type");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Removing item " + entry.id, e);
|
||||
}
|
||||
entriesToRemove.add(entry.id);
|
||||
continue;
|
||||
}
|
||||
mWorkspaceEntries.add(entry);
|
||||
}
|
||||
removeEntryFromDb(mDb, entriesToRemove);
|
||||
c.close();
|
||||
return mWorkspaceEntries;
|
||||
}
|
||||
|
||||
private int getFolderItemsCount(DbEntry entry) {
|
||||
Cursor c = queryWorkspace(
|
||||
new String[]{LauncherSettings.Favorites._ID, LauncherSettings.Favorites.INTENT},
|
||||
LauncherSettings.Favorites.CONTAINER + " = " + entry.id);
|
||||
|
||||
int total = 0;
|
||||
while (c.moveToNext()) {
|
||||
try {
|
||||
String intent = c.getString(1);
|
||||
verifyIntent(intent);
|
||||
total++;
|
||||
entry.mFolderItems.add(intent);
|
||||
} catch (Exception e) {
|
||||
removeEntryFromDb(mDb, IntArray.wrap(c.getInt(0)));
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
return total;
|
||||
}
|
||||
|
||||
private Cursor queryWorkspace(String[] columns, String where) {
|
||||
return mDb.query(mTableName, columns, where, null, null, null, null);
|
||||
}
|
||||
|
||||
/** Verifies if the mIntent should be restored. */
|
||||
private void verifyIntent(String intentStr)
|
||||
throws Exception {
|
||||
Intent intent = Intent.parseUri(intentStr, 0);
|
||||
if (intent.getComponent() != null) {
|
||||
verifyPackage(intent.getComponent().getPackageName());
|
||||
} else if (intent.getPackage() != null) {
|
||||
// Only verify package if the component was null.
|
||||
verifyPackage(intent.getPackage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Verifies if the package should be restored */
|
||||
private void verifyPackage(String packageName)
|
||||
throws Exception {
|
||||
if (!mValidPackages.contains(packageName)) {
|
||||
// TODO(b/151468819): Handle promise app icon restoration during grid migration.
|
||||
throw new Exception("Package not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
|
||||
|
||||
private String mIntent;
|
||||
private String mProvider;
|
||||
private Set<String> mFolderItems = new HashSet<>();
|
||||
|
||||
/** Comparator according to the reading order */
|
||||
@Override
|
||||
public int compareTo(DbEntry another) {
|
||||
if (screenId != another.screenId) {
|
||||
return Integer.compare(screenId, another.screenId);
|
||||
}
|
||||
if (cellY != another.cellY) {
|
||||
return -Integer.compare(cellY, another.cellY);
|
||||
}
|
||||
return Integer.compare(cellX, another.cellX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DbEntry entry = (DbEntry) o;
|
||||
return Objects.equals(mIntent, entry.mIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mIntent);
|
||||
}
|
||||
|
||||
public void updateContentValues(ContentValues values) {
|
||||
values.put(LauncherSettings.Favorites.SCREEN, screenId);
|
||||
values.put(LauncherSettings.Favorites.CELLX, cellX);
|
||||
values.put(LauncherSettings.Favorites.CELLY, cellY);
|
||||
values.put(LauncherSettings.Favorites.SPANX, spanX);
|
||||
values.put(LauncherSettings.Favorites.SPANY, spanY);
|
||||
}
|
||||
|
||||
public String getIntentStr() {
|
||||
return mIntent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,13 +118,20 @@ public class LauncherDbUtils {
|
|||
db.execSQL("DROP TABLE IF EXISTS " + tableName);
|
||||
}
|
||||
|
||||
/** Copy from table to the to table. */
|
||||
public static void copyTable(SQLiteDatabase db, String from, String to, Context context) {
|
||||
/** Copy fromTable in fromDb to toTable in toDb. */
|
||||
public static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
|
||||
String toTable, Context context) {
|
||||
long userSerial = UserCache.INSTANCE.get(context).getSerialNumberForUser(
|
||||
Process.myUserHandle());
|
||||
dropTable(db, to);
|
||||
Favorites.addTableToDb(db, userSerial, false, to);
|
||||
db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from);
|
||||
dropTable(toDb, toTable);
|
||||
Favorites.addTableToDb(toDb, userSerial, false, toTable);
|
||||
if (fromDb != toDb) {
|
||||
toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
|
||||
toDb.execSQL(
|
||||
"INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable);
|
||||
} else {
|
||||
toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue