Render user's actual workspace in ThemePicker preview (Part 3)
go/grid-migration-preview
With this change, we can see actual grid migration in wallpaper preview.
The approach here: we use a tmp table (favorites_preview) here specifically for this preview (to write off the migration results), and load from this tmp table workspace items if migration is necessary and successful. Otherwise, we load from the current workspace.
UPDATED: this change should be completely compatible with the new multi-db grid migration algorithm. Here is why
1. In LauncherPreviewRender#renderScreenShot, I added a check to decide which grid migration preview method we should call. Once v2 preview method is implemented, it should be integrated with other parts of this change perfectly (the reason will be mentioned below).
2. While we have multiple DBs, mOpenHelper in LauncherProvider always points to the current db we are using. Queries using CONTENT_URI is routed to whatever DB mOpenHelper points to, so it works perfectly to directly operate on CONTENT_URI even when we use multi-db underneath the hood.
3. With 1 and 2 mentioned, I believe in order for this preview change to support multi-db, we only need to implement the V2 grid migration algorithm. Because most of what we are doing in this change is wrapped in GridSizeMigrationTask, it's perfectly safeguarded.
Bug: 144052839
Change-Id: Ie6d6048d77326f96546c8a180a7cd8f15b47e4c4
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index 1ed4bca..c426dc5 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -65,7 +65,7 @@
};
mIdp.numHotseatIcons = 3;
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
.migrateHotseat();
// First item is dropped as it has the least weight.
verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
@@ -82,7 +82,7 @@
};
mIdp.numHotseatIcons = 3;
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
.migrateHotseat();
// First item is dropped as it has the least weight.
verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
@@ -127,7 +127,7 @@
{ 5, 2, -1, 6},
}});
- new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Column 2 and row 2 got removed.
@@ -147,7 +147,7 @@
{ 5, 2, -1, 6},
}});
- new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column get moved to new screen
@@ -172,7 +172,7 @@
{ 3, 1, -1, 4},
}});
- new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on the 3rd
@@ -204,7 +204,7 @@
{ 5, 2, -1, 6},
}});
- new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -235,7 +235,7 @@
{ 5, 2, 7, -1},
}}, 0);
- new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 4)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -262,7 +262,7 @@
{ 5, 6, 7, -1},
}}, 0);
- new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -282,7 +282,7 @@
* represent the workspace grid.
*/
private void verifyWorkspace(int[][][] ids) {
- IntArray allScreens = getWorkspaceScreenIds(mDb);
+ IntArray allScreens = getWorkspaceScreenIds(mDb, LauncherSettings.Favorites.TABLE_NAME);
assertEquals(ids.length, allScreens.size());
int total = 0;
@@ -351,7 +351,7 @@
private final LinkedList<Point> mPoints;
public MultiStepMigrationTaskVerifier(int... points) {
- super(null, null, null);
+ super(null, null, null, false);
mPoints = new LinkedList<>();
for (int i = 0; i < points.length; i += 2) {
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 7fa3ee9..6e41a4f 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -52,6 +52,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherRoboTestRunner;
@@ -91,7 +92,7 @@
SCREEN, CELLX, CELLY, RESTORED, INTENT
});
- mLoaderCursor = new LoaderCursor(mCursor, mApp);
+ mLoaderCursor = new LoaderCursor(mCursor, LauncherSettings.Favorites.CONTENT_URI, mApp);
mLoaderCursor.allUsers.put(0, Process.myUserHandle());
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 857db8e..2ad84b9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -47,6 +47,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.DefaultDisplay.Info;
@@ -156,6 +157,11 @@
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
+ if (context instanceof LauncherPreviewRenderer.PreviewContext) {
+ throw new IllegalArgumentException(
+ "PreviewContext is passed into this IDP constructor");
+ }
+
String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
: null;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d77285d..2f38037 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -27,6 +27,8 @@
import android.os.Handler;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
@@ -55,12 +57,12 @@
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
- private final SecureSettingsObserver mNotificationDotsObserver;
- private final InstallSessionTracker mInstallSessionTracker;
- private final SimpleBroadcastReceiver mModelChangeReceiver;
- private final SafeCloseable mCalendarChangeTracker;
- private final SafeCloseable mUserChangeListener;
+ private SecureSettingsObserver mNotificationDotsObserver;
+ private InstallSessionTracker mInstallSessionTracker;
+ private SimpleBroadcastReceiver mModelChangeReceiver;
+ private SafeCloseable mCalendarChangeTracker;
+ private SafeCloseable mUserChangeListener;
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
@@ -74,15 +76,8 @@
return mContext;
}
- private LauncherAppState(Context context) {
- Log.v(Launcher.TAG, "LauncherAppState initiated");
- Preconditions.assertUIThread();
- mContext = context;
-
- mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext);
- mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
- mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
- mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
+ public LauncherAppState(Context context) {
+ this(context, LauncherFiles.APP_ICONS_DB);
mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
@@ -123,6 +118,17 @@
}
}
+ public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
+ Log.v(Launcher.TAG, "LauncherAppState initiated");
+ Preconditions.assertUIThread();
+ mContext = context;
+
+ mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
+ mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
+ mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
+ mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
+ }
+
protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
if (areNotificationDotsEnabled) {
NotificationListener.requestRebind(new ComponentName(
@@ -148,11 +154,19 @@
* Call from Application.onTerminate(), which is not guaranteed to ever be called.
*/
public void onTerminate() {
- mContext.unregisterReceiver(mModelChangeReceiver);
+ if (mModelChangeReceiver != null) {
+ mContext.unregisterReceiver(mModelChangeReceiver);
+ }
mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
- mInstallSessionTracker.unregister();
- mCalendarChangeTracker.close();
- mUserChangeListener.close();
+ if (mInstallSessionTracker != null) {
+ mInstallSessionTracker.unregister();
+ }
+ if (mCalendarChangeTracker != null) {
+ mCalendarChangeTracker.close();
+ }
+ if (mUserChangeListener != null) {
+ mUserChangeListener.close();
+ }
CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
if (mNotificationDotsObserver != null) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 49831f6..216c221 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -93,15 +93,26 @@
public static final String TABLE_NAME = "favorites";
/**
- * Backup table created when when the favorites table is modified during grid migration
+ * Backup table created when the favorites table is modified during grid migration
*/
public static final String BACKUP_TABLE_NAME = "favorites_bakup";
/**
- * The content:// style URL for this table
+ * Temporary table used specifically for grid migrations during wallpaper preview
*/
- public static final Uri CONTENT_URI = Uri.parse("content://" +
- LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
+ public static final String PREVIEW_TABLE_NAME = "favorites_preview";
+
+ /**
+ * The content:// style URL for "favorites" table
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://"
+ + LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
+
+ /**
+ * The content:// style URL for "favorites_preview" table
+ */
+ public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
+ + LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
/**
* The content:// style URL for a given row, identified by its id.
@@ -111,8 +122,8 @@
* @return The unique content URL for the specified row.
*/
public static Uri getContentUri(int id) {
- return Uri.parse("content://" + LauncherProvider.AUTHORITY +
- "/" + TABLE_NAME + "/" + id);
+ return Uri.parse("content://" + LauncherProvider.AUTHORITY
+ + "/" + TABLE_NAME + "/" + id);
}
/**
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index a429af2..0927b26 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -20,14 +20,19 @@
import static android.view.View.VISIBLE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
+import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
+import static com.android.launcher3.model.GridSizeMigrationTask.needsToMigrate;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.annotation.TargetApi;
import android.app.Fragment;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -70,17 +75,31 @@
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -101,6 +120,81 @@
private static final String TAG = "LauncherPreviewRenderer";
+ /**
+ * Context used just for preview. It also provides a few objects (e.g. UserCache) just for
+ * preview purposes.
+ */
+ public static class PreviewContext extends ContextWrapper {
+
+ private static final Set<MainThreadInitializedObject> WHITELIST = new HashSet<>(
+ Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
+ LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE));
+
+ private final InvariantDeviceProfile mIdp;
+ private final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();
+ private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
+ new ConcurrentLinkedQueue<>();
+
+ public PreviewContext(Context base, InvariantDeviceProfile idp) {
+ super(base);
+ mIdp = idp;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ /**
+ * Find a cached object from mObjectMap if we have already created one. If not, generate
+ * an object using the provider.
+ */
+ public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject,
+ MainThreadInitializedObject.ObjectProvider<T> provider) {
+ if (!WHITELIST.contains(mainThreadInitializedObject)) {
+ throw new IllegalStateException("Leaking unknown objects");
+ }
+ if (mainThreadInitializedObject == LauncherAppState.INSTANCE) {
+ throw new IllegalStateException(
+ "Should not use MainThreadInitializedObject to initialize this with "
+ + "PreviewContext");
+ }
+ if (mainThreadInitializedObject == InvariantDeviceProfile.INSTANCE) {
+ return (T) mIdp;
+ }
+ if (mObjectMap.containsKey(mainThreadInitializedObject)) {
+ return (T) mObjectMap.get(mainThreadInitializedObject);
+ }
+ T t = provider.get(this);
+ mObjectMap.put(mainThreadInitializedObject, t);
+ return t;
+ }
+
+ public LauncherIcons newLauncherIcons(Context context, boolean shapeDetection) {
+ LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
+ if (launcherIconsForPreview != null) {
+ return launcherIconsForPreview;
+ }
+ return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
+ -1 /* poolId */, shapeDetection);
+ }
+
+ private final class LauncherIconsForPreview extends LauncherIcons {
+
+ private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
+ int poolId, boolean shapeDetection) {
+ super(context, fillResIconDpi, iconBitmapSize, poolId, shapeDetection);
+ }
+
+ @Override
+ public void recycle() {
+ // Clear any temporary state variables
+ clear();
+ mIconPool.offer(this);
+ }
+ }
+ }
+
private final Handler mUiHandler;
private final Context mContext;
private final InvariantDeviceProfile mIdp;
@@ -282,15 +376,28 @@
private void renderScreenShot(Canvas canvas) {
if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
- final LauncherModel launcherModel = LauncherAppState.getInstance(
- mContext).getModel();
- final WorkspaceItemsInfoFetcher fetcher = new WorkspaceItemsInfoFetcher();
- launcherModel.enqueueModelUpdateTask(fetcher);
- WorkspaceResult workspaceResult;
- try {
- workspaceResult = fetcher.mTask.get(5, TimeUnit.SECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Log.d(TAG, "Error fetching workspace items info", e);
+ boolean needsToMigrate = needsToMigrate(mContext, mIdp);
+ boolean success = false;
+ if (needsToMigrate) {
+ success = MULTI_DB_GRID_MIRATION_ALGO.get()
+ ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp)
+ : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp);
+ }
+
+ WorkspaceFetcher fetcher;
+ if (needsToMigrate && success) {
+ LauncherAppState appForPreview = new LauncherAppState(
+ new PreviewContext(mContext, mIdp), null /* iconCacheFileName */);
+ fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
+ MODEL_EXECUTOR.execute(fetcher);
+ } else {
+ fetcher = new WorkspaceItemsInfoFetcher();
+ LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
+ (LauncherModel.ModelUpdateTask) fetcher);
+ }
+ WorkspaceResult workspaceResult = fetcher.get();
+
+ if (workspaceResult == null) {
return;
}
@@ -379,8 +486,13 @@
}
}
- private static class WorkspaceItemsInfoFetcher implements Callable<WorkspaceResult>,
- LauncherModel.ModelUpdateTask {
+ private static void measureView(View view, int width, int height) {
+ view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
+ view.layout(0, 0, width, height);
+ }
+
+ private static class WorkspaceItemsInfoFetcher implements LauncherModel.ModelUpdateTask,
+ WorkspaceFetcher {
private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
@@ -399,6 +511,11 @@
}
@Override
+ public FutureTask<WorkspaceResult> getTask() {
+ return mTask;
+ }
+
+ @Override
public void run() {
mTask.run();
}
@@ -417,9 +534,45 @@
}
}
- private static void measureView(View view, int width, int height) {
- view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
- view.layout(0, 0, width, height);
+ private static class WorkspaceItemsInfoFromPreviewFetcher extends LoaderTask implements
+ WorkspaceFetcher {
+
+ private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
+
+ WorkspaceItemsInfoFromPreviewFetcher(LauncherAppState app) {
+ super(app, null, new BgDataModel(), null);
+ }
+
+ @Override
+ public FutureTask<WorkspaceResult> getTask() {
+ return mTask;
+ }
+
+ @Override
+ public void run() {
+ mTask.run();
+ }
+
+ @Override
+ public WorkspaceResult call() throws Exception {
+ List<ShortcutInfo> allShortcuts = new ArrayList<>();
+ loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI);
+ return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
+ mBgDataModel.widgetsModel);
+ }
+ }
+
+ private interface WorkspaceFetcher extends Runnable, Callable<WorkspaceResult> {
+ FutureTask<WorkspaceResult> getTask();
+
+ default WorkspaceResult get() {
+ try {
+ return getTask().get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Log.d(TAG, "Error fetching workspace items info", e);
+ return null;
+ }
+ }
}
private static class WorkspaceResult {
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 804acc3..f27c9da 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -81,9 +81,13 @@
private int mPendingIconRequestCount = 0;
- public IconCache(Context context, InvariantDeviceProfile inv) {
- super(context, LauncherFiles.APP_ICONS_DB, MODEL_EXECUTOR.getLooper(),
- inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
+ public IconCache(Context context, InvariantDeviceProfile idp) {
+ this(context, idp, LauncherFiles.APP_ICONS_DB);
+ }
+
+ public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName) {
+ super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
+ idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
mShortcutCachingLogic = new ShortcutCachingLogic();
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index cbd7c53..bf7897e 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -19,8 +19,8 @@
import android.content.Context;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -41,6 +41,11 @@
* avoid allocating new objects in many cases.
*/
public static LauncherIcons obtain(Context context, boolean shapeDetection) {
+ if (context instanceof LauncherPreviewRenderer.PreviewContext) {
+ return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context,
+ shapeDetection);
+ }
+
int poolId;
synchronized (sPoolSync) {
if (sPool != null) {
@@ -52,7 +57,7 @@
poolId = sPoolId;
}
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
shapeDetection);
}
@@ -68,7 +73,7 @@
private LauncherIcons next;
- private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
+ protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
boolean shapeDetection) {
super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
mPoolId = poolId;
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index 07a7551..4a1bc4d 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -100,12 +100,24 @@
Process.myUserHandle()), 0);
return false;
}
+ return restoreIfBackupExists(Favorites.TABLE_NAME);
+ }
+
+ public boolean restoreToPreviewIfBackupExists() {
+ if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
+ return false;
+ }
+
+ return restoreIfBackupExists(Favorites.PREVIEW_TABLE_NAME);
+ }
+
+ private boolean restoreIfBackupExists(String toTableName) {
if (loadDBProperties() != STATE_SANITIZED) {
return false;
}
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
Process.myUserHandle());
- copyTable(mDb, BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
+ copyTable(mDb, BACKUP_TABLE_NAME, toTableName, userSerial);
Log.d(TAG, "Backup table found");
return true;
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index c35c4b9..3ba740d 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -3,6 +3,7 @@
import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.Utilities.parsePoint;
+import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -14,6 +15,7 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
+import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -29,6 +31,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
@@ -69,6 +72,7 @@
private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
private final HashSet<String> mValidPackages;
+ private final String mTableName;
private final int mSrcX, mSrcY;
private final int mTrgX, mTrgY;
@@ -78,10 +82,12 @@
private final int mDestHotseatSize;
protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
- HashSet<String> validPackages, Point sourceSize, Point targetSize) {
+ HashSet<String> validPackages, boolean usePreviewTable, Point sourceSize,
+ Point targetSize) {
mContext = context;
mDb = db;
mValidPackages = validPackages;
+ mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
mSrcX = sourceSize.x;
mSrcY = sourceSize.y;
@@ -97,10 +103,12 @@
}
protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
- HashSet<String> validPackages, int srcHotseatSize, int destHotseatSize) {
+ HashSet<String> validPackages, boolean usePreviewTable, int srcHotseatSize,
+ int destHotseatSize) {
mContext = context;
mDb = db;
mValidPackages = validPackages;
+ mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
mSrcHotseatSize = srcHotseatSize;
@@ -120,7 +128,7 @@
// Update items
int updateCount = mUpdateOperations.size();
for (int i = 0; i < updateCount; i++) {
- mDb.update(Favorites.TABLE_NAME, mUpdateOperations.valueAt(i),
+ mDb.update(mTableName, mUpdateOperations.valueAt(i),
"_id=" + mUpdateOperations.keyAt(i), null);
}
@@ -128,8 +136,8 @@
if (DEBUG) {
Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
}
- mDb.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
- Favorites._ID, mEntryToRemove), null);
+ mDb.delete(mTableName, Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove),
+ null);
}
return updateCount > 0 || !mEntryToRemove.isEmpty();
@@ -182,8 +190,8 @@
}
@VisibleForTesting
- static IntArray getWorkspaceScreenIds(SQLiteDatabase db) {
- return LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME, Favorites.SCREEN,
+ static IntArray getWorkspaceScreenIds(SQLiteDatabase db, String tableName) {
+ return LauncherDbUtils.queryIntArray(db, tableName, Favorites.SCREEN,
Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
Favorites.SCREEN, Favorites.SCREEN);
}
@@ -192,7 +200,7 @@
* @return true if any DB change was made
*/
protected boolean migrateWorkspace() throws Exception {
- IntArray allScreens = getWorkspaceScreenIds(mDb);
+ IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName);
if (allScreens.isEmpty()) {
throw new Exception("Unable to get workspace screens");
}
@@ -244,12 +252,12 @@
/**
* Migrate a particular screen id.
* Strategy:
- * 1) For all possible combinations of row and column, pick the one which causes the least
- * data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
- * 2) Maintain a list of all lost items before this screen, and add any new item lost from
- * this screen to that list as well.
- * 3) If all those items from the above list can be placed on this screen, place them
- * (otherwise they are placed on a new screen).
+ * 1) For all possible combinations of row and column, pick the one which causes the least
+ * data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
+ * 2) Maintain a list of all lost items before this screen, and add any new item lost from
+ * this screen to that list as well.
+ * 3) If all those items from the above list can be placed on this screen, place them
+ * (otherwise they are placed on a new screen).
*/
protected void migrateScreen(int screenId) {
// If we are migrating the first screen, do not touch the first row.
@@ -362,9 +370,9 @@
/**
* Tries the remove the provided row and column.
*
- * @param items all the items on the screen under operation
+ * @param items all the items on the screen under operation
* @param outLoss array of size 2. The first entry is filled with weight loss, and the second
- * with the overall item movement.
+ * with the overall item movement.
*/
private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
ArrayList<DbEntry> items, float[] outLoss) {
@@ -379,13 +387,13 @@
for (DbEntry item : items) {
if ((item.cellX <= col && (item.spanX + item.cellX) > col)
- || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
+ || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
removedItems.add(item);
- if (item.cellX >= col) item.cellX --;
- if (item.cellY >= row) item.cellY --;
+ if (item.cellX >= col) item.cellX--;
+ if (item.cellY >= row) item.cellY--;
} else {
- if (item.cellX > col) item.cellX --;
- if (item.cellY > row) item.cellY --;
+ if (item.cellX > col) item.cellX--;
+ if (item.cellY > row) item.cellY--;
finalItems.add(item);
occupied.markCells(item, true);
}
@@ -438,9 +446,9 @@
/**
* Recursively finds a placement for the provided items.
*
- * @param index the position in {@link #itemsToPlace} to start looking at.
- * @param weightLoss total weight loss upto this point
- * @param moveCost total move cost upto this point
+ * @param index the position in {@link #itemsToPlace} to start looking at.
+ * @param weightLoss total weight loss upto this point
+ * @param moveCost total move cost upto this point
* @param itemsPlaced all the items already placed upto this point
*/
public void find(int index, float weightLoss, float moveCost,
@@ -481,11 +489,11 @@
float newMoveCost = moveCost;
if (x != myX) {
me.cellX = x;
- newMoveCost ++;
+ newMoveCost++;
}
if (y != myY) {
me.cellY = y;
- newMoveCost ++;
+ newMoveCost++;
}
if (ignoreMove) {
newMoveCost = moveCost;
@@ -500,35 +508,35 @@
// Try resizing horizontally
if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
- me.spanX --;
+ me.spanX--;
occupied.markCells(me, true);
// 1 extra move cost
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
occupied.markCells(me, false);
- me.spanX ++;
+ me.spanX++;
}
// Try resizing vertically
if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
- me.spanY --;
+ me.spanY--;
occupied.markCells(me, true);
// 1 extra move cost
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
occupied.markCells(me, false);
- me.spanY ++;
+ me.spanY++;
}
// Try resizing horizontally & vertically
if (myH > me.minSpanY && myW > me.minSpanX &&
occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
- me.spanX --;
- me.spanY --;
+ me.spanX--;
+ me.spanY--;
occupied.markCells(me, true);
// 2 extra move cost
find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
occupied.markCells(me, false);
- me.spanX ++;
- me.spanY ++;
+ me.spanX++;
+ me.spanY++;
}
me.cellX = myX;
me.cellY = myY;
@@ -565,11 +573,11 @@
float newMoveCost = moveCost;
if (newX != myX) {
me.cellX = newX;
- newMoveCost ++;
+ newMoveCost++;
}
if (newY != myY) {
me.cellY = newY;
- newMoveCost ++;
+ newMoveCost++;
}
if (ignoreMove) {
newMoveCost = moveCost;
@@ -602,7 +610,7 @@
}
private ArrayList<DbEntry> loadHotseatEntries() {
- Cursor c = queryWorkspace(
+ Cursor c = queryWorkspace(
new String[]{
Favorites._ID, // 0
Favorites.ITEM_TYPE, // 1
@@ -787,7 +795,7 @@
}
protected Cursor queryWorkspace(String[] columns, String where) {
- return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
+ return mDb.query(mTableName, columns, where, null, null, null, null);
}
/**
@@ -879,24 +887,44 @@
}
/**
- * Migrates the workspace and hotseat in case their sizes changed.
+ * 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) {
+ if (context instanceof LauncherPreviewRenderer.PreviewContext) {
+ return true;
+ }
+ return migrateGridIfNeeded(context, null);
+ }
+
+ /**
+ * Run the migration algorithm if needed. For preview, we provide the intended idp because it
+ * has not been changed. If idp is null, we read it from the context, for actual grid migration.
*
* @return false if the migration failed.
*/
- public static boolean migrateGridIfNeeded(Context context) {
- SharedPreferences prefs = Utilities.getPrefs(context);
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
+ boolean migrateForPreview = idp != null;
+ if (!migrateForPreview) {
+ idp = LauncherAppState.getIDP(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.
+ if (!needsToMigrate(context, idp)) {
return true;
}
- long migrationStartTime = System.currentTimeMillis();
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ String gridSizeString = getPointString(idp.numColumns, idp.numRows);
+ long migrationStartTime = SystemClock.elapsedRealtime();
try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
.getBinder(Settings.EXTRA_VALUE)) {
@@ -907,33 +935,39 @@
KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
boolean dbChanged = false;
+ if (migrateForPreview) {
+ copyTable(transaction.getDb(), Favorites.TABLE_NAME, Favorites.PREVIEW_TABLE_NAME,
+ context);
+ }
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
srcHotseatCount, sourceSize.x, sourceSize.y);
- if (backupTable.backupOrRestoreAsNeeded()) {
+ if (migrateForPreview ? backupTable.restoreToPreviewIfBackupExists()
+ : backupTable.backupOrRestoreAsNeeded()) {
dbChanged = true;
srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
}
HashSet<String> validPackages = getValidPackages(context);
- // Hotseat
+ // Hotseat.
if (srcHotseatCount != idp.numHotseatIcons) {
// Migrate hotseat.
- dbChanged = new GridSizeMigrationTask(context, transaction.getDb(),
- validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
+ dbChanged = new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
+ migrateForPreview, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
}
// Grid size
Point targetSize = new Point(idp.numColumns, idp.numRows);
- if (new MultiStepMigrationTask(validPackages, context, transaction.getDb())
- .migrate(sourceSize, targetSize)) {
+ if (new MultiStepMigrationTask(validPackages, context, transaction.getDb(),
+ migrateForPreview).migrate(sourceSize, targetSize)) {
dbChanged = true;
}
if (dbChanged) {
// Make sure we haven't removed everything.
final Cursor c = context.getContentResolver().query(
- Favorites.CONTENT_URI, null, null, null, null);
+ migrateForPreview ? Favorites.PREVIEW_CONTENT_URI : Favorites.CONTENT_URI,
+ null, null, null, null);
boolean hasData = c.moveToNext();
c.close();
if (!hasData) {
@@ -942,21 +976,25 @@
}
transaction.commit();
- Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+ if (!migrateForPreview) {
+ Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+ }
return true;
} catch (Exception e) {
- Log.e(TAG, "Error during grid migration", e);
+ Log.e(TAG, "Error during preview grid migration", e);
return false;
} finally {
- Log.v(TAG, "Workspace migration completed in "
- + (System.currentTimeMillis() - migrationStartTime));
+ Log.v(TAG, "Preview workspace migration completed in "
+ + (SystemClock.elapsedRealtime() - 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();
+ if (!migrateForPreview) {
+ // Save current configuration, so that the migration does not run again.
+ prefs.edit()
+ .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
+ .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+ .apply();
+ }
}
}
@@ -989,7 +1027,7 @@
.getBinder(Settings.EXTRA_VALUE)) {
GridSizeMigrationTask task = new GridSizeMigrationTask(
context, transaction.getDb(), getValidPackages(context),
- Integer.MAX_VALUE, Integer.MAX_VALUE);
+ false /* usePreviewTable */, Integer.MAX_VALUE, Integer.MAX_VALUE);
// Load all the valid entries
ArrayList<DbEntry> items = task.loadHotseatEntries();
@@ -1011,12 +1049,14 @@
private final HashSet<String> mValidPackages;
private final Context mContext;
private final SQLiteDatabase mDb;
+ private final boolean mUsePreviewTable;
public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
- SQLiteDatabase db) {
+ SQLiteDatabase db, boolean usePreviewTable) {
mValidPackages = validPackages;
mContext = context;
mDb = db;
+ mUsePreviewTable = usePreviewTable;
}
public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
@@ -1052,8 +1092,8 @@
}
protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
- return new GridSizeMigrationTask(mContext, mDb,
- mValidPackages, sourceSize, nextSize).migrateWorkspace();
+ return new GridSizeMigrationTask(mContext, mDb, mValidPackages, mUsePreviewTable,
+ sourceSize, nextSize).migrateWorkspace();
}
}
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 63b7191..197b29c 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -18,6 +18,8 @@
import android.content.Context;
+import com.android.launcher3.InvariantDeviceProfile;
+
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
* result of restoring from a larger device or device density change.
@@ -28,12 +30,19 @@
}
+ /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
+ public static boolean migrateGridIfNeeded(Context context) {
+ // To be implemented.
+ return true;
+ }
+
/**
- * Migrates the workspace and hotseat in case their sizes changed.
+ * Run the migration algorithm if needed. For preview, we provide the intended idp because it
+ * has not been changed. If idp is null, we read it from the context, for actual grid migration.
*
* @return false if the migration failed.
*/
- public static boolean migrateGridIfNeeded(Context context) {
+ public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
// To be implemented.
return true;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 4961432..2311dcc 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
+import android.net.Uri;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -64,6 +65,7 @@
public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+ private final Uri mContentUri;
private final Context mContext;
private final PackageManager mPM;
private final IconCache mIconCache;
@@ -96,8 +98,10 @@
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor c, LauncherAppState app) {
- super(c);
+ public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app) {
+ super(cursor);
+
+ mContentUri = contentUri;
mContext = app.getContext();
mIconCache = app.getIconCache();
mIDP = app.getInvariantDeviceProfile();
@@ -312,9 +316,8 @@
public boolean commitDeleted() {
if (itemsToRemove.size() > 0) {
// Remove dead items
- mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, itemsToRemove), null);
+ mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery(
+ LauncherSettings.Favorites._ID, itemsToRemove), null);
return true;
}
return false;
@@ -339,7 +342,7 @@
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.RESTORED, 0);
- mContext.getContentResolver().update(LauncherSettings.Favorites.CONTENT_URI, values,
+ mContext.getContentResolver().update(mContentUri, values,
Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, restoredRows), null);
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 6223a23..fbf01fc 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -36,6 +36,7 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.ShortcutInfo;
+import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -76,7 +77,6 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.LooperIdleLock;
@@ -106,7 +106,7 @@
private final LauncherAppState mApp;
private final AllAppsList mBgAllAppsList;
- private final BgDataModel mBgDataModel;
+ protected final BgDataModel mBgDataModel;
private FirstScreenBroadcast mFirstScreenBroadcast;
@@ -284,6 +284,10 @@
@VisibleForTesting
void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
+ loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI);
+ }
+
+ protected void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri) {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -327,8 +331,8 @@
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(contentResolver.query(
- LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
+ final LoaderCursor c = new LoaderCursor(
+ contentResolver.query(contentUri, null, null, null, null), contentUri, mApp);
Map<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 2c843f9..f7ecc3f 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -22,10 +22,12 @@
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
+import android.os.Process;
import android.util.Log;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.IntArray;
import java.util.Locale;
@@ -116,6 +118,15 @@
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) {
+ 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);
+ }
+
/**
* Utility class to simplify managing sqlite transactions
*/
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
index 6d839f3..c0111b9 100644
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -18,7 +18,6 @@
import android.content.ContentValues;
import android.content.Context;
-import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
@@ -43,7 +42,7 @@
protected LossyScreenMigrationTask(
Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
// Decrease the rows count by 1
- super(context, db, getValidPackages(context),
+ super(context, db, getValidPackages(context), false /* usePreviewTable */,
new Point(idp.numColumns, idp.numRows + 1),
new Point(idp.numColumns, idp.numRows));
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 520a9ed..fc9f8f7 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -22,6 +22,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.concurrent.ExecutionException;
@@ -39,6 +40,10 @@
}
public T get(Context context) {
+ if (context instanceof PreviewContext) {
+ return ((PreviewContext) context).getObject(this, mProvider);
+ }
+
if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
mValue = TraceHelper.whitelistIpcs("main.thread.object",