From 5cba47636223dbbdb2e515693008c2c60f37566f Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Mon, 13 Jun 2022 14:38:43 -0700 Subject: [PATCH] Adding test for the Widgets reordering. TAPL design chagnes at https://docs.google.com/document/d/1PdJZZIn-85-UMRFGZuqj-tJgruIWcg31lZnrb34iBTY/edit?resourcekey=0-uAZuiLCDFV9YhOtLB7wQHQ The tests consist of a board representing the widgets on the CellLayout a position to move the main widget (m) to and the resulting board. iiiii iiiii ----- --x-- -xxx- -> -xmx- --m-- ----- ----- ----- Move m to 2,2. Then whe make sure the device corresponds with the resulting board. I had to add the event TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick"); on ItemLongClickListener because the tests where not expecting a long press on a widget after is being place on the workspace. Also, I needed to add the option to drag a widget to a specific point instead of the previous option of only dragging to the workspace. Fix: 231449779 Test: Run the test and make sure they pass. Change-Id: I58183b7ce2ca64c999e21073cce5e0ba6e6f3a9e --- .../touch/ItemLongClickListener.java | 7 +- .../launcher3/celllayout/CellLayoutBoard.java | 152 ++++++++++++++ .../launcher3/celllayout/ReorderWidgets.java | 197 ++++++++++++++++++ .../celllayout/TestBoardAppIcon.java | 44 ++++ .../launcher3/celllayout/TestBoardWidget.java | 48 +++++ .../celllayout/testcases/FullReorderCase.java | 65 ++++++ .../testcases/MoveOutReorderCase.java | 66 ++++++ .../celllayout/testcases/PushReorderCase.java | 76 +++++++ .../celllayout/testcases/ReorderTestCase.java | 46 ++++ .../testcases/SimpleReorderCase.java | 51 +++++ .../tapl/LauncherInstrumentation.java | 7 + .../com/android/launcher3/tapl/Widget.java | 69 +++++- .../com/android/launcher3/tapl/Workspace.java | 31 ++- .../launcher3/tapl/WorkspaceDragSource.java | 41 ++-- 14 files changed, 878 insertions(+), 22 deletions(-) create mode 100644 tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java create mode 100644 tests/src/com/android/launcher3/celllayout/ReorderWidgets.java create mode 100644 tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java create mode 100644 tests/src/com/android/launcher3/celllayout/TestBoardWidget.java create mode 100644 tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java create mode 100644 tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java create mode 100644 tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java create mode 100644 tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java create mode 100644 tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index 6bae745ef8..53cd4167d7 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -38,6 +38,7 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.views.BubbleTextHolder; +import com.android.launcher3.widget.LauncherAppWidgetHostView; /** * Class to handle long-clicks on workspace items and start drag as a result. @@ -51,7 +52,11 @@ public class ItemLongClickListener { ItemLongClickListener::onAllAppsItemLongClick; private static boolean onWorkspaceItemLongClick(View v) { - TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick"); + if (v instanceof LauncherAppWidgetHostView) { + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick"); + } else { + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick"); + } Launcher launcher = Launcher.getLauncher(v.getContext()); if (!canStartDrag(launcher)) return false; if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false; diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java new file mode 100644 index 0000000000..3ca05bce80 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java @@ -0,0 +1,152 @@ +/* + * 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 android.graphics.Point; +import android.graphics.Rect; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + + +public class CellLayoutBoard { + + static final int INFINITE = 99999; + + char[][] mBoard = new char[30][30]; + + List mWidgetsRects = new ArrayList<>(); + Map mWidgetsMap = new HashMap<>(); + + List mIconPoints = new ArrayList<>(); + Map mIconsMap = new HashMap<>(); + + Point mMain = new Point(); + + CellLayoutBoard() { + for (int x = 0; x < mBoard.length; x++) { + for (int y = 0; y < mBoard[0].length; y++) { + mBoard[x][y] = '-'; + } + } + } + + public List getWidgets() { + return mWidgetsRects; + } + + public Point getMain() { + return mMain; + } + + public TestBoardWidget getWidgetRect(char c) { + return mWidgetsMap.get(c); + } + + public static TestBoardWidget getWidgetRect(int x, int y, Set used, char[][] board) { + char type = board[x][y]; + Queue search = new ArrayDeque(); + Point current = new Point(x, y); + search.add(current); + used.add(current); + List neighbors = new ArrayList<>(List.of( + new Point(-1, 0), + new Point(0, -1), + new Point(1, 0), + new Point(0, 1)) + ); + Rect widgetRect = new Rect(INFINITE, -INFINITE, -INFINITE, INFINITE); + while (!search.isEmpty()) { + current = search.poll(); + widgetRect.top = Math.max(widgetRect.top, current.y); + widgetRect.right = Math.max(widgetRect.right, current.x); + widgetRect.bottom = Math.min(widgetRect.bottom, current.y); + widgetRect.left = Math.min(widgetRect.left, current.x); + for (Point p : neighbors) { + Point next = new Point(current.x + p.x, current.y + p.y); + if (next.x < 0 || next.x >= board.length) continue; + if (next.y < 0 || next.y >= board[0].length) continue; + if (board[next.x][next.y] == type && !used.contains(next)) { + used.add(next); + search.add(next); + } + } + } + return new TestBoardWidget(type, widgetRect); + } + + public static boolean isWidget(char type) { + return type != 'i' && type != '-'; + } + + public static boolean isIcon(char type) { + return type == 'i'; + } + + private static List getRects(char[][] board) { + Set used = new HashSet<>(); + List widgetsRects = new ArrayList<>(); + for (int x = 0; x < board.length; x++) { + for (int y = 0; y < board[0].length; y++) { + if (!used.contains(new Point(x, y)) && isWidget(board[x][y])) { + widgetsRects.add(getWidgetRect(x, y, used, board)); + } + } + } + return widgetsRects; + } + + private static List getIconPoints(char[][] board) { + List iconPoints = new ArrayList<>(); + for (int x = 0; x < board.length; x++) { + for (int y = 0; y < board[0].length; y++) { + if (isIcon(board[x][y])) { + iconPoints.add(new TestBoardAppIcon(new Point(x, y), board[x][y])); + } + } + } + return iconPoints; + } + + public static CellLayoutBoard boardFromString(String boardStr) { + String[] lines = boardStr.split("\n"); + CellLayoutBoard board = new CellLayoutBoard(); + + for (int y = 0; y < lines.length; y++) { + String line = lines[y]; + for (int x = 0; x < line.length(); x++) { + char c = line.charAt(x); + if (c == 'm') { + board.mMain = new Point(x, y); + } + if (c != '-') { + board.mBoard[x][y] = line.charAt(x); + } + } + } + board.mWidgetsRects = getRects(board.mBoard); + board.mWidgetsRects.forEach( + widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect)); + board.mIconPoints = getIconPoints(board.mBoard); + return board; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java new file mode 100644 index 0000000000..ed0b71d12e --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -0,0 +1,197 @@ +/* + * 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.WidgetUtils.createWidgetInfo; + +import static org.junit.Assert.assertTrue; + +import android.graphics.Point; +import android.graphics.Rect; +import android.util.Log; +import android.view.View; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.CellLayout; +import com.android.launcher3.celllayout.testcases.FullReorderCase; +import com.android.launcher3.celllayout.testcases.MoveOutReorderCase; +import com.android.launcher3.celllayout.testcases.PushReorderCase; +import com.android.launcher3.celllayout.testcases.ReorderTestCase; +import com.android.launcher3.celllayout.testcases.SimpleReorderCase; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; + + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ReorderWidgets extends AbstractLauncherUiTest { + + @Rule + public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); + + private static final String TAG = ReorderWidgets.class.getSimpleName(); + + private View getViewAt(int cellX, int cellY) { + return getFromLauncher(l -> l.getWorkspace().getScreenWithId( + l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY)); + } + + private Point getCellDimensions() { + return getFromLauncher(l -> { + CellLayout cellLayout = l.getWorkspace().getScreenWithId( + l.getWorkspace().getScreenIdForPageIndex(0)); + return new Point(cellLayout.getWidth() / cellLayout.getCountX(), + cellLayout.getHeight() / cellLayout.getCountY()); + }); + } + + @Before + public void setup() throws Throwable { + TaplTestsLauncher3.initialize(this); + clearHomescreen(); + } + + /** + * Validate if the given board represent the current CellLayout + **/ + private boolean validateBoard(CellLayoutBoard board) { + boolean match = true; + Point cellDimensions = getCellDimensions(); + for (TestBoardWidget widgetRect: board.getWidgets()) { + if (widgetRect.shouldIgnore()) { + continue; + } + View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY()); + match &= widgetRect.getSpanX() + == Math.round(widget.getWidth() / (float) cellDimensions.x); + match &= widgetRect.getSpanY() + == Math.round(widget.getHeight() / (float) cellDimensions.y); + if (!match) return match; + } + return match; + } + + /** + * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. + */ + private void fillWithWidgets(TestBoardWidget widgetRect) { + int initX = widgetRect.getCellX(); + int initY = widgetRect.getCellY(); + for (int x = 0; x < widgetRect.getSpanX(); x++) { + for (int y = 0; y < widgetRect.getSpanY(); y++) { + int auxX = initX + x; + int auxY = initY + y; + try { + // this widgets are filling, we don't care if we can't place them + addWidgetInCell( + new TestBoardWidget('x', + new Rect(auxX, auxY, auxX, auxY)) + ); + } catch (Exception e) { + Log.d(TAG, "Unable to place filling widget at " + auxX + "," + auxY); + } + } + } + } + + private void addWidgetInCell(TestBoardWidget widgetRect) { + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); + LauncherAppWidgetInfo item = createWidgetInfo(info, + ApplicationProvider.getApplicationContext(), true); + item.cellX = widgetRect.getCellX(); + item.cellY = widgetRect.getCellY(); + + item.spanX = widgetRect.getSpanX(); + item.spanY = widgetRect.getSpanY(); + addItemToScreen(item); + } + + private void addCorrespondingWidgetRect(TestBoardWidget widgetRect) { + if (widgetRect.mType == 'x') { + fillWithWidgets(widgetRect); + } else { + addWidgetInCell(widgetRect); + } + } + + private void runTestCase(ReorderTestCase testCase) { + Point mainWidgetCellPos = testCase.mStart.getMain(); + + testCase.mStart.getWidgets().forEach(this::addCorrespondingWidgetRect); + + mLauncher.getWorkspace() + .getWidgetAtCell(mainWidgetCellPos.x, mainWidgetCellPos.y) + .dragWidgetToWorkspace(testCase.moveMainTo.x, testCase.moveMainTo.y) + .dismiss(); // dismiss resize frame + + boolean isValid = false; + for (CellLayoutBoard board : testCase.mEnd) { + isValid |= validateBoard(board); + } + assertTrue("None of the valid boards match with the current state", isValid); + } + + /** + * Run only the test define for the current grid size if such test exist + * + * @param testCaseMap map containing all the tests per grid size (Point) + */ + private void runTestCaseMap(Map testCaseMap, String testName) { + Point iconGridDimensions = mLauncher.getWorkspace().getIconGridDimensions(); + Log.d(TAG, "Running test " + testName + " for grid " + iconGridDimensions); + Assume.assumeTrue( + "The test " + testName + " doesn't support " + iconGridDimensions + " grid layout", + testCaseMap.containsKey(iconGridDimensions)); + runTestCase(testCaseMap.get(iconGridDimensions)); + } + + @Test + public void simpleReorder() { + runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE, + SimpleReorderCase.class.getSimpleName()); + } + + @Test + public void pushTest() { + runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName()); + } + + @Test + public void fullReorder() { + runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName()); + } + + @Test + public void moveOutReorder() { + runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE, + MoveOutReorderCase.class.getSimpleName()); + } +} diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java b/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java new file mode 100644 index 0000000000..04604d7735 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java @@ -0,0 +1,44 @@ +/* + * 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 android.graphics.Point; + +public class TestBoardAppIcon { + public Point coord; + public char mType; + + public TestBoardAppIcon(Point coord, char type) { + this.coord = coord; + mType = type; + } + + public char getType() { + return mType; + } + + public void setType(char type) { + mType = type; + } + + public Point getCoord() { + return coord; + } + + public void setCoord(Point coord) { + this.coord = coord; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java b/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java new file mode 100644 index 0000000000..7f9aa9516c --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java @@ -0,0 +1,48 @@ +/* + * 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 android.graphics.Rect; + +public class TestBoardWidget { + public char mType; + public Rect mBounds; + + TestBoardWidget(char type, Rect bounds) { + this.mType = type; + this.mBounds = bounds; + } + + int getSpanX() { + return mBounds.right - mBounds.left + 1; + } + + int getSpanY() { + return mBounds.top - mBounds.bottom + 1; + } + + int getCellX() { + return mBounds.left; + } + + int getCellY() { + return mBounds.bottom; + } + + boolean shouldIgnore() { + return this.mType == 'x'; + } +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java new file mode 100644 index 0000000000..ff03a50fbc --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java @@ -0,0 +1,65 @@ +/* + * 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.testcases; + +import android.graphics.Point; + +import java.util.Map; + +public class FullReorderCase { + private static final String START_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "222mm\n" + + "222mm\n" + + "ad111\n" + + "bc111"; + + 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" + + "mm111\n" + + "mm111"; + + private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5, + MOVE_TO_5x5, + END_BOARD_STR_5x5); + + private static final String START_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "2222mm\n" + + "2222mm\n" + + "ad1111\n" + + "bc1111"; + + 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" + + "mm1111\n" + + "mm1111"; + + private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5, + MOVE_TO_6x5, + END_BOARD_STR_6x5); + + public static final Map TEST_BY_GRID_SIZE = + Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5); +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java new file mode 100644 index 0000000000..32bf05a52c --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java @@ -0,0 +1,66 @@ +/* + * 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.testcases; + +import android.graphics.Point; + +import java.util.Map; + +public class MoveOutReorderCase { + private static final String START_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "34-m-\n" + + "35111\n" + + "32111\n" + + "32111"; + + private static final Point MOVE_TO_5x5 = new Point(1, 2); + + private static final String END_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "345--\n" + + "3m111\n" + + "32111\n" + + "32111"; + + private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5, + MOVE_TO_5x5, + END_BOARD_STR_5x5); + + + private static final String START_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "34-m--\n" + + "351111\n" + + "321111\n" + + "321111"; + + private static final Point MOVE_TO_6x5 = new Point(1, 2); + + private static final String END_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "345---\n" + + "3m1111\n" + + "321111\n" + + "321111"; + + private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5, + MOVE_TO_6x5, + END_BOARD_STR_6x5); + + public static final Map TEST_BY_GRID_SIZE = + Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5); +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java new file mode 100644 index 0000000000..b386946176 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.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.testcases; + +import android.graphics.Point; + +import com.android.launcher3.celllayout.CellLayoutBoard; + +import java.util.Map; + +public class PushReorderCase { + private static final String START_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "222m-\n" + + "--111\n" + + "--333\n" + + "-----"; + private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString( + START_BOARD_STR_5x5); + + private static final Point MOVE_TO_5x5 = new Point(2, 1); + + private static final String END_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "--m--\n" + + "222--\n" + + "--111\n" + + "--333"; + private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString( + END_BOARD_STR_5x5); + + private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5, + MOVE_TO_5x5, + END_BOARD_5x5); + + + private static final String START_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "2222m-\n" + + "--111-\n" + + "--333-\n" + + "------"; + private static final CellLayoutBoard START_BOARD_6x5 = CellLayoutBoard.boardFromString( + START_BOARD_STR_6x5); + + private static final Point MOVE_TO_6x5 = new Point(2, 1); + + private static final String END_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "--m---\n" + + "2222--\n" + + "--111-\n" + + "--333-"; + private static final CellLayoutBoard END_BOARD_6x5 = CellLayoutBoard.boardFromString( + END_BOARD_STR_6x5); + + private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_6x5, + MOVE_TO_6x5, + END_BOARD_6x5); + + public static final Map TEST_BY_GRID_SIZE = + Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5); +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java new file mode 100644 index 0000000000..0a28668d96 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java @@ -0,0 +1,46 @@ +/* + * 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.testcases; + +import android.graphics.Point; + +import com.android.launcher3.celllayout.CellLayoutBoard; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ReorderTestCase { + public CellLayoutBoard mStart; + public Point moveMainTo; + public List mEnd; + + ReorderTestCase(CellLayoutBoard start, Point moveMainTo, CellLayoutBoard ... end) { + mStart = start; + this.moveMainTo = moveMainTo; + mEnd = Arrays.asList(end); + } + + ReorderTestCase(String start, Point moveMainTo, String ... end) { + mStart = CellLayoutBoard.boardFromString(start); + this.moveMainTo = moveMainTo; + mEnd = Arrays + .asList(end) + .stream() + .map(CellLayoutBoard::boardFromString) + .collect(Collectors.toList()); + } +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java new file mode 100644 index 0000000000..57e1398bd2 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java @@ -0,0 +1,51 @@ +/* + * 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.testcases; + +import android.graphics.Point; + +import com.android.launcher3.celllayout.CellLayoutBoard; + +import java.util.Map; + +public class SimpleReorderCase { + private static final String START_BOARD_STR = "" + + "xxxxx\n" + + "--mm-\n" + + "--mm-\n" + + "-----\n" + + "-----"; + private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString( + START_BOARD_STR); + + private static final Point MOVE_TO_5x5 = new Point(4, 4); + + private static final String END_BOARD_STR_5x5 = "" + + "xxxxx\n" + + "-----\n" + + "-----\n" + + "---mm\n" + + "---mm"; + private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString( + END_BOARD_STR_5x5); + + private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5, + MOVE_TO_5x5, + END_BOARD_5x5); + + public static final Map TEST_BY_GRID_SIZE = + Map.of(new Point(5, 5), TEST_CASE_5x5); +} diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index fa6141ad23..f5b5a6a76f 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1221,6 +1221,13 @@ public final class LauncherInstrumentation { return object; } + @NonNull + List waitForObjectsBySelector(BySelector selector) { + final List objects = mDevice.wait(Until.findObjects(selector), WAIT_TIME_MS); + assertNotNull("Can't find any view in Launcher, selector: " + selector, objects); + return objects; + } + private UiObject2 waitForObjectBySelector(BySelector selector) { final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS); assertNotNull("Can't find a view in Launcher, selector: " + selector, object); diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java index 2346249564..e1a09af050 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widget.java +++ b/tests/tapl/com/android/launcher3/tapl/Widget.java @@ -69,7 +69,24 @@ public final class Widget extends Launchable implements WorkspaceDragSource { */ @NonNull public WidgetResizeFrame dragWidgetToWorkspace() { - return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false); + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { + return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, -1, + -1); + } + } + + /** + * Drags a non-configurable widget from the widgets container to the workspace at cellX and + * cellY and returns the resize frame that is shown after the widget is added. + */ + @NonNull + public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "Dragging widget to workspace cell " + cellX + "," + cellY)) { + return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, + cellX, cellY); + } } /** @@ -79,7 +96,32 @@ public final class Widget extends Launchable implements WorkspaceDragSource { */ @Nullable public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) { - return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig); + // TODO(b/239438337, fransebas) add correct event checking for this case + //try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { + return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig, -1, -1); + //} + } + + /** + * Drags an object to the center of homescreen. + * + * @param startsActivity whether it's expected to start an activity. + * @param isWidgetShortcut whether we drag a widget shortcut + * @param cellX X position in the CellLayout + * @param cellY Y position in the CellLayout + */ + private void dragToWorkspace(boolean startsActivity, boolean isWidgetShortcut, int cellX, + int cellY) { + Launchable launchable = getLaunchable(); + LauncherInstrumentation launcher = launchable.mLauncher; + Workspace.dragIconToWorkspace( + launcher, + launchable, + () -> Workspace.getCellCenter(launchable.mLauncher, cellX, cellY), + startsActivity, + isWidgetShortcut, + launchable::addExpectedEventsForLongClick); + } /** @@ -88,11 +130,28 @@ public final class Widget extends Launchable implements WorkspaceDragSource { * *

