Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df1c1c84 authored by Sebastián Franco's avatar Sebastián Franco Committed by Android (Google) Code Review
Browse files

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

parents 69a83d0d 5cba4763
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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) {
        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;
+152 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+197 −0
Original line number Diff line number Diff line
/*
 * 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());
    }
}
+44 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+48 −0
Original line number Diff line number Diff line
/*
 * 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';
    }
}
Loading