From ccec1de6aee9aa5a79f12b6443627207934a055d Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Wed, 6 Oct 2021 11:10:12 +0000 Subject: [PATCH] Add new quickstep test for many tasks open at once. This test is needed for testing overview-grid-related functionality. - Opening non-focused tasks - Dismissing a non-focused task from the grid - Grid rows rebalancing after dismissal, which only happens when enough apps are open to be off-screen. - Grid tasks do not scroll after dismissal. Test: TaplTestsQuickstep.java Bug: 197630182 Change-Id: Ic907db4643cdc2eb9e4610dab917347e234e470c --- .../QuickstepTestInformationHandler.java | 11 +++ .../android/quickstep/views/RecentsView.java | 16 +++- .../android/quickstep/TaplTestsQuickstep.java | 77 ++++++++++++++++++- .../launcher3/testing/TestProtocol.java | 2 + tests/AndroidManifest-common.xml | 51 +++++++----- .../OtherBaseTestingActivity.java | 22 ++++++ .../android/launcher3/tapl/BaseOverview.java | 73 +++++++++++++++++- .../tapl/LauncherInstrumentation.java | 5 ++ .../android/launcher3/tapl/OverviewTask.java | 28 ++++++- 9 files changed, 261 insertions(+), 24 deletions(-) create mode 100644 tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index 6ccb152cbc..95ab62f1f6 100644 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -63,6 +63,17 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, focusedTaskRect.height()); return response; } + + case TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET: { + if (!mDeviceProfile.isTablet) { + return null; + } + Rect gridTaskRect = new Rect(); + LauncherActivityInterface.INSTANCE.calculateGridTaskSize(mContext, mDeviceProfile, + gridTaskRect, PagedOrientationHandler.PORTRAIT); + response.putParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD, gridTaskRect); + return response; + } } return super.call(method, arg); diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 4d488db4cf..292e9d77cb 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1484,6 +1484,20 @@ public abstract class RecentsView LauncherState.NORMAL); + assertTrue("Launcher internal state is not Home", + isInState(() -> LauncherState.NORMAL)); executeOnLauncher( launcher -> assertEquals("Still have tasks after dismissing all", 0, getTaskCount(launcher))); @@ -180,6 +181,14 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { return launcher.getOverviewPanel().getTaskViewCount(); } + private int getTopRowTaskCountForTablet(Launcher launcher) { + return launcher.getOverviewPanel().getTopRowTaskCountForTablet(); + } + + private int getBottomRowTaskCountForTablet(Launcher launcher) { + return launcher.getOverviewPanel().getBottomRowTaskCountForTablet(); + } + @Test @NavigationModeSwitch @PortraitLandscape @@ -276,4 +285,70 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { isTestActivityRunning(2)); getAndAssertBackground(); } + + @Test + @PortraitLandscape + public void testOverviewForTablet() throws Exception { + if (!mLauncher.isTablet()) { + return; + } + for (int i = 2; i <= 12; i++) { + startTestActivity(i); + } + + Overview overview = mLauncher.pressHome().switchToOverview(); + executeOnLauncher( + launcher -> assertTrue("Don't have at least 11 tasks", + getTaskCount(launcher) >= 11)); + + // Test scroll the first task off screen + overview.scrollCurrentTaskOffScreen(); + assertTrue("Launcher internal state is not Overview", + isInState(() -> LauncherState.OVERVIEW)); + executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0", + getCurrentOverviewPage(launcher) > 0)); + + // Test opening the task. + overview.getCurrentTask().open(); + assertTrue("Test activity didn't open from Overview", + mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity8")), + DEFAULT_UI_TIMEOUT)); + + // Scroll the task offscreen as it is now first + overview = mLauncher.pressHome().switchToOverview(); + overview.scrollCurrentTaskOffScreen(); + assertTrue("Launcher internal state is not Overview", + isInState(() -> LauncherState.OVERVIEW)); + executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0", + getCurrentOverviewPage(launcher) > 0)); + + // Test dismissing the later task. + final Integer numTasks = getFromLauncher(this::getTaskCount); + overview.getCurrentTask().dismiss(); + executeOnLauncher( + launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview", + numTasks - 1, getTaskCount(launcher))); + executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after dismissal", + (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet( + launcher)) <= 1))); + + // Test dismissing more tasks. + assertTrue("Launcher internal state didn't remain in Overview", + isInState(() -> LauncherState.OVERVIEW)); + overview.getCurrentTask().dismiss(); + assertTrue("Launcher internal state didn't remain in Overview", + isInState(() -> LauncherState.OVERVIEW)); + overview.getCurrentTask().dismiss(); + executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals", + (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet( + launcher)) <= 1))); + + // Test dismissing all tasks. + mLauncher.pressHome().switchToOverview().dismissAllTasks(); + assertTrue("Launcher internal state is not Home", + isInState(() -> LauncherState.NORMAL)); + executeOnLauncher( + launcher -> assertEquals("Still have tasks after dismissing all", + 0, getTaskCount(launcher))); + } } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 893a215ba7..5bf0342fcc 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -104,6 +104,8 @@ public final class TestProtocol { public static final String REQUEST_GET_ACTIVITIES = "get-activities"; public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET = "get-focused-task-height-for-tablet"; + public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET = + "get-grid-task-size-rect-for-tablet"; public static Long sForcePauseTimeout; public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout"; diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 8222f75268..aae8fb55e4 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -145,6 +145,16 @@ + + + + + + - + - - + + - + - - + + - + - - + + + + + + + + diff --git a/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java new file mode 100644 index 0000000000..8bcab6265c --- /dev/null +++ b/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 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.testcomponent; + +/** + * Extension of BaseTestingActivity to help test many activities open at once. + */ +public class OtherBaseTestingActivity extends BaseTestingActivity {} diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index b037be4295..0e3b5013d8 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -25,6 +25,7 @@ import androidx.test.uiautomator.UiObject2; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * Common overview panel for both Launcher and fallback recents @@ -53,14 +54,18 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { } private void flingForwardImpl() { + flingForwardImpl(0); + } + + private void flingForwardImpl(int rightMargin) { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to fling forward in overview")) { LauncherInstrumentation.log("Overview.flingForward before fling"); final UiObject2 overview = verifyActiveContainer(); final int leftMargin = mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth(); - mLauncher.scroll( - overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, false); + mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, rightMargin, 0), + 20, false); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("flung forwards")) { verifyActiveContainer(); @@ -86,6 +91,8 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { mLauncher.clickLauncherObject( mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector)); + + mLauncher.waitUntilLauncherObjectGone(clearAllSelector); } } @@ -110,6 +117,40 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { } } + /** + * Scrolls the current task via flinging forward until it is off screen. + * + * If only one task is present, it is only partially scrolled off screen and will still be + * the current task. + */ + public void scrollCurrentTaskOffScreen() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to scroll current task off screen in overview")) { + verifyActiveContainer(); + + OverviewTask task = getCurrentTask(); + mLauncher.assertNotNull("current task is null", task); + flingForwardImpl(task.getTaskCenterX()); + + try (LauncherInstrumentation.Closable c2 = + mLauncher.addContextLayer("scrolled task off screen")) { + verifyActiveContainer(); + verifyActionsViewVisibility(); + + if (getTaskCount() > 1) { + if (mLauncher.isTablet()) { + mLauncher.assertTrue("current task is not grid height", + getCurrentTask().getVisibleHeight() == mLauncher + .getGridTaskRectForTablet().height()); + } + mLauncher.assertTrue("Current task not scrolled off screen", + !getCurrentTask().equals(task)); + } + } + } + } + /** * Gets the current task in the carousel, or fails if the carousel is empty. * @@ -130,6 +171,20 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { return new OverviewTask(mLauncher, widestTask, this); } + /** + * Returns a list of all tasks fully visible in the tablet grid overview. + */ + @NonNull + public List getCurrentTasksForTablet() { + final List taskViews = getTasks(); + mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size()); + + final int gridTaskWidth = mLauncher.getGridTaskRectForTablet().width(); + + return taskViews.stream().filter(t -> t.getVisibleBounds().width() == gridTaskWidth).map( + t -> new OverviewTask(mLauncher, t, this)).collect(Collectors.toList()); + } + @NonNull private List getTasks() { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( @@ -166,11 +221,18 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { } } + /** + * Returns if clear all button is visible. + */ + public boolean isClearAllVisible() { + return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all")); + } + /* TODO(b/197630182): Once b/188790554 is fixed, remove instanceof check. Currently, when swiping from app to overview in Fallback Recents, taskbar remains and no action buttons are visible, so we are only testing Overview for now, not BaseOverview. */ private void verifyActionsViewVisibility() { - if (!(this instanceof Overview)) { + if (!(this instanceof Overview) || !hasTasks()) { return; } try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( @@ -198,8 +260,13 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { /** * Returns Overview focused task if it exists. + * + * @throws IllegalStateException if not run on a tablet device. */ UiObject2 getFocusedTaskForTablet() { + if (!mLauncher.isTablet()) { + throw new IllegalStateException("Must be run on tablet device."); + } final List taskViews = getTasks(); if (taskViews.size() == 0) { return null; diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 55fb2c1ce3..9ee0e25bd7 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -326,6 +326,11 @@ public final class LauncherInstrumentation { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + Rect getGridTaskRectForTablet() { + return ((Rect) getTestInfo(TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET) + .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD)); + } + float getExactScreenCenterX() { return getRealDisplaySize().x / 2f; } diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 9419839d5d..15bddd7c88 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -24,7 +24,9 @@ import androidx.test.uiautomator.UiObject2; import com.android.launcher3.testing.TestProtocol; +import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * A recent task in the overview panel carousel. @@ -47,10 +49,14 @@ public final class OverviewTask { mOverview.verifyActiveContainer(); } - private int getVisibleHeight() { + int getVisibleHeight() { return mTask.getVisibleBounds().height(); } + int getTaskCenterX() { + return mTask.getVisibleCenter().x; + } + /** * Dismisses the task by swiping up. */ @@ -68,6 +74,8 @@ public final class OverviewTask { boolean taskWasFocused = mLauncher.isTablet() && getVisibleHeight() == mLauncher .getFocusedTaskHeightForTablet(); + List originalTasksCenterX = getCurrentTasksCenterXList(); + boolean isClearAllVisibleBeforeDismiss = mOverview.isClearAllVisible(); dismissBySwipingUp(); @@ -76,6 +84,16 @@ public final class OverviewTask { mLauncher.assertNotNull("No task became focused", mOverview.getFocusedTaskForTablet()); } + if (!isClearAllVisibleBeforeDismiss) { + List currentTasksCenterX = getCurrentTasksCenterXList(); + if (originalTasksCenterX.size() == currentTasksCenterX.size()) { + // Check for the same number of visible tasks before and after to + // avoid asserting on cases of shifting all tasks to close the distance + // between clear all and tasks at the end of the grid. + mLauncher.assertTrue("Task centers not aligned", + originalTasksCenterX.equals(currentTasksCenterX)); + } + } } } } @@ -94,6 +112,14 @@ public final class OverviewTask { + centerY, "swiping to dismiss"); } + private List getCurrentTasksCenterXList() { + return mLauncher.isTablet() + ? mOverview.getCurrentTasksForTablet().stream() + .map(OverviewTask::getTaskCenterX) + .collect(Collectors.toList()) + : List.of(mOverview.getCurrentTask().getTaskCenterX()); + } + /** * Clicks at the task. */