If {@code configurable} is true, then either accepts or cancels the configuration based * on {@code acceptsConfig}. + *

If either {@code cellX} or {@code cellY} are negative, then a default location would be + * chosen + * + * @param configurable if the widget has a configuration activity. + * @param acceptsConfig if the widget has a configuration, then if we should accept it or + * cancel it + * @param cellX X position to drop the widget in the workspace + * @param cellY Y position to drop the widget in the workspace + * @return returns the given resize frame of the widget after being dropped, if + * configurable is true and acceptsConfig is false then the widget would not be places and will + * be cancel and it returns null. */ @Nullable - private WidgetResizeFrame dragWidgetToWorkspace( - boolean configurable, boolean acceptsConfig) { - dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false); + private WidgetResizeFrame dragWidgetToWorkspace(boolean configurable, boolean acceptsConfig, + int cellX, int cellY) { + if (cellX == -1 || cellY == -1) { + internalDragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ + false); + } else { + dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false, + cellX, cellY); + } if (configurable) { // Configure the widget. diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 5e5fdecaea..6044b06d00 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -90,7 +90,7 @@ public final class Workspace extends Home { final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1; final int swipeHeight = mLauncher.getTestInfo( - TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT) + TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT) .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); LauncherInstrumentation.log( "switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY @@ -271,7 +271,7 @@ public final class Workspace extends Home { /** * @return map of text -> center of the view. In case of icons with the same name, the one with - * lower x coordinate is selected. + * lower x coordinate is selected. */ public Map getWorkspaceIconsPositions() { final UiObject2 workspace = verifyActiveContainer(); @@ -284,6 +284,7 @@ public final class Workspace extends Home { /* valueMapper= */ UiObject2::getVisibleCenter, /* mergeFunction= */ (p1, p2) -> p1.x < p2.x ? p1 : p2)); } + /* * Get the center point of the delete/uninstall icon in the drop target bar. */ @@ -581,6 +582,32 @@ public final class Workspace extends Home { } } + /** + * @param cellX X position of the widget trying to get. + * @param cellY Y position of the widget trying to get. + * @return returns the Widget in the given position in the Launcher or an Exception if no such + * widget is in that position. + */ + @NonNull + public Widget getWidgetAtCell(int cellX, int cellY) { + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "getting widget at cell position " + cellX + "," + cellY)) { + final List widgets = mLauncher.waitForObjectsBySelector( + By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView")); + Point coordinateInScreen = Workspace.getCellCenter(mLauncher, cellX, cellY); + for (UiObject2 widget : widgets) { + if (widget.getVisibleBounds().contains(coordinateInScreen.x, + coordinateInScreen.y)) { + return new Widget(mLauncher, widget); + } + } + } + mLauncher.fail("Unable to find widget at cell " + cellX + "," + cellY); + // This statement is unreachable because mLauncher.fail throws an exception + // but is needed for compiling + return null; + } + @Nullable public Widget tryGetPendingWidget(long timeout) { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java index 021cc98c23..141476c782 100644 --- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java +++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java @@ -32,23 +32,36 @@ interface WorkspaceDragSource { Launchable launchable = getLaunchable(); LauncherInstrumentation launcher = launchable.mLauncher; try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) { - final Point launchableCenter = launchable.getObject().getVisibleCenter(); - final Point displaySize = launcher.getRealDisplaySize(); - final int width = displaySize.x / 2; - Workspace.dragIconToWorkspace( - launcher, - launchable, - () -> new Point( - launchableCenter.x >= width - ? launchableCenter.x - width / 2 - : launchableCenter.x + width / 2, - displaySize.y / 2), - startsActivity, - isWidgetShortcut, - launchable::addExpectedEventsForLongClick); + internalDragToWorkspace(startsActivity, isWidgetShortcut); } } + /** + * TODO(Redesign WorkspaceDragSource to have actual private methods) + * Temporary private method + * + * @param startsActivity whether it's expected to start an activity. + * @param isWidgetShortcut whether we drag a widget shortcut + */ + default void internalDragToWorkspace(boolean startsActivity, boolean isWidgetShortcut) { + Launchable launchable = getLaunchable(); + LauncherInstrumentation launcher = launchable.mLauncher; + final Point launchableCenter = launchable.getObject().getVisibleCenter(); + final Point displaySize = launcher.getRealDisplaySize(); + final int width = displaySize.x / 2; + Workspace.dragIconToWorkspace( + launcher, + launchable, + () -> new Point( + launchableCenter.x >= width + ? launchableCenter.x - width / 2 + : launchableCenter.x + width / 2, + displaySize.y / 2), + startsActivity, + isWidgetShortcut, + launchable::addExpectedEventsForLongClick); + } + /** * Drag an object to the given cell in workspace. The target cell must be empty. *