Merge "Adding test for the Widgets reordering." into tm-qpr-dev

This commit is contained in:
Sebastián Franco
2022-07-19 16:36:31 +00:00
committed by Android (Google) Code Review
14 changed files with 878 additions and 22 deletions
@@ -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;
@@ -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<TestBoardWidget> mWidgetsRects = new ArrayList<>();
Map<Character, TestBoardWidget> mWidgetsMap = new HashMap<>();
List<TestBoardAppIcon> mIconPoints = new ArrayList<>();
Map<Character, TestBoardAppIcon> 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<TestBoardWidget> 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<Point> used, char[][] board) {
char type = board[x][y];
Queue<Point> search = new ArrayDeque<Point>();
Point current = new Point(x, y);
search.add(current);
used.add(current);
List<Point> 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<TestBoardWidget> getRects(char[][] board) {
Set<Point> used = new HashSet<>();
List<TestBoardWidget> 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<TestBoardAppIcon> getIconPoints(char[][] board) {
List<TestBoardAppIcon> 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;
}
}
@@ -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<Point, ReorderTestCase> 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());
}
}
@@ -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;
}
}
@@ -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';
}
}
@@ -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<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
}
@@ -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<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
}
@@ -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<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
}
@@ -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<CellLayoutBoard> 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());
}
}
@@ -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<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
Map.of(new Point(5, 5), TEST_CASE_5x5);
}
@@ -1221,6 +1221,13 @@ public final class LauncherInstrumentation {
return object;
}
@NonNull
List<UiObject2> waitForObjectsBySelector(BySelector selector) {
final List<UiObject2> 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);
@@ -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 {
*
* <p> If {@code configurable} is true, then either accepts or cancels the configuration based
* on {@code acceptsConfig}.
* <p> 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.
@@ -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<String, Point> 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<UiObject2> 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(
@@ -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.
*