Add hotseat icons to TestWorkspaceBuilder and submit everything in a batch
Now TestWorkspaceBuilder has the logic to add hotseat icons and
the operations to add items to the Workspace is done in batches
which is faster and more stable. To submit everything in batches
it uses the FavoriteItemsTransaction.
This improves the design because TestWorkspaceBuilder and
HomeScreenImageTest belong to the Test layer and they should
only deal with the logic to run a test and TestWorkspaceBuilder
is the Model layer and holds all the logic to interact with the
Launcher model/data, so this separates the concern of each class.
Also, chagned the name from CellLayoutBoardBuilder to
TestWorkspaceBuilder to make it clearer.
Bug: 243440737
Bug: 235518637
Bug: 242323136
Test: atest HomeScreenImageTest
Test: atest ReorderWidgets
Change-Id: I14eef064fade153b8362537743b061958bb3071d
diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
new file mode 100644
index 0000000..8ce932d
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.ContentWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+public class FavoriteItemsTransaction {
+ private ArrayList<ItemInfo> mItemsToSubmit;
+ private Context mContext;
+ private ContentResolver mResolver;
+ public AbstractLauncherUiTest mTest;
+
+ public FavoriteItemsTransaction(Context context, AbstractLauncherUiTest test) {
+ mItemsToSubmit = new ArrayList<>();
+ mContext = context;
+ mResolver = mContext.getContentResolver();
+ mTest = test;
+ }
+
+ public FavoriteItemsTransaction addItem(ItemInfo itemInfo) {
+ this.mItemsToSubmit.add(itemInfo);
+ return this;
+ }
+
+ public FavoriteItemsTransaction removeLast() {
+ this.mItemsToSubmit.remove(this.mItemsToSubmit.size() - 1);
+ return this;
+ }
+
+ /**
+ * Commits all the ItemInfo into the database of Favorites
+ **/
+ public void commit() throws ExecutionException, InterruptedException {
+ List<ContentValues> values = new ArrayList<>();
+ for (ItemInfo item : this.mItemsToSubmit) {
+ ContentWriter writer = new ContentWriter(mContext);
+ item.onAddToDatabase(writer);
+ writer.put(LauncherSettings.Favorites._ID, item.id);
+ values.add(writer.getValues(mContext));
+ }
+ // Submit the icons to the database in the model thread to prevent race conditions
+ MODEL_EXECUTOR.submit(() -> mResolver.bulkInsert(LauncherSettings.Favorites.CONTENT_URI,
+ values.toArray(new ContentValues[0]))).get();
+ // Reload the state of the Launcher
+ MAIN_EXECUTOR.submit(() -> LauncherAppState.getInstance(
+ mContext).getModel().forceReload()).get();
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 93fbf97..2846cae 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -47,6 +47,7 @@
import org.junit.runner.RunWith;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
@SmallTest
@@ -58,7 +59,7 @@
private static final String TAG = ReorderWidgets.class.getSimpleName();
- TestWorkspaceBuilder mBoardBuilder;
+ TestWorkspaceBuilder mWorkspaceBuilder;
private View getViewAt(int cellX, int cellY) {
return getFromLauncher(l -> l.getWorkspace().getScreenWithId(
@@ -76,7 +77,7 @@
@Before
public void setup() throws Throwable {
- mBoardBuilder = new TestWorkspaceBuilder(this);
+ mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext);
TaplTestsLauncher3.initialize(this);
clearHomescreen();
}
@@ -87,7 +88,7 @@
private boolean validateBoard(CellLayoutBoard board) {
boolean match = true;
Point cellDimensions = getCellDimensions();
- for (CellLayoutBoard.WidgetRect widgetRect: board.getWidgets()) {
+ for (CellLayoutBoard.WidgetRect widgetRect : board.getWidgets()) {
if (widgetRect.shouldIgnore()) {
continue;
}
@@ -108,10 +109,13 @@
return match;
}
- private void runTestCase(ReorderTestCase testCase) {
+ private void runTestCase(ReorderTestCase testCase)
+ throws ExecutionException, InterruptedException {
Point mainWidgetCellPos = testCase.mStart.getMain();
- mBoardBuilder.buildBoard(testCase.mStart);
+ FavoriteItemsTransaction transaction =
+ new FavoriteItemsTransaction(mTargetContext, this);
+ mWorkspaceBuilder.buildFromBoard(testCase.mStart, transaction).commit();
Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x,
mainWidgetCellPos.y);
@@ -132,7 +136,8 @@
*
* @param testCaseMap map containing all the tests per grid size (Point)
*/
- private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName) {
+ private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName)
+ throws ExecutionException, InterruptedException {
Point iconGridDimensions = mLauncher.getWorkspace().getIconGridDimensions();
Log.d(TAG, "Running test " + testName + " for grid " + iconGridDimensions);
Assume.assumeTrue(
@@ -143,26 +148,26 @@
@ScreenRecord // b/242323136
@Test
- public void simpleReorder() {
+ public void simpleReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE,
SimpleReorderCase.class.getSimpleName());
}
@ScreenRecord // b/242323136
@Test
- public void pushTest() {
+ public void pushTest() throws ExecutionException, InterruptedException {
runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName());
}
@ScreenRecord // b/242323136
@Test
- public void fullReorder() {
+ public void fullReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName());
}
@ScreenRecord // b/242323136
@Test
- public void moveOutReorder() {
+ public void moveOutReorder() throws ExecutionException, InterruptedException {
runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE,
MoveOutReorderCase.class.getSimpleName());
}
diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
index 10e399d..16448af 100644
--- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
+++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
@@ -15,9 +15,12 @@
*/
package com.android.launcher3.celllayout;
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
import android.graphics.Rect;
import android.os.Process;
import android.os.UserHandle;
@@ -25,8 +28,10 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.AppInfo;
+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.ui.AbstractLauncherUiTest;
@@ -35,6 +40,7 @@
public class TestWorkspaceBuilder {
+ private static final String TAG = "CellLayoutBoardBuilder";
private static final ComponentName APP_COMPONENT_NAME = new ComponentName(
"com.google.android.calculator", "com.android.calculator2.Calculator");
@@ -42,69 +48,118 @@
private UserHandle mMyUser;
- public TestWorkspaceBuilder(AbstractLauncherUiTest test) {
+ private Context mContext;
+ private ContentResolver mResolver;
+
+ public TestWorkspaceBuilder(AbstractLauncherUiTest test, Context context) {
mTest = test;
mMyUser = Process.myUserHandle();
+ mContext = context;
+ mResolver = mContext.getContentResolver();
}
- private static final String TAG = "CellLayoutBoardBuilder";
-
/**
* Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
*/
- private void fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect) {
+ private FavoriteItemsTransaction fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect,
+ FavoriteItemsTransaction transaction) {
int initX = widgetRect.getCellX();
int initY = widgetRect.getCellY();
for (int x = initX; x < initX + widgetRect.getSpanX(); x++) {
for (int y = initY; y < initY + widgetRect.getSpanY(); y++) {
try {
// this widgets are filling, we don't care if we can't place them
- addWidgetInCell(
+ ItemInfo item = createWidgetInCell(
new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE,
new Rect(x, y, x, y))
);
+ transaction.addItem(item);
} catch (Exception e) {
Log.d(TAG, "Unable to place filling widget at " + x + "," + y);
}
}
}
+ return transaction;
}
- private void addWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) {
+ 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));
+ }
+
+ private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect,
+ FavoriteItemsTransaction transaction) {
+ if (widgetRect.mType == 'x') {
+ fillWithWidgets(widgetRect, transaction);
+ } else {
+ transaction.addItem(createWidgetInCell(widgetRect));
+ }
+ }
+
+ /**
+ * Builds the given board into the transaction
+ */
+ public FavoriteItemsTransaction buildFromBoard(CellLayoutBoard board,
+ FavoriteItemsTransaction transaction) {
+ board.getWidgets().forEach(
+ (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction));
+ board.getIcons().forEach((iconPoint) ->
+ transaction.addItem(createIconInCell(iconPoint))
+ );
+ return transaction;
+ }
+
+ /**
+ * Fills the hotseat row with apps instead of suggestions, for this to work the workspace should
+ * be clean otherwise this doesn't overrides the existing icons.
+ */
+ public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) {
+ int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numShownHotseatIcons;
+ for (int i = 0; i < hotseatCount; i++) {
+ transaction.addItem(getHotseatValues(i));
+ }
+ return transaction;
+ }
+
+ private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false);
LauncherAppWidgetInfo item = createWidgetInfo(info,
ApplicationProvider.getApplicationContext(), true);
-
+ item.id = getID();
item.cellX = widgetRect.getCellX();
item.cellY = widgetRect.getCellY();
item.spanX = widgetRect.getSpanX();
item.spanY = widgetRect.getSpanY();
- mTest.addItemToScreen(item);
+ item.screenId = FIRST_SCREEN_ID;
+ return item;
}
- private void addIconInCell(CellLayoutBoard.IconPoint iconPoint) {
- AppInfo appInfo = new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser,
- AppInfo.makeLaunchIntent(APP_COMPONENT_NAME));
-
- appInfo.cellX = iconPoint.getCoord().x;
- appInfo.cellY = iconPoint.getCoord().y;
- appInfo.minSpanY = appInfo.minSpanX = appInfo.spanX = appInfo.spanY = 1;
- appInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
- appInfo.componentName = APP_COMPONENT_NAME;
-
- mTest.addItemToScreen(new WorkspaceItemInfo(appInfo));
+ private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint) {
+ WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
+ item.id = getID();
+ item.screenId = FIRST_SCREEN_ID;
+ item.cellX = iconPoint.getCoord().x;
+ item.cellY = iconPoint.getCoord().y;
+ item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1;
+ item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ return item;
}
- private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect) {
- if (widgetRect.mType == 'x') {
- fillWithWidgets(widgetRect);
- } else {
- addWidgetInCell(widgetRect);
- }
- }
-
- public void buildBoard(CellLayoutBoard board) {
- board.getWidgets().forEach(this::addCorrespondingWidgetRect);
- board.getIcons().forEach(this::addIconInCell);
+ 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;
+ item.rank = x;
+ item.screenId = x;
+ item.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ return item;
}
}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
index 94e55cf..a98882c 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
@@ -31,13 +31,13 @@
+ "xxxxx\n"
+ "222mm\n"
+ "222mm\n"
- + "ad111\n"
- + "bc111";
+ + "ii111\n"
+ + "ii111";
private static final Point MOVE_TO_5x5 = new Point(0, 4);
private static final String END_BOARD_STR_5x5 = ""
+ "xxxxx\n"
- + "222ad\n"
- + "222bc\n"
+ + "222ii\n"
+ + "222ii\n"
+ "mm111\n"
+ "mm111";
private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
@@ -50,13 +50,13 @@
+ "xxxxxx\n"
+ "2222mm\n"
+ "2222mm\n"
- + "ad1111\n"
- + "bc1111";
+ + "ii1111\n"
+ + "ii1111";
private static final Point MOVE_TO_6x5 = new Point(0, 4);
private static final String END_BOARD_STR_6x5 = ""
+ "xxxxxx\n"
- + "2222ad\n"
- + "2222bc\n"
+ + "2222ii\n"
+ + "2222ii\n"
+ "mm1111\n"
+ "mm1111";
private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
@@ -68,13 +68,13 @@
private static final String START_BOARD_STR_4x4 = ""
+ "xxxx\n"
+ "22mm\n"
- + "admm\n"
- + "bc11";
+ + "iimm\n"
+ + "ii11";
private static final Point MOVE_TO_4x4 = new Point(0, 3);
private static final String END_BOARD_STR_4x4 = ""
+ "xxxx\n"
- + "22ad\n"
- + "mmbc\n"
+ + "22ii\n"
+ + "mmii\n"
+ "mm11";
private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,