Merge "Render user's actual workspace in ThemePicker preview (Part 3)" into ub-launcher3-master
This commit is contained in:
commit
4365734017
|
@ -65,7 +65,7 @@ public class GridSizeMigrationTaskTest {
|
|||
};
|
||||
|
||||
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 @@ public class GridSizeMigrationTaskTest {
|
|||
};
|
||||
|
||||
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 @@ public class GridSizeMigrationTaskTest {
|
|||
{ 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 @@ public class GridSizeMigrationTaskTest {
|
|||
{ 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 @@ public class GridSizeMigrationTaskTest {
|
|||
{ 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 @@ public class GridSizeMigrationTaskTest {
|
|||
{ 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 @@ public class GridSizeMigrationTaskTest {
|
|||
{ 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 @@ public class GridSizeMigrationTaskTest {
|
|||
{ 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 @@ public class GridSizeMigrationTaskTest {
|
|||
* 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 @@ public class GridSizeMigrationTaskTest {
|
|||
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) {
|
||||
|
|
|
@ -52,6 +52,7 @@ import android.os.Process;
|
|||
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 @@ public class LoaderCursorTest {
|
|||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ import androidx.annotation.Nullable;
|
|||
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 @@ public class InvariantDeviceProfile {
|
|||
|
||||
@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;
|
||||
|
|
|
@ -27,6 +27,8 @@ import android.content.pm.LauncherApps;
|
|||
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 @@ public class LauncherAppState {
|
|||
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 @@ public class LauncherAppState {
|
|||
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 class LauncherAppState {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ public class LauncherAppState {
|
|||
* 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) {
|
||||
|
|
|
@ -93,15 +93,26 @@ public class LauncherSettings {
|
|||
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 @@ public class LauncherSettings {
|
|||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,14 +20,19 @@ import static android.view.View.MeasureSpec.makeMeasureSpec;
|
|||
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.folder.FolderIcon;
|
|||
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 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
|
|||
|
||||
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 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
|
|||
|
||||
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 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -398,6 +510,11 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
|
|||
mAllAppsList = allAppsList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FutureTask<WorkspaceResult> getTask() {
|
||||
return mTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mTask.run();
|
||||
|
@ -417,9 +534,45 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -81,9 +81,13 @@ public class IconCache extends BaseIconCache {
|
|||
|
||||
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();
|
||||
|
|
|
@ -19,8 +19,8 @@ package com.android.launcher3.icons;
|
|||
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 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
* 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 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
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 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
|
||||
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;
|
||||
|
|
|
@ -100,12 +100,24 @@ public class GridBackupTable {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.android.launcher3.model;
|
|||
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.content.pm.PackageManager;
|
|||
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.LauncherSettings.Settings;
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
|
||||
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 @@ public class GridSizeMigrationTask {
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
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 @@ public class GridSizeMigrationTask {
|
|||
// 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 @@ public class GridSizeMigrationTask {
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
@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 @@ public class GridSizeMigrationTask {
|
|||
* @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 @@ public class GridSizeMigrationTask {
|
|||
/**
|
||||
* 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 @@ public class GridSizeMigrationTask {
|
|||
/**
|
||||
* 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 @@ public class GridSizeMigrationTask {
|
|||
|
||||
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 @@ public class GridSizeMigrationTask {
|
|||
/**
|
||||
* 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 @@ public class GridSizeMigrationTask {
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
|
||||
// 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 @@ public class GridSizeMigrationTask {
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
private ArrayList<DbEntry> loadHotseatEntries() {
|
||||
Cursor c = queryWorkspace(
|
||||
Cursor c = queryWorkspace(
|
||||
new String[]{
|
||||
Favorites._ID, // 0
|
||||
Favorites.ITEM_TYPE, // 1
|
||||
|
@ -787,7 +795,7 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
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 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public class GridSizeMigrationTask {
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
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 @@ public class GridSizeMigrationTask {
|
|||
.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 @@ public class GridSizeMigrationTask {
|
|||
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 @@ public class GridSizeMigrationTask {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package com.android.launcher3.model;
|
|||
|
||||
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,13 +30,20 @@ public class GridSizeMigrationTaskV2 {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the workspace and hotseat in case their sizes changed.
|
||||
*
|
||||
* @return false if the migration failed.
|
||||
*/
|
||||
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
|
||||
public static boolean migrateGridIfNeeded(Context context) {
|
||||
// To be implemented.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, InvariantDeviceProfile idp) {
|
||||
// To be implemented.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import android.content.pm.LauncherApps;
|
|||
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 class LoaderCursor extends CursorWrapper {
|
|||
|
||||
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 class LoaderCursor extends CursorWrapper {
|
|||
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 class LoaderCursor extends CursorWrapper {
|
|||
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 @@ public class LoaderCursor extends CursorWrapper {
|
|||
// 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);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import android.content.pm.LauncherApps;
|
|||
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.qsb.QsbContainerView;
|
|||
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 @@ public class LoaderTask implements Runnable {
|
|||
|
||||
private final LauncherAppState mApp;
|
||||
private final AllAppsList mBgAllAppsList;
|
||||
private final BgDataModel mBgDataModel;
|
||||
protected final BgDataModel mBgDataModel;
|
||||
|
||||
private FirstScreenBroadcast mFirstScreenBroadcast;
|
||||
|
||||
|
@ -284,6 +284,10 @@ public class LoaderTask implements Runnable {
|
|||
|
||||
@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 @@ public class LoaderTask implements Runnable {
|
|||
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;
|
||||
|
||||
|
|
|
@ -22,10 +22,12 @@ import android.database.Cursor;
|
|||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Binder;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
|
||||
import java.util.Locale;
|
||||
|
@ -116,6 +118,15 @@ 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) {
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -18,7 +18,6 @@ package com.android.launcher3.provider;
|
|||
|
||||
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 @@ public class LossyScreenMigrationTask extends GridSizeMigrationTask {
|
|||
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));
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.os.Looper;
|
|||
|
||||
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 class MainThreadInitializedObject<T> {
|
|||
}
|
||||
|
||||
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",
|
||||
|
|
Loading…
Reference in New Issue