diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index dc28c6aabc..3819dd4d3f 100644 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java @@ -31,7 +31,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherModel; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.testing.shared.TestProtocol; @@ -189,10 +189,11 @@ public class DebugTestInformationHandler extends TestInformationHandler { case TestProtocol.REQUEST_CLEAR_DATA: { final long identity = Binder.clearCallingIdentity(); try { - LauncherSettings.Settings.call(mContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - MAIN_EXECUTOR.submit(() -> - LauncherAppState.getInstance(mContext).getModel().forceReload()); + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + MAIN_EXECUTOR.execute(model::forceReload); + }); return response; } finally { Binder.restoreCallingIdentity(identity); diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index 048243e345..b77c43fc2f 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -95,9 +95,7 @@ public class HotseatEduController { } } if (pageId == -1) { - pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + pageId = mLauncher.getModel().getModelDbController().getNewScreenId(); mNewScreens = IntArray.wrap(pageId); } boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout(); diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java index 726abff5da..8c01d04095 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java @@ -22,9 +22,10 @@ import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherModel; import com.android.launcher3.model.GridBackupTable; -import com.android.launcher3.provider.LauncherDbUtils; +import com.android.launcher3.model.ModelDbController; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; /** * A helper class to manage migration revert restoration for hybrid hotseat @@ -36,16 +37,13 @@ public class HotseatRestoreHelper { */ public static void createBackup(Context context) { MODEL_EXECUTOR.execute(() -> { - try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) - LauncherSettings.Settings.call( - context.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_TRANSACTION) - .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + ModelDbController dbController = LauncherAppState.getInstance(context) + .getModel().getModelDbController(); + try (SQLiteTransaction transaction = dbController.newTransaction()) { GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb()); backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE); transaction.commit(); - LauncherSettings.Settings.call(context.getContentResolver(), - LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE); + dbController.refreshHotseatRestoreTable(); } }); } @@ -55,18 +53,15 @@ public class HotseatRestoreHelper { */ public static void restoreBackup(Context context) { MODEL_EXECUTOR.execute(() -> { - try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) - LauncherSettings.Settings.call( - context.getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_TRANSACTION) - .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + LauncherModel model = LauncherAppState.getInstance(context).getModel(); + try (SQLiteTransaction transaction = model.getModelDbController().newTransaction()) { if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) { return; } GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb()); backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true); transaction.commit(); - LauncherAppState.getInstance(context).getModel().forceReload(); + model.forceReload(); } }); } diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java index acb2db5925..8cc84875e0 100644 --- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java +++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java @@ -17,8 +17,8 @@ package com.android.quickstep; import static androidx.test.InstrumentationRegistry.getContext; import static androidx.test.InstrumentationRegistry.getInstrumentation; -import static androidx.test.InstrumentationRegistry.getTargetContext; +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE; import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; @@ -32,7 +32,6 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; import android.appwidget.AppWidgetManager; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -42,14 +41,16 @@ import android.view.View; import android.view.ViewConfiguration; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.filters.Suppress; -import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.tapl.LaunchedAppState; import com.android.launcher3.testcomponent.ListViewService; @@ -57,6 +58,7 @@ import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.Executors; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; @@ -67,6 +69,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.IntConsumer; /** @@ -84,9 +87,9 @@ import java.util.function.IntConsumer; @RunWith(AndroidJUnit4.class) public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { - private ContentResolver mResolver; private SparseArray mConfigMap; private InitTracker mInitTracker; + private LauncherModel mModel; @Before public void setUp() throws Exception { @@ -101,8 +104,8 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { TaplTestsLauncher3.initialize(this); - mResolver = mTargetContext.getContentResolver(); - LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + mModel = LauncherAppState.getInstance(mTargetContext).getModel(); + Executors.MODEL_EXECUTOR.submit(mModel.getModelDbController()::createEmptyDB).get(); // Get static configuration map Field field = ViewConfiguration.class.getDeclaredField("sConfigurations"); @@ -182,26 +185,30 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { private void executeSwipeUpTestWithWidget(IntConsumer widgetIdCreationCallback, IntConsumer updateBeforeSwipeUp, String finalWidgetText) { try { - // Clear all existing data - LauncherSettings.Settings.call(mResolver, - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mResolver, - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(false); + // Make sure the widget is big enough to show a list of items info.minSpanX = 2; info.minSpanY = 2; info.spanX = 2; info.spanY = 2; - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true); + AtomicInteger widgetId = new AtomicInteger(); + new FavoriteItemsTransaction(mTargetContext) + .addItem(() -> { + LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, true); + item.screenId = FIRST_SCREEN_ID; + widgetId.set(item.appWidgetId); + return item; + }) + .commitAndLoadHome(mLauncher); + + - addItemToScreen(item); assertTrue("Widget is not present", mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null); - int widgetId = item.appWidgetId; // Verify widget id - widgetIdCreationCallback.accept(widgetId); + widgetIdCreationCallback.accept(widgetId.get()); // Go to overview once so that all views are initialized and cached startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); @@ -214,7 +221,7 @@ public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { LaunchedAppState launchedAppState = mLauncher.getLaunchedAppState(); // Update widget - updateBeforeSwipeUp.accept(widgetId); + updateBeforeSwipeUp.accept(widgetId.get()); launchedAppState.switchToOverview(); assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount); diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index ede7e2f950..c7cdfa8c69 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -17,6 +17,8 @@ package com.android.launcher3; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; import android.content.ComponentName; import android.content.ContentValues; @@ -30,7 +32,6 @@ import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.XmlResourceParser; import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; import android.os.Bundle; import android.os.Process; import android.text.TextUtils; @@ -44,7 +45,6 @@ import androidx.annotation.StringRes; import androidx.annotation.WorkerThread; import androidx.annotation.XmlRes; -import com.android.launcher3.LauncherProvider.SqlArguments; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; @@ -619,9 +619,7 @@ public class AutoInstallsLayout { // failed to add, and less than 2 were actually added if (folderItems.size() < 2) { // Delete the folder - Uri uri = Favorites.getContentUri(folderId); - SqlArguments args = new SqlArguments(uri, null, null); - mDb.delete(args.table, args.where, args.args); + mDb.delete(TABLE_NAME, itemIdMatch(folderId), null); addedId = -1; // If we have a single item, promote it to where the folder @@ -634,7 +632,7 @@ public class AutoInstallsLayout { copyInteger(myValues, childValues, Favorites.CELLY); addedId = folderItems.get(0); - mDb.update(Favorites.TABLE_NAME, childValues, + mDb.update(TABLE_NAME, childValues, Favorites._ID + "=" + addedId, null); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 4e066b0985..f225f855f1 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -143,6 +143,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi @NonNull private final ModelDelegate mModelDelegate; + private int mLastLoadId = -1; + // Runnable to check if the shortcuts permission has changed. @NonNull private final Runnable mDataValidationCheck = new Runnable() { @@ -553,6 +555,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi if (mLoaderTask != task) { throw new CancellationException("Loader already stopped"); } + mLastLoadId++; mTask = task; mIsLoaderTaskRunning = true; mModelLoaded = false; @@ -722,4 +725,12 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]); } } + + /** + * Returns the ID for the last model load. If the load ID doesn't match for a transaction, the + * transaction should be ignored. + */ + public int getLastLoadId() { + return mLastLoadId; + } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 9abec505c7..440e1465ea 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,21 +16,18 @@ package com.android.launcher3; -import android.annotation.TargetApi; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentProvider; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; -import android.content.OperationApplicationException; import android.database.Cursor; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.Binder; -import android.os.Build; -import android.os.Bundle; import android.os.Process; import android.text.TextUtils; import android.util.Log; @@ -38,12 +35,11 @@ import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.ModelDbController; -import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.widget.LauncherWidgetHolder; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; +import java.util.function.ToIntFunction; public class LauncherProvider extends ContentProvider { private static final String TAG = "LauncherProvider"; @@ -74,10 +70,6 @@ public class LauncherProvider extends ContentProvider { return true; } - public ModelDbController getModelDbController() { - return LauncherAppState.getInstance(getContext()).getModel().getModelDbController(); - } - @Override public String getType(Uri uri) { SqlArguments args = new SqlArguments(uri, null, null); @@ -91,180 +83,91 @@ public class LauncherProvider extends ContentProvider { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - SqlArguments args = new SqlArguments(uri, selection, selectionArgs); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(args.table); - Cursor result = getModelDbController().query( - args.table, projection, args.where, args.args, sortOrder); - result.setNotificationUri(getContext().getContentResolver(), uri); - return result; - } - - private void reloadLauncherIfExternal() { - if (Binder.getCallingPid() != Process.myPid()) { - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app != null) { - app.getModel().forceReload(); - } - } + Cursor[] result = new Cursor[1]; + executeControllerTask(controller -> { + result[0] = controller.query(args.table, projection, args.where, args.args, sortOrder); + return 0; + }); + return result[0]; } @Override - public Uri insert(Uri uri, ContentValues initialValues) { - // In very limited cases, we support system|signature permission apps to modify the db. - if (Binder.getCallingPid() != Process.myPid()) { - if (!initializeExternalAdd(initialValues)) { - return null; - } - } + public Uri insert(Uri uri, ContentValues values) { + int rowId = executeControllerTask(controller -> { + // 1. Ensure that externally added items have a valid item id + int id = controller.generateNewItemId(); + values.put(LauncherSettings.Favorites._ID, id); - SqlArguments args = new SqlArguments(uri); - int rowId = getModelDbController().insert(args.table, initialValues); - if (rowId < 0) return null; + // 2. In the case of an app widget, and if no app widget id is specified, we + // attempt allocate and bind the widget. + Integer itemType = values.getAsInteger(Favorites.ITEM_TYPE); + if (itemType != null + && itemType.intValue() == Favorites.ITEM_TYPE_APPWIDGET + && !values.containsKey(Favorites.APPWIDGET_ID)) { - uri = ContentUris.withAppendedId(uri, rowId); - reloadLauncherIfExternal(); - return uri; - } + ComponentName cn = ComponentName.unflattenFromString( + values.getAsString(Favorites.APPWIDGET_PROVIDER)); + if (cn == null) { + return 0; + } - private boolean initializeExternalAdd(ContentValues values) { - // 1. Ensure that externally added items have a valid item id - int id = getModelDbController().generateNewItemId(); - values.put(LauncherSettings.Favorites._ID, id); - - // 2. In the case of an app widget, and if no app widget id is specified, we - // attempt allocate and bind the widget. - Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE); - if (itemType != null && - itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && - !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) { - - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext()); - ComponentName cn = ComponentName.unflattenFromString( - values.getAsString(Favorites.APPWIDGET_PROVIDER)); - - if (cn != null) { LauncherWidgetHolder widgetHolder = LauncherWidgetHolder.newInstance(getContext()); try { int appWidgetId = widgetHolder.allocateAppWidgetId(); values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId); - if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) { + if (!AppWidgetManager.getInstance(getContext()) + .bindAppWidgetIdIfAllowed(appWidgetId, cn)) { widgetHolder.deleteAppWidgetId(appWidgetId); - return false; + return 0; } } catch (RuntimeException e) { Log.e(TAG, "Failed to initialize external widget", e); - return false; + return 0; } finally { // Necessary to destroy the holder to free up possible activity context widgetHolder.destroy(); } - } else { - return false; } - } - return true; - } + SqlArguments args = new SqlArguments(uri); + return controller.insert(args.table, values); + }); - @Override - public int bulkInsert(Uri uri, ContentValues[] values) { - SqlArguments args = new SqlArguments(uri); - getModelDbController().bulkInsert(args.table, values); - reloadLauncherIfExternal(); - return values.length; - } - - @TargetApi(Build.VERSION_CODES.M) - @Override - public ContentProviderResult[] applyBatch(ArrayList operations) - throws OperationApplicationException { - try (SQLiteTransaction t = getModelDbController().newTransaction()) { - final int numOperations = operations.size(); - final ContentProviderResult[] results = new ContentProviderResult[numOperations]; - for (int i = 0; i < numOperations; i++) { - ContentProviderOperation op = operations.get(i); - results[i] = op.apply(this, results, i); - } - t.commit(); - reloadLauncherIfExternal(); - return results; - } + return rowId < 0 ? null : ContentUris.withAppendedId(uri, rowId); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SqlArguments args = new SqlArguments(uri, selection, selectionArgs); - int count = getModelDbController().delete(args.table, args.where, args.args); - if (count > 0) { - reloadLauncherIfExternal(); - } - return count; + return executeControllerTask(c -> c.delete(args.table, args.where, args.args)); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SqlArguments args = new SqlArguments(uri, selection, selectionArgs); - int count = getModelDbController().update(args.table, values, args.where, args.args); - reloadLauncherIfExternal(); - return count; + return executeControllerTask(c -> c.update(args.table, values, args.where, args.args)); } - @Override - public Bundle call(String method, final String arg, final Bundle extras) { - if (Binder.getCallingUid() != Process.myUid()) { - return null; + private int executeControllerTask(ToIntFunction task) { + if (Binder.getCallingPid() == Process.myPid()) { + throw new IllegalArgumentException("Same process should call model directly"); } - - switch (method) { - case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: { - getModelDbController().clearEmptyDbFlag(); - return null; - } - case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: { - Bundle result = new Bundle(); - result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().deleteEmptyFolders().toArray()); - return result; - } - case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: { - Bundle result = new Bundle(); - result.putInt(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().generateNewItemId()); - return result; - } - case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: { - Bundle result = new Bundle(); - result.putInt(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().getNewScreenId()); - return result; - } - case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: { - getModelDbController().createEmptyDB(); - return null; - } - case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: { - getModelDbController().loadDefaultFavoritesIfNecessary(); - return null; - } - case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: { - getModelDbController().removeGhostWidgets(); - return null; - } - case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: { - Bundle result = new Bundle(); - result.putBinder(LauncherSettings.Settings.EXTRA_VALUE, - getModelDbController().newTransaction()); - return result; - } - case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: { - getModelDbController().refreshHotseatRestoreTable(); - return null; - } + try { + return MODEL_EXECUTOR.submit(() -> { + LauncherModel model = LauncherAppState.getInstance(getContext()).getModel(); + int count = task.applyAsInt(model.getModelDbController()); + if (count > 0) { + MAIN_EXECUTOR.submit(model::forceReload); + } + return count; + }).get(); + } catch (Exception e) { + throw new IllegalStateException(e); } - return null; } static class SqlArguments { diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 2397429346..105d5f30c8 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -16,10 +16,7 @@ package com.android.launcher3; -import android.content.ContentResolver; import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.os.Bundle; import android.provider.BaseColumns; import com.android.launcher3.model.data.ItemInfo; @@ -154,24 +151,6 @@ public class LauncherSettings { */ public static final String TMP_TABLE = "favorites_tmp"; - /** - * 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 a given row, identified by its id. - * - * @param id The row id. - * - * @return The unique content URL for the specified row. - */ - public static Uri getContentUri(int id) { - return Uri.parse("content://" + LauncherProvider.AUTHORITY - + "/" + TABLE_NAME + "/" + id); - } - /** * The container holding the favorite *

Type: INTEGER

@@ -339,42 +318,8 @@ public class LauncherSettings { * Launcher settings */ public static final class Settings { - - public static final Uri CONTENT_URI = Uri.parse("content://" + - LauncherProvider.AUTHORITY + "/settings"); - - public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag"; - - public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders"; - - public static final String METHOD_NEW_ITEM_ID = "generate_new_item_id"; - public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id"; - - public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db"; - - public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites"; - - public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets"; - - public static final String METHOD_NEW_TRANSACTION = "new_db_transaction"; - - public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table"; - - public static final String EXTRA_VALUE = "value"; - - public static final String EXTRA_DB_NAME = "db_name"; - public static final String LAYOUT_DIGEST_KEY = "launcher3.layout.provider.blob"; public static final String LAYOUT_DIGEST_LABEL = "launcher-layout"; public static final String LAYOUT_DIGEST_TAG = "ignore"; - - public static Bundle call(ContentResolver cr, String method) { - return call(cr, method, null /* arg */); - } - - public static Bundle call(ContentResolver cr, String method, String arg) { - return cr.call(CONTENT_URI, method, arg, null); - } - } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 52755d4e2c..adaf20f86f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -897,9 +897,8 @@ public class Workspace extends PagedView mWorkspaceScreens.remove(emptyScreenId); mScreenOrder.removeValue(emptyScreenId); - int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + int newScreenId = LauncherAppState.getInstance(getContext()) + .getModel().getModelDbController().getNewScreenId(); // Launcher database isn't aware of empty pages that are already bound, so we need to // skip those IDs manually. while (mWorkspaceScreens.containsKey(newScreenId)) { diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java index 5d85b1cf6e..1f165d1d54 100644 --- a/src/com/android/launcher3/model/BaseLauncherBinder.java +++ b/src/com/android/launcher3/model/BaseLauncherBinder.java @@ -106,6 +106,7 @@ public abstract class BaseLauncherBinder { synchronized (mBgDataModel) { if (incrementBindId) { mBgDataModel.lastBindId++; + mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId(); } mMyBindingId = mBgDataModel.lastBindId; return new DisjointWorkspaceBinder(workspacePages); @@ -126,6 +127,7 @@ public abstract class BaseLauncherBinder { mBgDataModel.extraItems.forEach(extraItems::add); if (incrementBindId) { mBgDataModel.lastBindId++; + mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId(); } mMyBindingId = mBgDataModel.lastBindId; workspaceItemCount = mBgDataModel.itemsIdMap.size(); diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 1f5670765b..7bcd03863c 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -125,6 +125,11 @@ public class BgDataModel { */ public int lastBindId = 0; + /** + * Load id for which the callbacks were successfully bound + */ + public int lastLoadId = -1; + /** * Clears all the data */ diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 4f542febb4..3daf4af159 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -33,7 +33,6 @@ import static com.android.launcher3.util.PackageManagerHelper.isSystemApp; import android.annotation.SuppressLint; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -61,7 +60,6 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; @@ -361,16 +359,15 @@ public class LoaderTask implements Runnable { String selection, @Nullable LoaderMemoryLogger memoryLogger) { final Context context = mApp.getContext(); - final ContentResolver contentResolver = context.getContentResolver(); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); final boolean isSafeMode = pmHelper.isSafeMode(); final boolean isSdCardReady = Utilities.isBootCompleted(); final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context); - mApp.getModel().getModelDbController().tryMigrateDB(); + ModelDbController dbController = mApp.getModel().getModelDbController(); + dbController.tryMigrateDB(); Log.d(TAG, "loadWorkspace: loading default favorites"); - mApp.getModel().getModelDbController().loadDefaultFavoritesIfNecessary(); - Settings.call(contentResolver, Settings.METHOD_LOAD_DEFAULT_FAVORITES); + dbController.loadDefaultFavoritesIfNecessary(); synchronized (mBgDataModel) { mBgDataModel.clear(); @@ -384,12 +381,11 @@ public class LoaderTask implements Runnable { mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); mShortcutKeyToPinnedShortcuts = new HashMap<>(); - ModelDbController dbController = mApp.getModel().getModelDbController(); final LoaderCursor c = new LoaderCursor( dbController.query(TABLE_NAME, null, selection, null, null), mApp, mUserManagerState); final Bundle extras = c.getExtras(); - mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME); + mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME); try { final LongSparseArray unlockedUsers = new LongSparseArray<>(); @@ -906,9 +902,7 @@ public class LoaderTask implements Runnable { private void sanitizeFolders(boolean itemsDeleted) { if (itemsDeleted) { // Remove any empty folder - int[] deletedFolderIds = Settings.call(mApp.getContext().getContentResolver(), - Settings.METHOD_DELETE_EMPTY_FOLDERS) - .getIntArray(Settings.EXTRA_VALUE); + IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders(); synchronized (mBgDataModel) { for (int folderId : deletedFolderIds) { mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); @@ -921,11 +915,9 @@ public class LoaderTask implements Runnable { private void sanitizeWidgetsShortcutsAndPackages() { Context context = mApp.getContext(); - ContentResolver contentResolver = context.getContentResolver(); // Remove any ghost widgets - Settings.call(contentResolver, - Settings.METHOD_REMOVE_GHOST_WIDGETS); + mApp.getModel().getModelDbController().removeGhostWidgets(); // Update pinned state of model shortcuts mBgDataModel.updateShortcutPinnedState(context); diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java index d5cc82b3f9..1b1b38f4fd 100644 --- a/src/com/android/launcher3/model/ModelDbController.java +++ b/src/com/android/launcher3/model/ModelDbController.java @@ -37,7 +37,6 @@ import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; -import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -85,6 +84,7 @@ public class ModelDbController { private static final String TAG = "LauncherProvider"; private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; + public static final String EXTRA_DB_NAME = "db_name"; protected DatabaseHelper mOpenHelper; @@ -140,7 +140,7 @@ public class ModelDbController { table, projection, selection, selectionArgs, null, null, sortOrder); final Bundle extra = new Bundle(); - extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName()); + extra.putString(EXTRA_DB_NAME, mOpenHelper.getDatabaseName()); result.setExtras(extra); return result; } @@ -161,28 +161,6 @@ public class ModelDbController { return rowId; } - /** - * Similar to insert but for adding multiple values in a transaction. - */ - @WorkerThread - public int bulkInsert(String table, ContentValues[] values) { - createDbIfNotExists(); - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - try (SQLiteTransaction t = new SQLiteTransaction(db)) { - int numValues = values.length; - for (int i = 0; i < numValues; i++) { - addModifiedTime(values[i]); - if (mOpenHelper.dbInsertAndCheck(db, table, values[i]) < 0) { - return 0; - } - } - onAddOrDeleteOp(db); - t.commit(); - } - return values.length; - } - /** * Refer {@link SQLiteDatabase#delete(String, String, String[])} */ @@ -191,10 +169,6 @@ public class ModelDbController { createDbIfNotExists(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - if (Binder.getCallingPid() != Process.myPid() - && Favorites.TABLE_NAME.equalsIgnoreCase(table)) { - mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase()); - } int count = db.delete(table, selection, selectionArgs); if (count > 0) { onAddOrDeleteOp(db); @@ -250,6 +224,7 @@ public class ModelDbController { public void createEmptyDB() { createDbIfNotExists(); mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase()); + LauncherPrefs.get(mContext).putSync(getEmptyDbCreatedKey().to(true)); } /** @@ -304,6 +279,10 @@ public class ModelDbController { */ private boolean migrateGridIfNeeded() { createDbIfNotExists(); + if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) { + // If we have already create a new DB, ignore migration + return false; + } InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext); if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) { return true; @@ -374,7 +353,7 @@ public class ModelDbController { } private void clearFlagEmptyDbCreated() { - LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName())); + LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey()); } /** @@ -388,7 +367,7 @@ public class ModelDbController { public synchronized void loadDefaultFavoritesIfNecessary() { createDbIfNotExists(); - if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()))) { + if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) { Log.d(TAG, "loading default workspace"); LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder(); @@ -507,6 +486,10 @@ public class ModelDbController { mOpenHelper, mContext.getResources(), defaultLayout); } + private ConstantItem getEmptyDbCreatedKey() { + return getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()); + } + /** * Re-composite given key in respect to database. If the current db is * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index f2afaebc57..a6b4d591b3 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -16,13 +16,12 @@ package com.android.launcher3.model; +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; -import android.net.Uri; import android.text.TextUtils; import android.util.Log; @@ -32,9 +31,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings.Favorites; -import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.celllayout.CellPosMapper.CellPos; @@ -45,6 +42,7 @@ import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.Executors; import com.android.launcher3.util.ItemInfoMatcher; @@ -80,7 +78,7 @@ public class ModelWriter { private final boolean mVerifyChanges; // Keep track of delete operations that occur when an Undo option is present; we may not commit. - private final List mDeleteRunnables = new ArrayList<>(); + private final List mDeleteRunnables = new ArrayList<>(); private boolean mPreparingToUndo; private final CellPosMapper mCellPosMapper; @@ -217,8 +215,7 @@ public class ModelWriter { item.spanX = spanX; item.spanY = spanY; notifyItemModified(item); - - MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> + new UpdateItemRunnable(item, () -> new ContentWriter(mContext) .put(Favorites.CONTAINER, item.container) .put(Favorites.CELLX, item.cellX) @@ -226,7 +223,8 @@ public class ModelWriter { .put(Favorites.RANK, item.rank) .put(Favorites.SPANX, item.spanX) .put(Favorites.SPANY, item.spanY) - .put(Favorites.SCREEN, item.screenId))); + .put(Favorites.SCREEN, item.screenId)) + .executeOnModelThread(); } /** @@ -234,11 +232,11 @@ public class ModelWriter { */ public void updateItemInDatabase(ItemInfo item) { notifyItemModified(item); - MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> { + new UpdateItemRunnable(item, () -> { ContentWriter writer = new ContentWriter(mContext); item.onAddToDatabase(writer); return writer; - })); + }).executeOnModelThread(); } private void notifyItemModified(ItemInfo item) { @@ -253,13 +251,12 @@ public class ModelWriter { int container, int screenId, int cellX, int cellY) { updateItemInfoProps(item, container, screenId, cellX, cellY); - final ContentResolver cr = mContext.getContentResolver(); - item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE); + item.id = mModel.getModelDbController().generateNewItemId(); notifyOtherCallbacks(c -> c.bindItems(Collections.singletonList(item), false)); ModelVerifier verifier = new ModelVerifier(); final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); - MODEL_EXECUTOR.execute(() -> { + newModelTask(() -> { // Write the item on background thread, as some properties might have been updated in // the background. final ContentWriter writer = new ContentWriter(mContext); @@ -272,7 +269,7 @@ public class ModelWriter { mBgDataModel.addItem(mContext, item, true); verifier.verifyModel(); } - }); + }).executeOnModelThread(); } /** @@ -303,15 +300,13 @@ public class ModelWriter { Collectors.joining(",")) + ". Reason: [" + (TextUtils.isEmpty(reason) ? "unknown" : reason) + "]"); notifyDelete(items); - enqueueDeleteRunnable(() -> { + enqueueDeleteRunnable(newModelTask(() -> { for (ItemInfo item : items) { - final Uri uri = Favorites.getContentUri(item.id); - mContext.getContentResolver().delete(uri, null, null); - + mModel.getModelDbController().delete(TABLE_NAME, itemIdMatch(item.id), null); mBgDataModel.removeItem(mContext, item); verifier.verifyModel(); } - }); + })); } /** @@ -321,7 +316,7 @@ public class ModelWriter { ModelVerifier verifier = new ModelVerifier(); notifyDelete(Collections.singleton(info)); - enqueueDeleteRunnable(() -> { + enqueueDeleteRunnable(newModelTask(() -> { mModel.getModelDbController().delete(Favorites.TABLE_NAME, Favorites.CONTAINER + "=" + info.id, null); mBgDataModel.removeItem(mContext, info.contents); @@ -331,7 +326,7 @@ public class ModelWriter { Favorites._ID + "=" + info.id, null); mBgDataModel.removeItem(mContext, info); verifier.verifyModel(); - }); + })); } /** @@ -343,7 +338,7 @@ public class ModelWriter { if (holder != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) { // Deleting an app widget ID is a void call but writes to disk before returning // to the caller... - enqueueDeleteRunnable(() -> holder.deleteAppWidgetId(info.appWidgetId)); + enqueueDeleteRunnable(newModelTask(() -> holder.deleteAppWidgetId(info.appWidgetId))); } deleteItemFromDatabase(info, reason); } @@ -373,19 +368,17 @@ public class ModelWriter { * {@link #commitDelete()} is called (or abandoned if {@link #abortDelete} is called). * Otherwise, we run the Runnable immediately. */ - private void enqueueDeleteRunnable(Runnable r) { + private void enqueueDeleteRunnable(ModelTask r) { if (mPreparingToUndo) { mDeleteRunnables.add(r); } else { - MODEL_EXECUTOR.execute(r); + r.executeOnModelThread(); } } public void commitDelete() { mPreparingToUndo = false; - for (Runnable runnable : mDeleteRunnables) { - MODEL_EXECUTOR.execute(runnable); - } + mDeleteRunnables.forEach(ModelTask::executeOnModelThread); mDeleteRunnables.clear(); } @@ -426,10 +419,9 @@ public class ModelWriter { } @Override - public void run() { - Uri uri = Favorites.getContentUri(mItemId); - mContext.getContentResolver().update(uri, mWriter.get().getValues(mContext), - null, null); + public void runImpl() { + mModel.getModelDbController().update( + TABLE_NAME, mWriter.get().getValues(mContext), itemIdMatch(mItemId), null); updateItemArrays(mItem, mItemId); } } @@ -444,27 +436,24 @@ public class ModelWriter { } @Override - public void run() { - ArrayList ops = new ArrayList<>(); - int count = mItems.size(); - for (int i = 0; i < count; i++) { - ItemInfo item = mItems.get(i); - final int itemId = item.id; - final Uri uri = Favorites.getContentUri(itemId); - ContentValues values = mValues.get(i); - - ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); - updateItemArrays(item, itemId); - } - try { - mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops); + public void runImpl() { + try (SQLiteTransaction t = mModel.getModelDbController().newTransaction()) { + int count = mItems.size(); + for (int i = 0; i < count; i++) { + ItemInfo item = mItems.get(i); + final int itemId = item.id; + mModel.getModelDbController().update( + TABLE_NAME, mValues.get(i), itemIdMatch(itemId), null); + updateItemArrays(item, itemId); + } + t.commit(); } catch (Exception e) { e.printStackTrace(); } } } - private abstract class UpdateItemBaseRunnable implements Runnable { + private abstract class UpdateItemBaseRunnable extends ModelTask { private final StackTraceElement[] mStackTrace; private final ModelVerifier mVerifier = new ModelVerifier(); @@ -515,6 +504,35 @@ public class ModelWriter { } } + private abstract class ModelTask implements Runnable { + + private final int mLoadId = mBgDataModel.lastLoadId; + + @Override + public final void run() { + if (mLoadId != mModel.getLastLoadId()) { + Log.d(TAG, "Model changed before the task could execute"); + return; + } + runImpl(); + } + + public final void executeOnModelThread() { + MODEL_EXECUTOR.execute(this); + } + + public abstract void runImpl(); + } + + private ModelTask newModelTask(Runnable r) { + return new ModelTask() { + @Override + public void runImpl() { + r.run(); + } + }; + } + /** * Utility class to verify model updates are propagated properly to the callback. */ diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java index 93fc6a539f..1fc8a03a83 100644 --- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java +++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java @@ -82,9 +82,7 @@ public class WorkspaceItemSpaceFinder { if (!found) { // Still no position found. Add a new screen to the end. - screenId = LauncherSettings.Settings.call(app.getContext().getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + screenId = app.getModel().getModelDbController().getNewScreenId(); // Save the screen id for binding in the workspace workspaceScreens.add(screenId); diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java index c718dcc877..1f908eb718 100644 --- a/src/com/android/launcher3/provider/LauncherDbUtils.java +++ b/src/com/android/launcher3/provider/LauncherDbUtils.java @@ -28,7 +28,6 @@ import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Icon; -import android.os.Binder; import android.os.PersistableBundle; import android.os.Process; import android.os.UserManager; @@ -50,6 +49,13 @@ import com.android.launcher3.util.IntSet; */ public class LauncherDbUtils { + /** + * Returns a string which can be used as a where clause for DB query to match the given itemId + */ + public static String itemIdMatch(int itemId) { + return "_id=" + itemId; + } + public static IntArray queryIntArray(boolean distinct, SQLiteDatabase db, String tableName, String columnName, String selection, String groupBy, String orderBy) { IntArray out = new IntArray(); @@ -166,7 +172,7 @@ public class LauncherDbUtils { /** * Utility class to simplify managing sqlite transactions */ - public static class SQLiteTransaction extends Binder implements AutoCloseable { + public static class SQLiteTransaction implements AutoCloseable { private final SQLiteDatabase mDb; public SQLiteTransaction(SQLiteDatabase db) { diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java index 80cc5aa90c..6f6a4439ba 100644 --- a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java +++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java @@ -15,24 +15,28 @@ */ package com.android.launcher3.celllayout; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.TestUtil.runOnExecutorSync; -import android.content.ContentValues; import android.content.Context; +import androidx.test.uiautomator.UiDevice; + import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ModelDbController; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; +import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.ContentWriter; import java.util.ArrayList; -import java.util.List; import java.util.function.Supplier; public class FavoriteItemsTransaction { @@ -67,15 +71,16 @@ public class FavoriteItemsTransaction { controller.clearEmptyDbFlag(); // Add new data - List values = new ArrayList<>(); - int count = mItemsToSubmit.size(); - for (int i = 0; i < count; i++) { - ContentWriter writer = new ContentWriter(mContext); - mItemsToSubmit.get(i).get().onAddToDatabase(writer); - writer.put(LauncherSettings.Favorites._ID, i); - values.add(writer.getValues(mContext)); + try (SQLiteTransaction transaction = controller.newTransaction()) { + int count = mItemsToSubmit.size(); + for (int i = 0; i < count; i++) { + ContentWriter writer = new ContentWriter(mContext); + mItemsToSubmit.get(i).get().onAddToDatabase(writer); + writer.put(LauncherSettings.Favorites._ID, i); + controller.insert(TABLE_NAME, writer.getValues(mContext)); + } + transaction.commit(); } - controller.bulkInsert(TABLE_NAME, values.toArray(new ContentValues[0])); }); // Reload model @@ -91,4 +96,15 @@ public class FavoriteItemsTransaction { runOnExecutorSync(MAIN_EXECUTOR, () -> { }); runOnExecutorSync(MAIN_EXECUTOR, () -> model.removeCallbacks(mockCb)); } + + /** + * Commits the transaction and waits for home load + */ + public void commitAndLoadHome(LauncherInstrumentation inst) { + commit(); + + // Launch the home activity + UiDevice.getInstance(getInstrumentation()).pressHome(); + inst.waitForLauncherInitialized(); + } } diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index b07a402bcf..7ec78bb2c9 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -61,7 +61,6 @@ public class ReorderWidgets extends AbstractLauncherUiTest { public void setup() throws Throwable { mWorkspaceBuilder = new TestWorkspaceBuilder(mTargetContext); TaplTestsLauncher3.initialize(this); - clearHomescreen(); } /** @@ -111,10 +110,10 @@ public class ReorderWidgets extends AbstractLauncherUiTest { new FavoriteItemsTransaction(mTargetContext); transaction = buildWorkspaceFromBoards(testCase.mStart, transaction); transaction.commit(); + mLauncher.waitForLauncherInitialized(); // resetLoaderState triggers the launcher to start loading the workspace which allows // waitForLauncherCondition to wait for that condition, otherwise the condition would // always be true and it wouldn't wait for the changes to be applied. - resetLoaderState(); waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.getCellX(), mainWidgetCellPos.getCellY()); diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java index cde733ed2b..8d06e33d83 100644 --- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java +++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java @@ -78,12 +78,6 @@ public class TestWorkspaceBuilder { return transaction; } - private int getID() { - return LauncherSettings.Settings.call( - mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); - } - private AppInfo getApp() { return new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser, AppInfo.makeLaunchIntent(APP_COMPONENT_NAME)); @@ -127,7 +121,6 @@ public class TestWorkspaceBuilder { return () -> { LauncherAppWidgetProviderInfo info = findWidgetProvider(false); LauncherAppWidgetInfo item = createWidgetInfo(info, getApplicationContext(), true); - item.id = getID(); item.cellX = widgetRect.getCellX(); item.cellY = widgetRect.getCellY(); item.spanX = widgetRect.getSpanX(); @@ -139,7 +132,6 @@ public class TestWorkspaceBuilder { private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint, int screenId) { WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); - item.id = getID(); item.screenId = screenId; item.cellX = iconPoint.getCoord().x; item.cellY = iconPoint.getCoord().y; @@ -150,7 +142,6 @@ public class TestWorkspaceBuilder { private ItemInfo getHotseatValues(int x) { WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); - item.id = getID(); item.cellX = x; item.cellY = 0; item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 4506fa36a2..98c4c61f43 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -19,7 +19,6 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.TestUtil.getOnUiThread; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -40,6 +39,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.system.OsConstants; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -49,11 +49,8 @@ import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.tapl.HomeAllApps; import com.android.launcher3.tapl.HomeAppIcon; @@ -66,7 +63,6 @@ import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.TestUtil; import com.android.launcher3.util.Wait; -import com.android.launcher3.util.WidgetUtils; import com.android.launcher3.util.rule.FailureWatcher; import com.android.launcher3.util.rule.LauncherActivityRule; import com.android.launcher3.util.rule.SamplerRule; @@ -83,8 +79,10 @@ import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import java.io.IOException; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -298,37 +296,19 @@ public abstract class AbstractLauncherUiTest { } /** - * Removes all icons from homescreen and hotseat. + * Runs the callback on the UI thread and returns the result. */ - public void clearHomescreen() { - LauncherSettings.Settings.call(mTargetContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mTargetContext.getContentResolver(), - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); - resetLoaderState(); - } - - protected void resetLoaderState() { + protected T getOnUiThread(final Callable callback) { try { - mMainThreadExecutor.execute( - () -> LauncherAppState.getInstance( - mTargetContext).getModel().forceReload()); - } catch (Throwable t) { - throw new IllegalArgumentException(t); + return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT, + TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e); + Process.sendSignal(Process.myPid(), OsConstants.SIGABRT); + throw new RuntimeException(e); + } catch (Throwable e) { + throw new RuntimeException(e); } - mLauncher.waitForLauncherInitialized(); - } - - /** - * Adds {@param item} on the homescreen on the 0th screen - */ - public void addItemToScreen(ItemInfo item) { - WidgetUtils.addItemToScreen(item, mTargetContext); - resetLoaderState(); - - // Launch the home activity - mDevice.pressHome(); - mLauncher.waitForLauncherInitialized(); } protected T getFromLauncher(Function f) { diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index fddbc37697..82bf644791 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -30,6 +30,7 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.testcomponent.WidgetConfigActivity; @@ -85,8 +86,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { * @param acceptConfig accept the config activity */ private void runTest(boolean acceptConfig) throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); // Drag widget to homescreen WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor(); diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 5cdc886a41..b869c9f1d0 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -23,9 +23,10 @@ import static org.junit.Assert.assertTrue; import android.platform.test.annotations.IwTest; import android.platform.test.annotations.PlatinumTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.WidgetResizeFrame; @@ -55,8 +56,7 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testDragIcon() throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); @@ -93,8 +93,8 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testDragCustomShortcut() throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); + mLauncher.getWorkspace().openAllWidgets() .getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity") .dragToWorkspace(false, true); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 99d22016a8..7db31618b1 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -15,9 +15,13 @@ */ package com.android.launcher3.ui.widget; -import static androidx.test.InstrumentationRegistry.getTargetContext; - -import static com.android.launcher3.util.TestUtil.getOnUiThread; +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; +import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; +import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; +import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; +import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; import static org.junit.Assert.assertEquals; @@ -27,7 +31,6 @@ import static org.junit.Assert.assertTrue; import android.appwidget.AppWidgetManager; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; @@ -35,11 +38,14 @@ import android.database.Cursor; import android.os.Bundle; import android.widget.RemoteViews; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.tapl.Widget; @@ -58,6 +64,7 @@ import org.junit.runner.RunWith; import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; /** * Tests for bind widget flow. @@ -71,24 +78,18 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); - private ContentResolver mResolver; - // Objects created during test, which should be cleaned up in the end. private Cursor mCursor; // App install session id. private int mSessionId = -1; + private LauncherModel mModel; + @Override @Before public void setUp() throws Exception { super.setUp(); - - mResolver = mTargetContext.getContentResolver(); - - // Clear all existing data - LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mResolver, - LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); + mModel = LauncherAppState.getInstance(mTargetContext).getModel(); } @After @@ -104,34 +105,24 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Test public void testBindNormalWidget_withConfig() { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(true); - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true); - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(true, true, i -> { }); verifyWidgetPresent(info); } @Test public void testBindNormalWidget_withoutConfig() { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(false); - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true); - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, true, i -> { }); verifyWidgetPresent(info); } @Test public void testUnboundWidget_removed() { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(false); - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false); - item.appWidgetId = -33; - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false, + item -> item.appWidgetId = -33); final Workspace workspace = mLauncher.getWorkspace(); // Item deleted from db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(0, mCursor.getCount()); // The view does not exist @@ -141,36 +132,26 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Test public void testPendingWidget_autoRestored() { // A non-restored widget with no config screen gets restored automatically. - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(false); - // Do not bind the widget - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false, + item -> item.restoreStatus = FLAG_ID_NOT_VALID); verifyWidgetPresent(info); } @Test public void testPendingWidget_withConfigScreen() { // A non-restored widget with config screen get bound and shows a 'Click to setup' UI. - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(true); - // Do not bind the widget - LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; - - addItemToScreen(item); + LauncherAppWidgetProviderInfo info = addWidgetToScreen(true, false, + item -> item.restoreStatus = FLAG_ID_NOT_VALID); verifyPendingWidgetPresent(); - // Item deleted from db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); mCursor.moveToNext(); // Widget has a valid Id now. assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) - & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + & FLAG_ID_NOT_VALID); assertNotNull(AppWidgetManager.getInstance(mTargetContext) .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( LauncherSettings.Favorites.APPWIDGET_ID)))); @@ -186,7 +167,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest { appWidgetManager.updateAppWidgetOptions(appWidgetId, b); appWidgetManager.updateAppWidget(appWidgetId, remoteViews); - // verify changes are reflected waitForLauncherCondition("App widget options did not update", l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean( @@ -198,17 +178,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest { @Test public void testPendingWidget_notRestored_removed() { - LauncherAppWidgetInfo item = getInvalidWidgetInfo(); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID - | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - - addItemToScreen(item); + addPendingItemToScreen(getInvalidWidgetInfo(), FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY); assertTrue("Pending widget exists", mLauncher.getWorkspace().tryGetPendingWidget(0) == null); // Item deleted from db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(0, mCursor.getCount()); } @@ -216,32 +191,25 @@ public class BindWidgetTest extends AbstractLauncherUiTest { public void testPendingWidget_notRestored_brokenInstall() { // A widget which is was being installed once, even if its not being // installed at the moment is not removed. - LauncherAppWidgetInfo item = getInvalidWidgetInfo(); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID - | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED - | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - - addItemToScreen(item); + addPendingItemToScreen(getInvalidWidgetInfo(), + FLAG_ID_NOT_VALID | FLAG_RESTORE_STARTED | FLAG_PROVIDER_NOT_READY); verifyPendingWidgetPresent(); // Verify item still exists in db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(1, mCursor.getCount()); // Widget still has an invalid id. mCursor.moveToNext(); - assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID, + assertEquals(FLAG_ID_NOT_VALID, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) - & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + & FLAG_ID_NOT_VALID); } @Test public void testPendingWidget_notRestored_activeInstall() throws Exception { // A widget which is being installed is not removed LauncherAppWidgetInfo item = getInvalidWidgetInfo(); - item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID - | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; // Create an active installer session SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); @@ -249,19 +217,18 @@ public class BindWidgetTest extends AbstractLauncherUiTest { PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller(); mSessionId = installer.createSession(params); - addItemToScreen(item); + addPendingItemToScreen(item, FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY); verifyPendingWidgetPresent(); // Verify item still exists in db - mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), - null, null, null, null, null); + mCursor = queryItem(); assertEquals(1, mCursor.getCount()); // Widget still has an invalid id. mCursor.moveToNext(); - assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID, + assertEquals(FLAG_ID_NOT_VALID, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) - & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + & FLAG_ID_NOT_VALID); } private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) { @@ -276,6 +243,28 @@ public class BindWidgetTest extends AbstractLauncherUiTest { widget != null); } + private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) { + item.restoreStatus = restoreStatus; + item.screenId = FIRST_SCREEN_ID; + new FavoriteItemsTransaction(mTargetContext) + .addItem(() -> item) + .commitAndLoadHome(mLauncher); + } + + private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen, + boolean bindWidget, Consumer itemOverride) { + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen); + new FavoriteItemsTransaction(mTargetContext) + .addItem(() -> { + LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget); + item.screenId = FIRST_SCREEN_ID; + itemOverride.accept(item); + return item; + }) + .commitAndLoadHome(mLauncher); + return info; + } + /** * Returns a LauncherAppWidgetInfo with package name which is not present on the device */ @@ -313,4 +302,14 @@ public class BindWidgetTest extends AbstractLauncherUiTest { item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; return item; } + + private Cursor queryItem() { + try { + return MODEL_EXECUTOR.submit(() -> + mModel.getModelDbController().query( + TABLE_NAME, null, itemIdMatch(0), null, null)).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index bf9eb5acd6..384fa468ef 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -134,8 +135,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, Intent... commandIntents) throws Throwable { - clearHomescreen(); - mDevice.pressHome(); + new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher); // Open Pin item activity BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver( diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 9dc04a14f2..4580082b96 100644 --- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -53,7 +53,6 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.ModelUpdateTask; import com.android.launcher3.LauncherPrefs; -import com.android.launcher3.LauncherProvider; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; @@ -241,12 +240,6 @@ public class LauncherModelHelper { mPm = spy(getBaseContext().getPackageManager()); mDbDir = new File(getCacheDir(), UUID.randomUUID().toString()); - setupProvider(LauncherProvider.AUTHORITY, new LauncherProvider() { - @Override - public boolean onCreate() { - return true; - } - }); } @Override diff --git a/tests/src/com/android/launcher3/util/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java index b0df0558dd..027a31aeb2 100644 --- a/tests/src/com/android/launcher3/util/WidgetUtils.java +++ b/tests/src/com/android/launcher3/util/WidgetUtils.java @@ -18,18 +18,14 @@ package com.android.launcher3.util; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; - import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.os.Bundle; import android.os.Process; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherWidgetHolder; @@ -87,33 +83,6 @@ public class WidgetUtils { return item; } - /** - * Adds {@param item} on the homescreen on the 0th screen - */ - public static void addItemToScreen(ItemInfo item, Context targetContext) { - ContentResolver resolver = targetContext.getContentResolver(); - int screenId = FIRST_SCREEN_ID; - // Update the screen id counter for the provider. - LauncherSettings.Settings.call(resolver, - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID); - - if (screenId > FIRST_SCREEN_ID) { - screenId = FIRST_SCREEN_ID; - } - - // Insert the item - ContentWriter writer = new ContentWriter(targetContext); - item.id = LauncherSettings.Settings.call( - resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); - item.screenId = screenId; - item.onAddToDatabase(writer); - writer.put(LauncherSettings.Favorites._ID, item.id); - resolver.insert(LauncherSettings.Favorites.CONTENT_URI, - writer.getValues(targetContext)); - } - - /** * Creates a {@link AppWidgetProviderInfo} for the provided component name */