From 5434c9d370d1491ac472f11df2307e3e8109f2eb Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Mon, 13 Jun 2022 14:38:43 -0700 Subject: [PATCH] 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 --- .../celllayout/FavoriteItemsTransaction.java | 76 ++++++++++++ .../launcher3/celllayout/ReorderWidgets.java | 25 ++-- .../celllayout/TestWorkspaceBuilder.java | 115 +++++++++++++----- .../celllayout/testcases/FullReorderCase.java | 24 ++-- 4 files changed, 188 insertions(+), 52 deletions(-) create mode 100644 tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java 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 0000000000..8ce932d281 --- /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 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 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 93fbf97e99..2846cae1e8 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.Test; import org.junit.runner.RunWith; import java.util.Map; +import java.util.concurrent.ExecutionException; @SmallTest @@ -58,7 +59,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { 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 @@ public class ReorderWidgets extends AbstractLauncherUiTest { @Before public void setup() throws Throwable { - mBoardBuilder = new TestWorkspaceBuilder(this); + mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext); TaplTestsLauncher3.initialize(this); clearHomescreen(); } @@ -87,7 +88,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { 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 @@ public class ReorderWidgets extends AbstractLauncherUiTest { 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 @@ public class ReorderWidgets extends AbstractLauncherUiTest { * * @param testCaseMap map containing all the tests per grid size (Point) */ - private void runTestCaseMap(Map testCaseMap, String testName) { + private void runTestCaseMap(Map 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 @@ public class ReorderWidgets extends AbstractLauncherUiTest { @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 10e399dfd5..16448afda0 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 android.util.Log; 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 @@ import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; 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 @@ public class TestWorkspaceBuilder { 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 94e55cfbde..a98882cc7f 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 @@ public class FullReorderCase { + "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 @@ public class FullReorderCase { + "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 @@ public class FullReorderCase { 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,