summaryrefslogtreecommitdiff
path: root/tests/tapl/com/android/launcher3
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tapl/com/android/launcher3')
-rw-r--r--tests/tapl/com/android/launcher3/tapl/AllApps.java74
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Background.java13
-rw-r--r--tests/tapl/com/android/launcher3/tapl/BaseOverview.java53
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Home.java5
-rw-r--r--tests/tapl/com/android/launcher3/tapl/HomeAllApps.java43
-rw-r--r--tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java222
-rw-r--r--tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java58
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Launchable.java70
-rw-r--r--tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java78
-rw-r--r--tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java374
-rw-r--r--tests/tapl/com/android/launcher3/tapl/OverviewTask.java104
-rw-r--r--tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java19
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Qsb.java48
-rw-r--r--tests/tapl/com/android/launcher3/tapl/SearchInputSource.java52
-rw-r--r--tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java31
-rw-r--r--tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java5
-rw-r--r--tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java3
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Taskbar.java69
-rw-r--r--tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java (renamed from tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java)14
-rw-r--r--tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java21
-rw-r--r--tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java5
-rw-r--r--tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java5
-rw-r--r--tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java42
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Widgets.java18
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Workspace.java72
-rw-r--r--tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java5
26 files changed, 1245 insertions, 258 deletions
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index fb08ea44eb..0e785659a6 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -16,6 +16,9 @@
package com.android.launcher3.tapl;
+import static android.view.KeyEvent.KEYCODE_ESCAPE;
+import static android.view.KeyEvent.KEYCODE_META_RIGHT;
+
import static com.android.launcher3.tapl.LauncherInstrumentation.DEFAULT_POLL_INTERVAL;
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
@@ -37,17 +40,27 @@ import com.android.launcher3.testing.shared.TestProtocol;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
*/
-public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
+public abstract class AllApps extends LauncherInstrumentation.VisibleContainer
+ implements KeyboardQuickSwitchSource {
// Defer updates flag used to defer all apps updates by a test's request.
private static final int DEFER_UPDATES_TEST = 1 << 1;
private static final int MAX_SCROLL_ATTEMPTS = 40;
+ private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
+ private static final String FAST_SCROLLER_RES_ID = "fast_scroller";
+
+ private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+ private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+
private final int mHeight;
private final int mIconHeight;
@@ -63,6 +76,16 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ @Override
+ public LauncherInstrumentation getLauncher() {
+ return mLauncher;
+ }
+
+ @Override
+ public LauncherInstrumentation.ContainerType getStartingContainerType() {
+ return getContainerType();
+ }
+
private boolean hasClickableIcon(UiObject2 allAppsContainer, UiObject2 appListRecycler,
BySelector appIconSelector, int displayBottom) {
final UiObject2 icon;
@@ -338,6 +361,55 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
}
/**
+ * Taps outside bottom sheet to dismiss it. Available on tablets only.
+ * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
+ */
+ public void dismissByTappingOutsideForTablet(boolean tapRight) {
+ mLauncher.assertTrue("Device must be a tablet", mLauncher.isTablet());
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to tap outside AllApps bottom sheet on the "
+ + (tapRight ? "right" : "left"))) {
+
+ final UiObject2 container = (tapRight)
+ ? mLauncher.waitForLauncherObject(FAST_SCROLLER_RES_ID) :
+ mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
+
+ mLauncher.touchOutsideContainer(container, tapRight, false);
+ try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
+ "tapped outside AllApps bottom sheet")) {
+ verifyVisibleContainerOnDismiss();
+ }
+ }
+ }
+
+ /** Presses the meta keyboard shortcut to dismiss AllApps. */
+ public void dismissByKeyboardShortcut() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.getDevice().pressKeyCode(KEYCODE_META_RIGHT);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "pressed meta key")) {
+ verifyVisibleContainerOnDismiss();
+ }
+ }
+ }
+
+ /** Presses the esc key to dismiss AllApps. */
+ public void dismissByEscKey() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
+ mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "pressed esc key")) {
+ verifyVisibleContainerOnDismiss();
+ }
+ }
+ }
+
+ protected abstract void verifyVisibleContainerOnDismiss();
+
+ /**
* Return the QSB UI object on the AllApps screen.
* @return the QSB UI object.
*/
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 677f204748..8713b689f3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -39,7 +39,8 @@ import java.util.regex.Pattern;
* Indicates the base state with a UI other than Overview running as foreground. It can also
* indicate Launcher as long as Launcher is not in Overview state.
*/
-public abstract class Background extends LauncherInstrumentation.VisibleContainer {
+public abstract class Background extends LauncherInstrumentation.VisibleContainer
+ implements KeyboardQuickSwitchSource {
private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500;
private static final Pattern SQUARE_BUTTON_EVENT = Pattern.compile("onOverviewToggle");
@@ -47,6 +48,16 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine
super(launcher);
}
+ @Override
+ public LauncherInstrumentation getLauncher() {
+ return mLauncher;
+ }
+
+ @Override
+ public LauncherInstrumentation.ContainerType getStartingContainerType() {
+ return getContainerType();
+ }
+
/**
* Swipes up or presses the square button to switch to Overview.
* Returns the base overview, which can be either in Launcher or the fallback recents.
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index aa5c770ab9..b6b4a47a1a 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -16,15 +16,19 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
+
import android.graphics.Rect;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@@ -182,7 +186,14 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
Taskbar taskbar = new Taskbar(mLauncher);
taskbar.touchBottomCorner(tapRight);
- verifyActiveContainer();
+ if (mLauncher.isTransientTaskbar()) {
+ // Tapping outside Transient Taskbar returns to Workspace, wait for that state.
+ new Workspace(mLauncher);
+ } else {
+ // Should stay in Overview.
+ verifyActiveContainer();
+ verifyActionsViewVisibility();
+ }
}
}
@@ -201,7 +212,8 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
OverviewTask task = getCurrentTask();
mLauncher.assertNotNull("current task is null", task);
mLauncher.scrollLeftByDistance(verifyActiveContainer(),
- task.getVisibleWidth() + mLauncher.getOverviewPageSpacing());
+ mLauncher.getRealDisplaySize().x - task.getUiObject().getVisibleBounds().left
+ + mLauncher.getOverviewPageSpacing());
try (LauncherInstrumentation.Closable c2 =
mLauncher.addContextLayer("scrolled task off screen")) {
@@ -231,14 +243,13 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
final List<UiObject2> taskViews = getTasks();
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
- // taskViews contains up to 3 task views: the 'main' (having the widest visible part) one
- // in the center, and parts of its right and left siblings. Find the main task view by
- // its width.
- final UiObject2 widestTask = Collections.max(taskViews,
- (t1, t2) -> Integer.compare(mLauncher.getVisibleBounds(t1).width(),
- mLauncher.getVisibleBounds(t2).width()));
-
- return new OverviewTask(mLauncher, widestTask, this);
+ // The widest, and most top-right task should be the current task
+ UiObject2 currentTask = Collections.max(taskViews,
+ Comparator.comparingInt((UiObject2 t) -> t.getParent().getVisibleBounds().width())
+ .thenComparingInt((UiObject2 t) -> t.getParent().getVisibleCenter().x)
+ .thenComparing(Comparator.comparing(
+ (UiObject2 t) -> t.getParent().getVisibleCenter().y).reversed()));
+ return new OverviewTask(mLauncher, currentTask, this);
}
/** Returns an overview task matching TestActivity {@param activityNumber}. */
@@ -320,6 +331,22 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
mLauncher.getOverviewObjectSelector("clear_all"));
}
+ /**
+ * Returns the taskbar if it's a tablet, or {@code null} otherwise.
+ */
+ @Nullable
+ public Taskbar getTaskbar() {
+ if (!mLauncher.isTablet()) {
+ return null;
+ }
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get the taskbar")) {
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
+
+ return new Taskbar(mLauncher);
+ }
+ }
+
protected boolean isActionsViewVisible() {
if (!hasTasks() || isClearAllVisible()) {
return false;
@@ -366,8 +393,10 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
}
int focusedTaskHeight = mLauncher.getFocusedTaskHeightForTablet();
for (UiObject2 task : taskViews) {
- if (task.getVisibleBounds().height() == focusedTaskHeight) {
- return new OverviewTask(mLauncher, task, this);
+ OverviewTask overviewTask = new OverviewTask(mLauncher, task, this);
+
+ if (overviewTask.getVisibleHeight() == focusedTaskHeight) {
+ return overviewTask;
}
}
return null;
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 252435badb..85e28e86cb 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -62,4 +62,9 @@ public abstract class Home extends Background {
protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
return true;
}
+
+ @Override
+ public boolean isHomeState() {
+ return true;
+ }
} \ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 295190146f..9ca2dc8463 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -23,7 +23,6 @@ import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.shared.TestProtocol;
public class HomeAllApps extends AllApps {
- private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
HomeAllApps(LauncherInstrumentation launcher) {
super(launcher);
@@ -98,25 +97,6 @@ public class HomeAllApps extends AllApps {
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
- /**
- * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
- * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
- */
- public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
- try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
- LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to tap outside AllApps bottom sheet on the "
- + (tapRight ? "right" : "left"))) {
- final UiObject2 allAppsBottomSheet =
- mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
- mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
- try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
- "tapped outside AllApps bottom sheet")) {
- return mLauncher.getWorkspace();
- }
- }
- }
-
@NonNull
@Override
public Qsb getQsb() {
@@ -128,4 +108,27 @@ public class HomeAllApps extends AllApps {
return mLauncher.getTestInfo(TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+
+ @Override
+ protected void verifyVisibleContainerOnDismiss() {
+ mLauncher.getWorkspace();
+ }
+
+ @Override
+ public boolean isHomeState() {
+ return true;
+ }
+
+ /** Send the "back" gesture to go to workspace. */
+ public Workspace pressBackToWorkspace() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to press back from all apps to workspace")) {
+ mLauncher.runToState(
+ () -> mLauncher.pressBackImpl(),
+ NORMAL_STATE_ORDINAL,
+ "pressing back");
+ return new Workspace(mLauncher);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
new file mode 100644
index 0000000000..a1d8059631
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2023 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.tapl;
+
+import static com.android.launcher3.tapl.LauncherInstrumentation.KEYBOARD_QUICK_SWITCH_RES_ID;
+
+import android.view.KeyEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.testing.shared.TestProtocol;
+
+import java.util.regex.Pattern;
+
+/**
+ * Operations on the Keyboard Quick Switch View
+ */
+public final class KeyboardQuickSwitch {
+
+ private static final Pattern EVENT_ALT_TAB_DOWN = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_TAB"
+ + ".*?metaState=META_ALT_ON");
+ private static final Pattern EVENT_ALT_TAB_UP = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
+ + ".*?metaState=META_ALT_ON");
+ private static final Pattern EVENT_ALT_SHIFT_TAB_DOWN = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_TAB"
+ + ".*?metaState=META_ALT_ON|META_SHIFT_ON");
+ private static final Pattern EVENT_ALT_SHIFT_TAB_UP = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
+ + ".*?metaState=META_ALT_ON|META_SHIFT_ON");
+ private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN"
+ + ".*?keyCode=KEYCODE_ESCAPE.*?metaState=META_ALT_ON");
+ private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
+ + ".*?keyCode=KEYCODE_ESCAPE.*?metaState=META_ALT_ON");
+ private static final Pattern EVENT_KQS_ALT_LEFT_UP = Pattern.compile(
+ "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
+ + ".*?keyCode=KEYCODE_ALT_LEFT");
+ private static final Pattern EVENT_HOME_ALT_LEFT_UP = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_UP"
+ + ".*?keyCode=KEYCODE_ALT_LEFT");
+
+ private final LauncherInstrumentation mLauncher;
+ private final LauncherInstrumentation.ContainerType mStartingContainerType;
+ private final boolean mExpectHomeKeyEventsOnDismiss;
+
+ KeyboardQuickSwitch(
+ LauncherInstrumentation launcher,
+ LauncherInstrumentation.ContainerType startingContainerType,
+ boolean expectHomeKeyEventsOnDismiss) {
+ mLauncher = launcher;
+ mStartingContainerType = startingContainerType;
+ mExpectHomeKeyEventsOnDismiss = expectHomeKeyEventsOnDismiss;
+ }
+
+ /**
+ * Focuses the next task in the Keyboard quick switch view.
+ * <p>
+ * Tasks are ordered left-to-right in LTR, and vice versa in RLT, in a carousel.
+ * <ul>
+ * <li>If no task has been focused yet, and there is only one task, then that task will be
+ * focused</li>
+ * <li>If no task has been focused yet, and there are two or more tasks, then the second
+ * task will be focused</li>
+ * <li>If the currently-focused task is at the end of the list, the first task will be
+ * focused</li>
+ * </ul>
+ */
+ public KeyboardQuickSwitch moveFocusForward() {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to move keyboard quick switch focus forward");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_UP);
+ mLauncher.assertTrue("Failed to press alt+tab",
+ mLauncher.getDevice().pressKeyCode(
+ KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "pressed alt+tab")) {
+ mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ return this;
+ }
+ }
+ }
+
+ /**
+ * Focuses the next task in the Keyboard quick switch view.
+ * <p>
+ * Tasks are ordered left-to-right in LTR, and vice versa in RLT, in a carousel.
+ * <ul>
+ * <li>If no task has been focused yet, and there is only one task, then that task will be
+ * focused</li>
+ * <li>If no task has been focused yet, and there are two or more tasks, then the second
+ * task will be focused</li>
+ * <li>If the currently-focused task is at the start of the list, the last task will be
+ * focused</li>
+ * </ul>
+ */
+ public KeyboardQuickSwitch moveFocusBackward() {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to move keyboard quick switch focus backward");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_UP);
+ mLauncher.assertTrue("Failed to press alt+shift+tab",
+ mLauncher.getDevice().pressKeyCode(
+ KeyEvent.KEYCODE_TAB,
+ KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON));
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "pressed alt+shift+tab")) {
+ mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ return this;
+ }
+ }
+ }
+
+ /**
+ * Dismisses the Keyboard Quick Switch view without launching the focused task.
+ * <p>
+ * The device will return to the same state it started in before displaying the Keyboard Quick
+ * Switch view.
+ */
+ public void dismiss() {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to dismiss keyboard quick switch view");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
+ mLauncher.assertTrue("Failed to press alt+tab",
+ mLauncher.getDevice().pressKeyCode(
+ KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_ALT_ON));
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "pressed alt+esc")) {
+ mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ // Verify the final state is the same as the initial state
+ mLauncher.verifyContainerType(mStartingContainerType);
+
+ // Wait until the device has fully settled before unpressing the key code
+ if (mExpectHomeKeyEventsOnDismiss) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_HOME_ALT_LEFT_UP);
+ }
+ mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
+ }
+ }
+ }
+
+ /**
+ * Launches the currently-focused app task.
+ * <p>
+ * This method should only be used if the focused task is for a recent running app, otherwise
+ * use {@link #launchFocusedOverviewTask()}.
+ *
+ * @param expectedPackageName the package name of the expected launched app
+ */
+ public LaunchedAppState launchFocusedAppTask(@NonNull String expectedPackageName) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ return (LaunchedAppState) launchFocusedTask(expectedPackageName);
+ }
+ }
+
+ /**
+ * Launches the currently-focused overview task.
+ * <p>
+ * This method only should be used if the focused task is for overview, otherwise use
+ * {@link #launchFocusedAppTask(String)}.
+ */
+ public Overview launchFocusedOverviewTask() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ return (Overview) launchFocusedTask(null);
+ }
+ }
+
+ private LauncherInstrumentation.VisibleContainer launchFocusedTask(
+ @Nullable String expectedPackageName) {
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to launch focused task: "
+ + (expectedPackageName == null ? "Overview" : expectedPackageName))) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KQS_ALT_LEFT_UP);
+ mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "un-pressed left alt")) {
+ mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
+
+ if (expectedPackageName != null) {
+ mLauncher.assertAppLaunched(expectedPackageName);
+ return mLauncher.getLaunchedAppState();
+ } else {
+ return mLauncher.getOverview();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java
new file mode 100644
index 0000000000..677ed0434a
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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.tapl;
+
+import static com.android.launcher3.tapl.LauncherInstrumentation.KEYBOARD_QUICK_SWITCH_RES_ID;
+
+import android.view.KeyEvent;
+
+/**
+ * {@link com.android.launcher3.tapl.LauncherInstrumentation.VisibleContainer} that can be used to
+ * show the keyboard quick switch view.
+ */
+interface KeyboardQuickSwitchSource {
+
+ /**
+ * Shows the Keyboard Quick Switch view.
+ */
+ default KeyboardQuickSwitch showQuickSwitchView() {
+ LauncherInstrumentation launcher = getLauncher();
+
+ try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
+ "want to show keyboard quick switch object");
+ LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
+ launcher.pressAndHoldKeyCode(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_LEFT_ON);
+
+ try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer(
+ "press and held alt+tab")) {
+ launcher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
+ launcher.unpressKeyCode(KeyEvent.KEYCODE_TAB, 0);
+
+ return new KeyboardQuickSwitch(
+ launcher, getStartingContainerType(), isHomeState());
+ }
+ }
+ }
+
+ /** This method requires public access, however should not be called in tests. */
+ LauncherInstrumentation getLauncher();
+
+ /** This method requires public access, however should not be called in tests. */
+ LauncherInstrumentation.ContainerType getStartingContainerType();
+
+ /** This method requires public access, however should not be called in tests. */
+ boolean isHomeState();
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index a953fa9009..fe927b3fd0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -16,15 +16,14 @@
package com.android.launcher3.tapl;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
import android.graphics.Point;
import android.view.MotionEvent;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -48,12 +47,36 @@ public abstract class Launchable {
return mObject;
}
+ protected boolean launcherStopsAfterLaunch() {
+ return true;
+ }
+
/**
* Clicks the object to launch its app.
+ * We are assuming non-translucent app launches because only such launches generate
+ * LAUNCHER_ACTIVITY_STOPPED_MESSAGE.
*/
public LaunchedAppState launch(String expectedPackageName) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- return launch(By.pkg(expectedPackageName));
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to launch an app from " + launchableType())) {
+ LauncherInstrumentation.log("Launchable.launch before click "
+ + mObject.getVisibleCenter() + " in "
+ + mLauncher.getVisibleBounds(mObject));
+
+ if (launcherStopsAfterLaunch()) {
+ mLauncher.executeAndWaitForLauncherStop(
+ () -> mLauncher.clickLauncherObject(mObject),
+ "clicking the launchable");
+ } else {
+ mLauncher.clickLauncherObject(mObject);
+ }
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ expectActivityStartEvents();
+ return mLauncher.assertAppLaunched(expectedPackageName);
+ }
+ }
}
}
@@ -61,21 +84,6 @@ public abstract class Launchable {
protected abstract String launchableType();
- private LaunchedAppState launch(BySelector selector) {
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "want to launch an app from " + launchableType())) {
- LauncherInstrumentation.log("Launchable.launch before click "
- + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
-
- mLauncher.clickLauncherObject(mObject);
-
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
- expectActivityStartEvents();
- return assertAppLaunched(selector);
- }
- }
- }
-
/**
* Clicks a launcher object to initiate splitscreen, where the selected app will be one of two
* apps running on the screen. Should be called when Launcher is in a "split staging" state
@@ -83,12 +91,18 @@ public abstract class Launchable {
* fired when the click is executed.
*/
public LaunchedAppState launchIntoSplitScreen() {
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "want to launch split tasks from " + launchableType())) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to launch split tasks from " + launchableType())) {
LauncherInstrumentation.log("Launchable.launch before click "
- + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
-
- mLauncher.clickLauncherObject(mObject);
+ + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(
+ mObject));
+ mLauncher.executeAndWaitForLauncherEvent(
+ () -> mLauncher.clickLauncherObject(mObject),
+ accessibilityEvent ->
+ accessibilityEvent.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ () -> "Unable to click object to launch split",
+ "Click launcher object to launch split");
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_START_EVENT);
@@ -97,14 +111,6 @@ public abstract class Launchable {
}
}
- protected LaunchedAppState assertAppLaunched(BySelector selector) {
- mLauncher.assertTrue(
- "App didn't start: (" + selector + ")",
- mLauncher.getDevice().wait(Until.hasObject(selector),
- LauncherInstrumentation.WAIT_TIME_MS));
- return new LaunchedAppState(mLauncher);
- }
-
Point startDrag(long downTime, Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
final Point iconCenter = getObject().getVisibleCenter();
final Point dragStartCenter = new Point(iconCenter.x,
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 30417c082f..184ece74f5 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -20,12 +20,11 @@ import static com.android.launcher3.tapl.LauncherInstrumentation.DEFAULT_POLL_IN
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_SHELL_DRAG_READY;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_SCALE;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import android.graphics.Point;
import android.graphics.Rect;
@@ -34,7 +33,6 @@ import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Condition;
import androidx.test.uiautomator.UiDevice;
@@ -55,13 +53,13 @@ public final class LaunchedAppState extends Background {
private static final int STASHED_TASKBAR_BOTTOM_EDGE_DP = 1;
private final Condition<UiDevice, Boolean> mStashedTaskbarHintScaleCondition =
- device -> mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat(
- TestProtocol.TEST_INFO_RESPONSE_FIELD) - UNSTASHED_TASKBAR_HANDLE_HINT_SCALE
+ device -> Math.abs(mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD) - UNSTASHED_TASKBAR_HANDLE_HINT_SCALE)
< 0.00001f;
private final Condition<UiDevice, Boolean> mStashedTaskbarDefaultScaleCondition =
- device -> mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat(
- TestProtocol.TEST_INFO_RESPONSE_FIELD) - 1f < 0.00001f;
+ device -> Math.abs(mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD) - 1f) < 0.00001f;
LaunchedAppState(LauncherInstrumentation launcher) {
super(launcher);
@@ -72,6 +70,11 @@ public final class LaunchedAppState extends Background {
return LauncherInstrumentation.ContainerType.LAUNCHED_APP;
}
+ @Override
+ public boolean isHomeState() {
+ return false;
+ }
+
/**
* Returns the taskbar.
*
@@ -80,8 +83,6 @@ public final class LaunchedAppState extends Background {
public Taskbar getTaskbar() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get the taskbar")) {
- mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
-
return new Taskbar(mLauncher);
}
}
@@ -109,37 +110,32 @@ public final class LaunchedAppState extends Background {
/**
* Returns the Taskbar in a visible state.
*
- * The taskbar must already be hidden when calling this method.
+ * The taskbar must already be hidden and in transient mode when calling this method.
*/
- public Taskbar showTaskbar() {
- mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+ public Taskbar swipeUpToUnstashTaskbar() {
+ mLauncher.assertTrue("Taskbar is not transient, swipe up not supported",
+ mLauncher.isTransientTaskbar());
+
mLauncher.getTestInfo(REQUEST_ENABLE_BLOCK_TIMEOUT);
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "want to show the taskbar")) {
+ "want to swipe up to unstash the taskbar")) {
mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- final long downTime = SystemClock.uptimeMillis();
- final int unstashTargetY = mLauncher.getRealDisplaySize().y
- - (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT)
- .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2);
- final Point unstashTarget = new Point(
- mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
+ int taskbarFromNavThreshold = mLauncher.getTestInfo(REQUEST_TASKBAR_FROM_NAV_THRESHOLD)
+ .getInt(TEST_INFO_RESPONSE_FIELD);
+ int startX = mLauncher.getRealDisplaySize().x / 2;
+ int startY = mLauncher.getRealDisplaySize().y - 1;
+ int endX = startX;
+ int endY = startY - taskbarFromNavThreshold;
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
+ mLauncher.linearGesture(startX, startY, endX, endY, 10, /* slowDown= */ true,
LauncherInstrumentation.GestureScope.EXPECT_PILFER);
- LauncherInstrumentation.log("showTaskbar: sent down");
+ LauncherInstrumentation.log("swipeUpToUnstashTaskbar: sent linear swipe up gesture");
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
- mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
- LauncherInstrumentation.GestureScope.EXPECT_PILFER);
-
- return new Taskbar(mLauncher);
- }
+ return new Taskbar(mLauncher);
} finally {
- mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT);
}
}
@@ -200,8 +196,8 @@ public final class LaunchedAppState extends Background {
try (LauncherInstrumentation.Closable c4 = launcher.addContextLayer(
"dropped item")) {
- launchable.assertAppLaunched(By.pkg(expectedNewPackageName));
- launchable.assertAppLaunched(By.pkg(expectedExistingPackageName));
+ launcher.assertAppLaunched(expectedNewPackageName);
+ launcher.assertAppLaunched(expectedExistingPackageName);
}
}
}
@@ -280,7 +276,8 @@ public final class LaunchedAppState extends Background {
Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2,
mLauncher.getRealDisplaySize().y - 1);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER,
- new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null);
+ new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null,
+ InputDevice.SOURCE_MOUSE);
mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition,
LauncherInstrumentation.WAIT_TIME_MS);
@@ -292,7 +289,7 @@ public final class LaunchedAppState extends Background {
mLauncher.getRealDisplaySize().y - 500);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT,
new Point(outsideStashedTaskbarHintArea.x, outsideStashedTaskbarHintArea.y),
- null);
+ null, InputDevice.SOURCE_MOUSE);
mLauncher.getDevice().wait(mStashedTaskbarDefaultScaleCondition,
LauncherInstrumentation.WAIT_TIME_MS);
@@ -343,4 +340,17 @@ public final class LaunchedAppState extends Background {
}
}
}
+
+ /** Send the "back" gesture to go to workspace. */
+ public Workspace pressBackToWorkspace() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to press back from launched app to workspace")) {
+ mLauncher.executeAndWaitForWallpaperAnimation(
+ () -> mLauncher.pressBackImpl(),
+ "pressing back"
+ );
+ return new Workspace(mLauncher);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c58ae1608a..91ef472ee4 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -20,21 +20,26 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DONT_KILL_APP;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT;
import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_NUM_ALL_APPS_COLUMNS;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
+import android.app.UiModeManager;
import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
@@ -48,6 +53,9 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -169,10 +177,13 @@ public final class LauncherInstrumentation {
private static final String OPEN_FOLDER_RES_ID = "folder_content";
static final String TASKBAR_RES_ID = "taskbar_view";
private static final String SPLIT_PLACEHOLDER_RES_ID = "split_placeholder";
+ static final String KEYBOARD_QUICK_SWITCH_RES_ID = "keyboard_quick_switch_view";
public static final int WAIT_TIME_MS = 30000;
static final long DEFAULT_POLL_INTERVAL = 1000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
+ private static final String ASSISTANT_PACKAGE = "com.google.android.googlequicksearchbox";
+ private static final String ASSISTANT_GO_HOME_RES_ID = "home_icon";
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@@ -204,15 +215,33 @@ public final class LauncherInstrumentation {
* Constructs the root of TAPL hierarchy. You get all other objects from it.
*/
public LauncherInstrumentation() {
- this(InstrumentationRegistry.getInstrumentation());
+ this(InstrumentationRegistry.getInstrumentation(), false);
}
/**
* Constructs the root of TAPL hierarchy. You get all other objects from it.
- * Deprecated: use the constructor without parameters instead.
+ */
+ public LauncherInstrumentation(boolean isLauncherTest) {
+ this(InstrumentationRegistry.getInstrumentation(), isLauncherTest);
+ }
+
+ /**
+ * Constructs the root of TAPL hierarchy. You get all other objects from it.
+ *
+ * @deprecated use the constructor without Instrumentation parameter instead.
*/
@Deprecated
public LauncherInstrumentation(Instrumentation instrumentation) {
+ this(instrumentation, false);
+ }
+
+ /**
+ * Constructs the root of TAPL hierarchy. You get all other objects from it.
+ *
+ * @deprecated use the constructor without Instrumentation parameter instead.
+ */
+ @Deprecated
+ public LauncherInstrumentation(Instrumentation instrumentation, boolean isLauncherTest) {
mInstrumentation = instrumentation;
mDevice = UiDevice.getInstance(instrumentation);
@@ -251,28 +280,31 @@ public final class LauncherInstrumentation {
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
if (TestHelpers.isInLauncherProcess()) {
pm.setComponentEnabledSetting(cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
- // b/195031154
- SystemClock.sleep(5000);
} else {
try {
final int userId = getContext().getUserId();
- final String launcherPidCommand = "pidof " + pi.packageName;
- final String initialPid = mDevice.executeShellCommand(launcherPidCommand)
- .replaceAll("\\s", "");
mDevice.executeShellCommand(
"pm enable --user " + userId + " " + cn.flattenToString());
- // Wait for Launcher restart after enabling test provider.
- for (int i = 0; i < 100; ++i) {
- final String currentPid = mDevice.executeShellCommand(launcherPidCommand)
- .replaceAll("\\s", "");
- if (!currentPid.isEmpty() && !currentPid.equals(initialPid)) break;
- if (i == 99) fail("Launcher didn't restart after enabling test provider");
- SystemClock.sleep(100);
- }
} catch (IOException e) {
fail(e.toString());
}
}
+
+ final int iterations = isLauncherTest ? 300 : 100;
+
+ // Wait for Launcher content provider to become enabled.
+ for (int i = 0; i < iterations; ++i) {
+ final ContentProviderClient testProvider = getContext().getContentResolver()
+ .acquireContentProviderClient(mTestProviderUri);
+ if (testProvider != null) {
+ testProvider.close();
+ break;
+ }
+ if (i == iterations - 1) {
+ fail("Launcher content provider is still not enabled");
+ }
+ SystemClock.sleep(100);
+ }
}
}
@@ -338,6 +370,16 @@ public final class LauncherInstrumentation {
.getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ Insets getImeInsets() {
+ return getTestInfo(TestProtocol.REQUEST_IME_INSETS)
+ .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
+ public int getNumAllAppsColumns() {
+ return getTestInfo(REQUEST_NUM_ALL_APPS_COLUMNS).getInt(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
public boolean isTablet() {
return getTestInfo(TestProtocol.REQUEST_IS_TABLET)
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -543,6 +585,11 @@ public final class LauncherInstrumentation {
if (hasSystemLauncherObject(OVERVIEW_RES_ID)) return "Overview";
if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
+ if (hasLauncherObject(TASKBAR_RES_ID)) return "Taskbar";
+ if (hasLauncherObject("wallpaper_carousel")) return "Launcher Settings Popup";
+ if (mDevice.hasObject(By.pkg(getLauncherPackageName()).depth(0))) {
+ return "<Launcher in invalid state>";
+ }
return "LaunchedApp (" + getVisiblePackages() + ")";
}
@@ -689,6 +736,7 @@ public final class LauncherInstrumentation {
/**
* Set the trackpad gesture type of the interaction.
+ *
* @param trackpadGestureType whether it's not from trackpad, two-finger, three-finger, or
* four-finger gesture.
*/
@@ -713,6 +761,10 @@ public final class LauncherInstrumentation {
mExpectedRotationCheckEnabled = expectedRotationCheckEnabled;
}
+ public boolean getExpectedRotationCheckEnabled() {
+ return mExpectedRotationCheckEnabled;
+ }
+
public String getNavigationModeMismatchError(boolean waitForCorrectState) {
final int waitTime = waitForCorrectState ? WAIT_TIME_MS : 0;
final NavigationModel navigationModel = getNavigationModel();
@@ -743,7 +795,7 @@ public final class LauncherInstrumentation {
return isTablet() ? getLauncherPackageName() : SYSTEMUI_PACKAGE;
}
- private UiObject2 verifyContainerType(ContainerType containerType) {
+ UiObject2 verifyContainerType(ContainerType containerType) {
waitForLauncherInitialized();
if (mExpectedRotationCheckEnabled && mExpectedRotation != null) {
@@ -772,12 +824,8 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
-
- if (is3PLauncher() && isTablet()) {
- waitForSystemLauncherObject(TASKBAR_RES_ID);
- } else {
- waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- }
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(WORKSPACE_RES_ID);
}
@@ -786,12 +834,8 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
-
- if (is3PLauncher() && isTablet()) {
- waitForSystemLauncherObject(TASKBAR_RES_ID);
- } else {
- waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- }
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(WIDGETS_RES_ID);
}
@@ -801,6 +845,7 @@ public final class LauncherInstrumentation {
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
return waitForLauncherObject(APPS_RES_ID);
}
@@ -809,8 +854,9 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
- if (is3PLauncher() && isTablet()) {
+ if (is3PLauncher() && isTablet() && !isTransientTaskbar()) {
waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
@@ -823,12 +869,13 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- if (isTablet()) {
+ if (isTablet() && !is3PLauncher()) {
waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
}
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
return waitForSystemLauncherObject(OVERVIEW_RES_ID);
}
@@ -843,6 +890,7 @@ public final class LauncherInstrumentation {
}
waitForSystemLauncherObject(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
return waitForSystemLauncherObject(OVERVIEW_RES_ID);
}
case LAUNCHED_APP: {
@@ -851,13 +899,18 @@ public final class LauncherInstrumentation {
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+ waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
if (mIgnoreTaskbarVisibility) {
return null;
}
if (isTablet()) {
- waitForSystemLauncherObject(TASKBAR_RES_ID);
+ // Only check that Persistent Taskbar is visible, since Transient Taskbar
+ // may or may not be visible by design.
+ if (!isTransientTaskbar()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ }
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
}
@@ -887,6 +940,12 @@ public final class LauncherInstrumentation {
fail("Launcher didn't initialize");
}
+ public boolean isLauncherActivityStarted() {
+ return getTestInfo(
+ TestProtocol.REQUEST_IS_LAUNCHER_LAUNCHER_ACTIVITY_STARTED).
+ getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
Parcelable executeAndWaitForLauncherEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message,
String actionName) {
@@ -915,6 +974,14 @@ public final class LauncherInstrumentation {
}
}
+ void executeAndWaitForLauncherStop(Runnable command, String actionName) {
+ executeAndWaitForLauncherEvent(
+ () -> command.run(),
+ event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Launcher activity didn't stop", actionName);
+ }
+
/**
* Get the resource ID of visible floating view.
*/
@@ -931,7 +998,7 @@ public final class LauncherInstrumentation {
/**
* Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content.
*/
- private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) {
+ private void swipeUpToCloseFloatingView() {
final Point displaySize = getRealDisplaySize();
final Optional<String> floatingRes = getFloatingResId();
@@ -940,16 +1007,11 @@ public final class LauncherInstrumentation {
return;
}
- GestureScope gestureScope = gestureStartFromLauncher
- // Without the navigation bar layer, the gesture scope on tablets remains inside the
- // launcher process.
- ? (isTablet() ? GestureScope.DONT_EXPECT_PILFER : GestureScope.EXPECT_PILFER)
- : GestureScope.EXPECT_PILFER;
linearGesture(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
- false, gestureScope);
+ false, GestureScope.EXPECT_PILFER);
try (LauncherInstrumentation.Closable c1 = addContextLayer(
String.format("Swiped up from floating view %s to home", floatingRes.get()))) {
@@ -969,6 +1031,25 @@ public final class LauncherInstrumentation {
}
/**
+ * Goes to home from immersive fullscreen app by first swiping up to bring navbar, and then
+ * performing {@code goHome()} action.
+ * Currently only supports gesture navigation mode.
+ *
+ * @return the Workspace object.
+ */
+ public Workspace goHomeFromImmersiveFullscreenApp() {
+ assertTrue("expected gesture navigation mode",
+ getNavigationModel() == NavigationModel.ZERO_BUTTON);
+ final Point displaySize = getRealDisplaySize();
+ linearGesture(
+ displaySize.x / 2, displaySize.y - 1,
+ displaySize.x / 2, 0,
+ ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
+ false, GestureScope.EXPECT_PILFER);
+ return goHome();
+ }
+
+ /**
* Goes to home by swiping up in zero-button mode or pressing Home button.
* Calling it after another TAPL call is safe because all TAPL methods wait for the animations
* to finish.
@@ -998,11 +1079,8 @@ public final class LauncherInstrumentation {
final Point displaySize = getRealDisplaySize();
- boolean gestureStartFromLauncher =
- isTablet() ? !isLauncher3() : isLauncherVisible();
-
// CLose floating views before going back to home.
- swipeUpToCloseFloatingView(gestureStartFromLauncher);
+ swipeUpToCloseFloatingView();
if (hasLauncherObject(WORKSPACE_RES_ID)) {
log(action = "already at home");
@@ -1024,7 +1102,7 @@ public final class LauncherInstrumentation {
action = "clicking home button";
runToState(
- waitForNavigationUiObject("home")::click,
+ getHomeButton()::click,
NORMAL_STATE_ORDINAL,
!hasLauncherObject(WORKSPACE_RES_ID)
&& (hasLauncherObject(APPS_RES_ID)
@@ -1043,30 +1121,34 @@ public final class LauncherInstrumentation {
*/
public void pressBack() {
try (Closable e = eventsCheck(); Closable c = addContextLayer("want to press back")) {
- waitForLauncherInitialized();
- final boolean launcherVisible =
- isTablet() ? isLauncherContainerVisible() : isLauncherVisible();
- boolean isThreeFingerTrackpadGesture =
- mTrackpadGestureType == TrackpadGestureType.THREE_FINGER;
- if (getNavigationModel() == NavigationModel.ZERO_BUTTON
- || isThreeFingerTrackpadGesture) {
- final Point displaySize = getRealDisplaySize();
- // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the
- // issue is solved.
- int startX = isThreeFingerTrackpadGesture ? displaySize.x / 4 : 0;
- int endX = isThreeFingerTrackpadGesture ? displaySize.x * 3 / 4 : displaySize.x / 2;
- linearGesture(startX, displaySize.y / 4, endX, displaySize.y / 4,
- 10, false, GestureScope.DONT_EXPECT_PILFER);
+ pressBackImpl();
+ }
+ }
+
+ void pressBackImpl() {
+ waitForLauncherInitialized();
+ final boolean launcherVisible =
+ isTablet() ? isLauncherContainerVisible() : isLauncherVisible();
+ boolean isThreeFingerTrackpadGesture =
+ mTrackpadGestureType == TrackpadGestureType.THREE_FINGER;
+ if (getNavigationModel() == NavigationModel.ZERO_BUTTON
+ || isThreeFingerTrackpadGesture) {
+ final Point displaySize = getRealDisplaySize();
+ // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the
+ // issue is solved.
+ int startX = isThreeFingerTrackpadGesture ? displaySize.x / 4 : 0;
+ int endX = isThreeFingerTrackpadGesture ? displaySize.x * 3 / 4 : displaySize.x / 2;
+ linearGesture(startX, displaySize.y / 4, endX, displaySize.y / 4,
+ 10, false, GestureScope.DONT_EXPECT_PILFER);
+ } else {
+ waitForNavigationUiObject("back").click();
+ }
+ if (launcherVisible) {
+ if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
} else {
- waitForNavigationUiObject("back").click();
- }
- if (launcherVisible) {
- if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
- } else {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
- }
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
}
}
}
@@ -1158,6 +1240,14 @@ public final class LauncherInstrumentation {
}
}
+ LaunchedAppState assertAppLaunched(@NonNull String expectedPackageName) {
+ BySelector packageSelector = By.pkg(expectedPackageName);
+ assertTrue("App didn't start: (" + packageSelector + ")",
+ mDevice.wait(Until.hasObject(packageSelector),
+ LauncherInstrumentation.WAIT_TIME_MS));
+ return new LaunchedAppState(this);
+ }
+
void waitUntilLauncherObjectGone(String resId) {
waitUntilGoneBySelector(getLauncherObjectSelector(resId));
}
@@ -1205,6 +1295,28 @@ public final class LauncherInstrumentation {
}
@NonNull
+ private UiObject2 getHomeButton() {
+ UiModeManager uiManager =
+ (UiModeManager) getContext().getSystemService(Context.UI_MODE_SERVICE);
+ if (uiManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
+ return waitForAssistantHomeButton();
+ } else {
+ return waitForNavigationUiObject("home");
+ }
+ }
+
+ /* Assistant Home button is present when system is in car mode. */
+ @NonNull
+ UiObject2 waitForAssistantHomeButton() {
+ final UiObject2 object = mDevice.wait(
+ Until.findObject(By.res(ASSISTANT_PACKAGE, ASSISTANT_GO_HOME_RES_ID)),
+ WAIT_TIME_MS);
+ assertNotNull(
+ "Can't find an assistant UI object with id: " + ASSISTANT_GO_HOME_RES_ID, object);
+ return object;
+ }
+
+ @NonNull
UiObject2 waitForNavigationUiObject(String resId) {
String resPackage = getNavigationButtonResPackage();
final UiObject2 object = mDevice.wait(
@@ -1268,6 +1380,16 @@ public final class LauncherInstrumentation {
}
}
+ void waitForObjectFocused(UiObject2 object, String waitReason) {
+ try {
+ assertTrue("Timed out waiting for object to be focused for " + waitReason + " "
+ + object.getResourceName(),
+ object.wait(Until.focused(true), WAIT_TIME_MS));
+ } catch (StaleObjectException e) {
+ fail("The object disappeared from screen");
+ }
+ }
+
@NonNull
UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
return waitForObjectsInContainer(container, selector).get(0);
@@ -1516,8 +1638,11 @@ public final class LauncherInstrumentation {
scroll(
container,
Direction.LEFT,
- new Rect(leftGestureMargin, 0,
- containerRect.width() - distance - rightGestureMarginInContainer, 0),
+ new Rect(leftGestureMargin,
+ 0,
+ Math.max(containerRect.width() - distance - leftGestureMargin,
+ rightGestureMarginInContainer),
+ 0),
10,
true);
}
@@ -1690,7 +1815,7 @@ public final class LauncherInstrumentation {
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
- boolean isGridOnlyOverviewEnabled() {
+ public boolean isGridOnlyOverviewEnabled() {
return getTestInfo(TestProtocol.REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW).getBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@@ -1698,11 +1823,21 @@ public final class LauncherInstrumentation {
public void sendPointer(long downTime, long currentTime, int action, Point point,
GestureScope gestureScope) {
sendPointer(downTime, currentTime, action, point, gestureScope,
- InputDevice.SOURCE_TOUCHSCREEN);
+ InputDevice.SOURCE_TOUCHSCREEN, false);
+ }
+
+ private void injectEvent(InputEvent event) {
+ assertTrue("injectInputEvent failed: event=" + event,
+ mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
}
public void sendPointer(long downTime, long currentTime, int action, Point point,
GestureScope gestureScope, int source) {
+ sendPointer(downTime, currentTime, action, point, gestureScope, source, false);
+ }
+
+ public void sendPointer(long downTime, long currentTime, int action, Point point,
+ GestureScope gestureScope, int source, boolean isRightClick) {
final boolean hasTIS = hasTIS();
int pointerCount = mPointerCount;
@@ -1731,16 +1866,49 @@ public final class LauncherInstrumentation {
final MotionEvent event = isTrackpadGesture
? getTrackpadMotionEvent(
- downTime, currentTime, action, point.x, point.y, pointerCount,
- mTrackpadGestureType)
+ downTime, currentTime, action, point.x, point.y, pointerCount,
+ mTrackpadGestureType)
: getMotionEvent(downTime, currentTime, action, point.x, point.y, source);
if (action == MotionEvent.ACTION_BUTTON_PRESS
|| action == MotionEvent.ACTION_BUTTON_RELEASE) {
event.setActionButton(MotionEvent.BUTTON_PRIMARY);
}
- assertTrue("injectInputEvent failed",
- mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
- event.recycle();
+ if (isRightClick) {
+ event.setButtonState(event.getButtonState() & MotionEvent.BUTTON_SECONDARY);
+ }
+ injectEvent(event);
+ }
+
+ private KeyEvent createKeyEvent(int keyCode, int metaState, boolean actionDown) {
+ long eventTime = SystemClock.uptimeMillis();
+ return KeyEvent.obtain(
+ eventTime,
+ eventTime,
+ actionDown ? ACTION_DOWN : ACTION_UP,
+ keyCode,
+ /* repeat= */ 0,
+ metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ /* scancode= */ 0,
+ /* flags= */ 0,
+ InputDevice.SOURCE_KEYBOARD,
+ /* characters =*/ null);
+ }
+
+ /**
+ * Sends a {@link KeyEvent} with {@link ACTION_DOWN} for the given key codes without sending
+ * a {@link KeyEvent} with {@link ACTION_UP}.
+ */
+ public void pressAndHoldKeyCode(int keyCode, int metaState) {
+ injectEvent(createKeyEvent(keyCode, metaState, true));
+ }
+
+
+ /**
+ * Sends a {@link KeyEvent} with {@link ACTION_UP} for the given key codes.
+ */
+ public void unpressKeyCode(int keyCode, int metaState) {
+ injectEvent(createKeyEvent(keyCode, metaState, false));
}
public long movePointer(long downTime, long startTime, long duration, Point from, Point to,
@@ -1814,6 +1982,22 @@ public final class LauncherInstrumentation {
return result;
}
+ @NonNull
+ UiObject2 rightClickAndGet(
+ @NonNull final UiObject2 target, @NonNull String resName, Pattern rightClickEvent) {
+ final Point targetCenter = target.getVisibleCenter();
+ final long downTime = SystemClock.uptimeMillis();
+ sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
+ /* isRightClick= */ true);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, rightClickEvent);
+ final UiObject2 result = waitForLauncherObject(resName);
+ sendPointer(downTime, SystemClock.uptimeMillis(), ACTION_UP, targetCenter,
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
+ /* isRightClick= */ true);
+ return result;
+ }
+
private static int getSystemIntegerRes(Context context, String resName) {
Resources res = context.getResources();
int resId = res.getIdentifier(resName, "integer", "android");
@@ -1854,6 +2038,7 @@ public final class LauncherInstrumentation {
}
}
+ /** Returns the bounds of the display as a Point where x is width and y is height. */
Point getRealDisplaySize() {
final Rect displayBounds = getContext().getSystemService(WindowManager.class)
.getMaximumWindowMetrics()
@@ -1916,6 +2101,11 @@ public final class LauncherInstrumentation {
: TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT);
}
+ public boolean isTransientTaskbar() {
+ return getTestInfo(TestProtocol.REQUEST_IS_TRANSIENT_TASKBAR)
+ .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
/** Enables transient taskbar for testing purposes only. */
public void enableTransientTaskbar(boolean enable) {
getTestInfo(enable
@@ -1932,6 +2122,13 @@ public final class LauncherInstrumentation {
getTestInfo(TestProtocol.REQUEST_RECREATE_TASKBAR);
}
+ // TODO(b/270393900): Remove with ENABLE_ALL_APPS_SEARCH_IN_TASKBAR flag cleanup.
+
+ /** Refreshes the known overview target in TIS. */
+ public void refreshOverviewTarget() {
+ getTestInfo(TestProtocol.REQUEST_REFRESH_OVERVIEW_TARGET);
+ }
+
public List<String> getHotseatIconNames() {
return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
.getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -1946,14 +2143,16 @@ public final class LauncherInstrumentation {
return String.join(", ", getActivities());
}
- public boolean noLeakedActivities() {
+ /** Returns whether no leaked activities are detected. */
+ public boolean noLeakedActivities(boolean requireOneActiveActivity) {
final String[] activities = getActivities();
+
for (String activity : activities) {
if (activity.contains("(destroyed)")) {
return false;
}
}
- return activities.length <= 2;
+ return activities.length <= (requireOneActiveActivity ? 1 : 2);
}
public int getActivitiesCreated() {
@@ -1994,7 +2193,8 @@ public final class LauncherInstrumentation {
};
}
- boolean isLauncher3() {
+ /** Returns whether the Launcher is a Launcher3 one */
+ public boolean isLauncher3() {
if (mIsLauncher3 == null) {
mIsLauncher3 = "com.android.launcher3".equals(getLauncherPackageName());
}
@@ -2086,7 +2286,13 @@ public final class LauncherInstrumentation {
? containerBounds.right + 1
: containerBounds.left - 1;
}
- int y = containerBounds.top + containerBounds.height() / 2;
+ // If IME is visible and overlaps the container bounds, touch above it.
+ int bottomBound = Math.min(
+ containerBounds.bottom,
+ getRealDisplaySize().y - getImeInsets().bottom);
+ int y = (bottomBound + containerBounds.top) / 2;
+ // Do not tap in the status bar.
+ y = Math.max(y, getWindowInsets().top);
final long downTime = SystemClock.uptimeMillis();
final Point tapTarget = new Point(x, y);
@@ -2113,4 +2319,14 @@ public final class LauncherInstrumentation {
}
return result;
}
+
+ /** Executes a runnable and waits for the wallpaper-open animation completion. */
+ public void executeAndWaitForWallpaperAnimation(Runnable r, String actionName) {
+ executeAndWaitForLauncherEvent(
+ () -> r.run(),
+ event -> TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Didn't detect finishing wallpaper-open animation",
+ actionName);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index e4cfc52a2c..f383e99584 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,8 +16,6 @@
package com.android.launcher3.tapl;
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
import android.graphics.Rect;
import androidx.annotation.NonNull;
@@ -36,6 +34,8 @@ import java.util.stream.Collectors;
*/
public final class OverviewTask {
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+ private static final String TASK_SNAPSHOT_1 = "snapshot";
+ private static final String TASK_SNAPSHOT_2 = "bottomright_snapshot";
static final Pattern TASK_START_EVENT = Pattern.compile("startActivityFromRecentsAsync");
static final Pattern SPLIT_SELECT_EVENT = Pattern.compile("enterSplitSelect");
@@ -55,20 +55,75 @@ public final class OverviewTask {
mOverview.verifyActiveContainer();
}
+ /**
+ * Returns the height of the visible task, or the combined height of two tasks in split with a
+ * divider between.
+ */
int getVisibleHeight() {
+ if (isTaskSplit()) {
+ return getCombinedSplitTaskHeight();
+ }
+
return mTask.getVisibleBounds().height();
}
+ /**
+ * Calculates the visible height for split tasks, containing 2 snapshot tiles and a divider.
+ */
+ private int getCombinedSplitTaskHeight() {
+ UiObject2 taskSnapshot1 = findObjectInTask(TASK_SNAPSHOT_1);
+ UiObject2 taskSnapshot2 = findObjectInTask(TASK_SNAPSHOT_2);
+
+ // If the split task is partly off screen, taskSnapshot1 can be invisible.
+ if (taskSnapshot1 == null) {
+ return taskSnapshot2.getVisibleBounds().height();
+ }
+
+ int top = Math.min(
+ taskSnapshot1.getVisibleBounds().top, taskSnapshot2.getVisibleBounds().top);
+ int bottom = Math.max(
+ taskSnapshot1.getVisibleBounds().bottom, taskSnapshot2.getVisibleBounds().bottom);
+
+ return bottom - top;
+ }
+
+ /**
+ * Returns the width of the visible task, or the combined width of two tasks in split with a
+ * divider between.
+ */
int getVisibleWidth() {
+ if (isTaskSplit()) {
+ return getCombinedSplitTaskWidth();
+ }
+
return mTask.getVisibleBounds().width();
}
+ /**
+ * Calculates the visible width for split tasks, containing 2 snapshot tiles and a divider.
+ */
+ private int getCombinedSplitTaskWidth() {
+ UiObject2 taskSnapshot1 = findObjectInTask(TASK_SNAPSHOT_1);
+ UiObject2 taskSnapshot2 = findObjectInTask(TASK_SNAPSHOT_2);
+
+ int left = Math.min(
+ taskSnapshot1.getVisibleBounds().left, taskSnapshot2.getVisibleBounds().left);
+ int right = Math.max(
+ taskSnapshot1.getVisibleBounds().right, taskSnapshot2.getVisibleBounds().right);
+
+ return right - left;
+ }
+
int getTaskCenterX() {
- return mTask.getVisibleCenter().x;
+ return mTask.getParent().getVisibleCenter().x;
+ }
+
+ int getTaskCenterY() {
+ return mTask.getParent().getVisibleCenter().y;
}
float getExactCenterX() {
- return mTask.getVisibleBounds().exactCenterX();
+ return mTask.getParent().getVisibleBounds().exactCenterX();
}
UiObject2 getUiObject() {
@@ -92,7 +147,8 @@ public final class OverviewTask {
boolean taskWasFocused = mLauncher.isTablet() && getVisibleHeight() == mLauncher
.getFocusedTaskHeightForTablet();
- List<Integer> originalTasksCenterX = getCurrentTasksCenterXList();
+ List<Integer> originalTasksCenterX =
+ getCurrentTasksCenterXList().stream().sorted().toList();
boolean isClearAllVisibleBeforeDismiss = mOverview.isClearAllVisible();
dismissBySwipingUp();
@@ -103,7 +159,8 @@ public final class OverviewTask {
mOverview.getFocusedTaskForTablet());
}
if (!isClearAllVisibleBeforeDismiss) {
- List<Integer> currentTasksCenterX = getCurrentTasksCenterXList();
+ List<Integer> currentTasksCenterX =
+ getCurrentTasksCenterXList().stream().sorted().toList();
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
@@ -121,7 +178,7 @@ public final class OverviewTask {
// Dismiss the task via flinging it up.
final Rect taskBounds = mLauncher.getVisibleBounds(mTask);
final int centerX = taskBounds.centerX();
- final int centerY = taskBounds.centerY();
+ final int centerY = taskBounds.bottom - 1;
mLauncher.executeAndWaitForLauncherEvent(
() -> mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false,
LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER),
@@ -133,8 +190,8 @@ public final class OverviewTask {
private List<Integer> getCurrentTasksCenterXList() {
return mLauncher.isTablet()
? mOverview.getCurrentTasksForTablet().stream()
- .map(OverviewTask::getTaskCenterX)
- .collect(Collectors.toList())
+ .map(OverviewTask::getTaskCenterX)
+ .collect(Collectors.toList())
: List.of(mOverview.getCurrentTask().getTaskCenterX());
}
@@ -144,11 +201,8 @@ public final class OverviewTask {
public LaunchedAppState open() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
verifyActiveContainer();
- mLauncher.executeAndWaitForEvent(
+ mLauncher.executeAndWaitForLauncherStop(
() -> mLauncher.clickLauncherObject(mTask),
- event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
- () -> "Launching task didn't open a new window: "
- + mTask.getParent().getContentDescription(),
"clicking an overview task");
if (mOverview.getContainerType()
== LauncherInstrumentation.ContainerType.SPLIT_SCREEN_SELECT) {
@@ -168,7 +222,7 @@ public final class OverviewTask {
}
}
- /** Taps the task menu. */
+ /** Taps the task menu. Returns the task menu object. */
@NonNull
public OverviewTaskMenu tapMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
@@ -184,7 +238,27 @@ public final class OverviewTask {
}
}
+ /** Taps the task menu of the split task. Returns the split task's menu object. */
+ @NonNull
+ public OverviewTaskMenu tapSplitTaskMenu() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to tap the split task's menu")) {
+ mLauncher.clickLauncherObject(
+ mLauncher.waitForObjectInContainer(mTask.getParent(), "bottomRight_icon"));
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "tapped the split task's menu")) {
+ return new OverviewTaskMenu(mLauncher);
+ }
+ }
+ }
+
boolean isTaskSplit() {
- return mLauncher.findObjectInContainer(mTask.getParent(), "bottomright_snapshot") != null;
+ return findObjectInTask(TASK_SNAPSHOT_2) != null;
+ }
+
+ private UiObject2 findObjectInTask(String resName) {
+ return mTask.getParent().findObject(mLauncher.getOverviewObjectSelector(resName));
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 7c29a6cf87..3d2914d754 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -50,15 +50,19 @@ public class OverviewTaskMenu {
}
}
- /** Taps the app info item from the overview task menu and returns the LaunchedAppState
- * representing the App info settings page. */
+ /**
+ * Taps the app info item from the overview task menu and returns the LaunchedAppState
+ * representing the App info settings page.
+ */
@NonNull
public LaunchedAppState tapAppInfoMenuItem() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"before tapping the app info menu item")) {
- mLauncher.clickLauncherObject(
- mLauncher.findObjectInContainer(mMenu, By.text("App info")));
+ mLauncher.executeAndWaitForLauncherStop(
+ () -> mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.text("App info"))),
+ "tapped app info menu item");
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"tapped app info menu item")) {
@@ -81,4 +85,11 @@ public class OverviewTaskMenu {
return new OverviewTaskMenuItem(mLauncher,
mLauncher.waitForObjectInContainer(mMenu, By.text(menuItemName)));
}
+
+ /**
+ * Taps outside task menu to dismiss it.
+ */
+ public void touchOutsideTaskMenuToDismiss() {
+ mLauncher.touchOutsideContainer(mMenu, false);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
index 7f3f61d81f..fe2a63dd57 100644
--- a/tests/tapl/com/android/launcher3/tapl/Qsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -22,13 +22,18 @@ import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
+import java.util.regex.Pattern;
+
/**
* Operations on qsb from either Home screen or AllApp screen.
*/
-public abstract class Qsb {
+public abstract class Qsb implements SearchInputSource {
private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+ private static final String LENS_ICON_RES_ID = "lens_icon";
+ private static final Pattern LENS_APP_RES_PATTERN = Pattern.compile(
+ ASSISTANT_APP_PACKAGE + ":id/lens.*");
protected final LauncherInstrumentation mLauncher;
private final UiObject2 mContainer;
private final String mQsbResName;
@@ -76,6 +81,37 @@ public abstract class Qsb {
}
/**
+ * Launches lens app by tapping lens icon on qsb.
+ */
+ @NonNull
+ public LaunchedAppState launchLens() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click lens icon button");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ UiObject2 lensIcon = mLauncher.waitForLauncherObject(LENS_ICON_RES_ID);
+
+ LauncherInstrumentation.log("Qsb.launchLens before click "
+ + lensIcon.getVisibleCenter() + " in "
+ + mLauncher.getVisibleBounds(lensIcon));
+
+ mLauncher.clickLauncherObject(lensIcon);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ // Package name is not enough to check if the app is launched, because many
+ // elements are having googlequicksearchbox as package name. So it checks if the
+ // corresponding app resource is displayed
+ BySelector selector = By.res(LENS_APP_RES_PATTERN);
+ mLauncher.assertTrue(
+ "Lens app didn't start: (" + selector + ")",
+ mLauncher.getDevice().wait(Until.hasObject(selector),
+ LauncherInstrumentation.WAIT_TIME_MS)
+ );
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
+ /**
* Show search result page from tapping qsb.
*/
public SearchResultFromQsb showSearchResult() {
@@ -92,6 +128,16 @@ public abstract class Qsb {
}
}
+ @Override
+ public LauncherInstrumentation getLauncher() {
+ return mLauncher;
+ }
+
+ @Override
+ public SearchResultFromQsb getSearchResultForInput() {
+ return createSearchResult();
+ }
+
protected SearchResultFromQsb createSearchResult() {
return new SearchResultFromQsb(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchInputSource.java b/tests/tapl/com/android/launcher3/tapl/SearchInputSource.java
new file mode 100644
index 0000000000..032948f851
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SearchInputSource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * Container that can be used to input a search query and retrieve a {@link SearchResultFromQsb}
+ * instance.
+ */
+interface SearchInputSource {
+ String INPUT_RES = "input";
+
+ /** Set the already focused search input edit text and update search results. */
+ default SearchResultFromQsb searchForInput(String input) {
+ LauncherInstrumentation launcher = getLauncher();
+ try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
+ "want to search for result with an input");
+ LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
+ launcher.executeAndWaitForLauncherEvent(
+ () -> {
+ UiObject2 editText = launcher.waitForLauncherObject(INPUT_RES);
+ launcher.waitForObjectFocused(editText, "search input");
+ editText.setText(input);
+ },
+ event -> TestProtocol.SEARCH_RESULT_COMPLETE.equals(event.getClassName()),
+ () -> "Didn't receive a search result completed message", "searching");
+ return getSearchResultForInput();
+ }
+ }
+
+ /** This method requires public access, however should not be called in tests. */
+ LauncherInstrumentation getLauncher();
+
+ /** This method requires public access, however should not be called in tests. */
+ SearchResultFromQsb getSearchResultForInput();
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index d02e747900..f0a8aa26c0 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -25,9 +25,7 @@ import java.util.ArrayList;
/**
* Operations on search result page opened from qsb.
*/
-public class SearchResultFromQsb {
- // The input resource id in the search box.
- private static final String INPUT_RES = "input";
+public class SearchResultFromQsb implements SearchInputSource {
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
// This particular ID change should happen with caution
@@ -39,15 +37,6 @@ public class SearchResultFromQsb {
mLauncher.waitForLauncherObject("search_container_all_apps");
}
- /** Set the input to the search input edit text and update search results. */
- public void searchForInput(String input) {
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to search for result with an input");
- LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.waitForLauncherObject(INPUT_RES).setText(input);
- }
- }
-
/** Find the app from search results with app name. */
public AppIcon findAppIcon(String appName) {
UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName));
@@ -91,7 +80,7 @@ public class SearchResultFromQsb {
* Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
* @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
*/
- public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
+ public void dismissByTappingOutsideForTablet(boolean tapRight) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to tap outside AllApps bottom sheet on the "
@@ -101,8 +90,22 @@ public class SearchResultFromQsb {
mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
"tapped outside AllApps bottom sheet")) {
- return mLauncher.getWorkspace();
+ verifyVisibleContainerOnDismiss();
}
}
}
+
+ protected void verifyVisibleContainerOnDismiss() {
+ mLauncher.getWorkspace();
+ }
+
+ @Override
+ public LauncherInstrumentation getLauncher() {
+ return mLauncher;
+ }
+
+ @Override
+ public SearchResultFromQsb getSearchResultForInput() {
+ return this;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
index 6c6ab05ac3..00291a3381 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
@@ -45,4 +45,9 @@ public class SearchResultFromTaskbarQsb extends SearchResultFromQsb {
protected TaskbarSearchWebSuggestion createWebSuggestion(UiObject2 webSuggestion) {
return new TaskbarSearchWebSuggestion(mLauncher, webSuggestion);
}
+
+ @Override
+ protected void verifyVisibleContainerOnDismiss() {
+ mLauncher.getLaunchedAppState().assertTaskbarVisible();
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
index ce1c3c0e9d..2870877f93 100644
--- a/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.tapl;
-/** Launchable that can serve as a source for dragging and dropping to splitscreen. */
+/** {@link Launchable} that can serve as a source for dragging and dropping to splitscreen. */
interface SplitscreenDragSource {
/**
@@ -35,5 +35,6 @@ interface SplitscreenDragSource {
}
}
+ /** This method requires public access, however should not be called in tests. */
Launchable getLaunchable();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index 8671738fe7..a202c53375 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -15,9 +15,9 @@
*/
package com.android.launcher3.tapl;
+import static android.view.KeyEvent.KEYCODE_META_RIGHT;
+
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import android.graphics.Point;
import android.graphics.Rect;
@@ -31,6 +31,8 @@ import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
+import org.junit.Assert;
+
import java.util.List;
import java.util.stream.Collectors;
@@ -43,6 +45,15 @@ public final class Taskbar {
Taskbar(LauncherInstrumentation launcher) {
mLauncher = launcher;
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "expect new taskbar to be visible")) {
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
+ }
+
+ if (!mLauncher.isTransientTaskbar()) {
+ Assert.assertEquals("Persistent taskbar should fill screen width",
+ getVisibleBounds().width(), mLauncher.getRealDisplaySize().x);
+ }
}
/**
@@ -59,40 +70,39 @@ public final class Taskbar {
}
/**
- * Hides this taskbar.
- *
- * The taskbar must already be visible when calling this method.
+ * Stashes this taskbar.
+ * <p>
+ * The taskbar must already be unstashed and in transient mode when calling this method.
*/
- public void hide() {
- mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+ public void swipeDownToStash() {
+ mLauncher.assertTrue("Taskbar is not transient, swipe down not supported",
+ mLauncher.isTransientTaskbar());
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to hide the taskbar");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
- final long downTime = SystemClock.uptimeMillis();
- Point stashTarget = new Point(
- mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
+ Rect taskbarBounds = getVisibleBounds();
+ int startX = taskbarBounds.centerX();
+ int startY = taskbarBounds.centerY();
+ int endX = startX;
+ int endY = mLauncher.getRealDisplaySize().y - 1;
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
+ mLauncher.linearGesture(startX, startY, endX, endY, 10, false,
LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- LauncherInstrumentation.log("hideTaskbar: sent down");
-
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+ LauncherInstrumentation.log("swipeDownToStash: sent linear swipe down gesture");
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "expect transient taskbar to be hidden after swipe down")) {
mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
- LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
}
- } finally {
- mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
}
}
/**
* Opens the Taskbar all apps page.
*/
- public AllAppsFromTaskbar openAllApps() {
+ public TaskbarAllApps openAllApps() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to open taskbar all apps");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
@@ -101,7 +111,26 @@ public final class Taskbar {
mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
getAllAppsButtonSelector()));
- return new AllAppsFromTaskbar(mLauncher);
+ return getAllApps();
+ }
+ }
+
+ /** Opens the Taskbar all apps page with the meta keyboard shortcut. */
+ public TaskbarAllApps openAllAppsFromKeyboardShortcut() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.getDevice().pressKeyCode(KEYCODE_META_RIGHT);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "pressed meta key")) {
+ return getAllApps();
+ }
+ }
+ }
+
+ /** Returns {@link TaskbarAllApps} if it is open, otherwise fails. */
+ public TaskbarAllApps getAllApps() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get taskbar all apps object")) {
+ return new TaskbarAllApps(mLauncher);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java
index 0e0291f82c..3d39041124 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java
@@ -23,9 +23,9 @@ import com.android.launcher3.testing.shared.TestProtocol;
/**
* Operations on AllApps opened from the Taskbar.
*/
-public class AllAppsFromTaskbar extends AllApps {
+public class TaskbarAllApps extends AllApps {
- AllAppsFromTaskbar(LauncherInstrumentation launcher) {
+ TaskbarAllApps(LauncherInstrumentation launcher) {
super(launcher);
}
@@ -68,4 +68,14 @@ public class AllAppsFromTaskbar extends AllApps {
public TaskbarAllAppsQsb getQsb() {
return new TaskbarAllAppsQsb(mLauncher, verifyActiveContainer());
}
+
+ @Override
+ protected void verifyVisibleContainerOnDismiss() {
+ mLauncher.getLaunchedAppState().assertTaskbarVisible();
+ }
+
+ @Override
+ public boolean isHomeState() {
+ return false;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
index 099acd4716..064f80cfcc 100644
--- a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSource {
private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+ private static final Pattern RIGHT_CLICK_EVENT = Pattern.compile("onTaskbarItemRightClick");
TaskbarAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
@@ -35,11 +36,26 @@ public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSour
return LONG_CLICK_EVENT;
}
+ protected Pattern getRightClickEvent() {
+ return RIGHT_CLICK_EVENT;
+ }
+
@Override
public TaskbarAppIconMenu openDeepShortcutMenu() {
return (TaskbarAppIconMenu) super.openDeepShortcutMenu();
}
+ /**
+ * Right-clicks the icon to open its menu.
+ */
+ public TaskbarAppIconMenu openDeepShortcutMenuWithRightClick() {
+ try (LauncherInstrumentation.Closable e = mLauncher.addContextLayer(
+ "want to return the shortcut menu when icon is right-clicked.")) {
+ return createMenu(mLauncher.rightClickAndGet(
+ mObject, /* resName= */ "deep_shortcuts_container", getRightClickEvent()));
+ }
+ }
+
@Override
protected TaskbarAppIconMenu createMenu(UiObject2 menu) {
return new TaskbarAppIconMenu(mLauncher, menu);
@@ -49,4 +65,9 @@ public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSour
public Launchable getLaunchable() {
return this;
}
+
+ @Override
+ protected boolean launcherStopsAfterLaunch() {
+ return false;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
index 424c58e260..e6fdda03cb 100644
--- a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
@@ -54,4 +54,9 @@ public final class TaskbarAppIconMenuItem extends AppIconMenuItem implements Spl
public Launchable getLaunchable() {
return this;
}
+
+ @Override
+ protected boolean launcherStopsAfterLaunch() {
+ return false;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java b/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java
index cd8ce42f49..f25af09d9e 100644
--- a/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java
@@ -42,4 +42,9 @@ public class TaskbarSearchWebSuggestion extends SearchWebSuggestion implements
public Launchable getLaunchable() {
return this;
}
+
+ @Override
+ protected boolean launcherStopsAfterLaunch() {
+ return false;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
index 8f51d04c3d..ec1cbd8ec1 100644
--- a/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
+++ b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
@@ -15,6 +15,16 @@
*/
package com.android.launcher3.tapl;
+import static com.android.launcher3.tapl.Launchable.DEFAULT_DRAG_STEPS;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.test.uiautomator.UiObject2;
+
/** The resize frame that is shown for a widget on the workspace. */
public class WidgetResizeFrame {
@@ -34,4 +44,36 @@ public class WidgetResizeFrame {
mLauncher.getDevice().pressHome();
}
}
+
+ /** Resizes the widget to double its height, and returns the resize frame. */
+ public WidgetResizeFrame resize() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to resize the widget frame.")) {
+ UiObject2 widget = mLauncher.waitForLauncherObject("widget_resize_frame");
+ UiObject2 bottomResizeHandle =
+ mLauncher.waitForLauncherObject("widget_resize_bottom_handle");
+ Rect originalWidgetSize = widget.getVisibleBounds();
+ Point targetStart = bottomResizeHandle.getVisibleCenter();
+ Point targetDest = bottomResizeHandle.getVisibleCenter();
+ targetDest.offset(0, originalWidgetSize.height());
+
+ final long downTime = SystemClock.uptimeMillis();
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetStart,
+ LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+ mLauncher.movePointer(targetStart, targetDest, DEFAULT_DRAG_STEPS,
+ true, downTime, downTime, true,
+ LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, targetDest,
+ LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "want to return resized widget resize frame")) {
+ float newHeight = mLauncher.waitForLauncherObject(
+ "widget_resize_frame").getVisibleBounds().height();
+ assertTrue("Widget not resized.", newHeight >= originalWidgetSize.height() * 2);
+ return this;
+ }
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 79b54ba0a3..105bc3bf50 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -34,7 +34,8 @@ import java.util.Collection;
/**
* All widgets container.
*/
-public final class Widgets extends LauncherInstrumentation.VisibleContainer {
+public final class Widgets extends LauncherInstrumentation.VisibleContainer
+ implements KeyboardQuickSwitchSource {
private static final int FLING_STEPS = 10;
private static final int SCROLL_ATTEMPTS = 60;
@@ -43,6 +44,21 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
verifyActiveContainer();
}
+ @Override
+ public LauncherInstrumentation getLauncher() {
+ return mLauncher;
+ }
+
+ @Override
+ public LauncherInstrumentation.ContainerType getStartingContainerType() {
+ return getContainerType();
+ }
+
+ @Override
+ public boolean isHomeState() {
+ return true;
+ }
+
/**
* Flings forward (down) and waits the fling's end.
*/
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 6f6f2926b7..f8fa00c365 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -16,6 +16,7 @@
package com.android.launcher3.tapl;
+import static android.view.KeyEvent.KEYCODE_META_RIGHT;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
@@ -114,6 +115,20 @@ public final class Workspace extends Home {
}
}
+ /** Opens the Launcher all apps page with the meta keyboard shortcut. */
+ public HomeAllApps openAllAppsFromKeyboardShortcut() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to open all apps search")) {
+ verifyActiveContainer();
+ mLauncher.getDevice().pressKeyCode(KEYCODE_META_RIGHT);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "pressed meta key")) {
+ return new HomeAllApps(mLauncher);
+ }
+ }
+ }
+
/**
* Returns the home qsb.
*
@@ -320,7 +335,8 @@ public final class Workspace extends Home {
homeAppIcon,
() -> new Point(0, 0),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
- null);
+ null,
+ /* startsActivity = */ false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dragged the app across workspace")) {
@@ -344,7 +360,8 @@ public final class Workspace extends Home {
homeAppIcon,
() -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
- /* expectDropEvents= */ null);
+ /* expectDropEvents= */ null,
+ /* startsActivity = */ false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dragged the app to the drop bar")) {
@@ -353,7 +370,6 @@ public final class Workspace extends Home {
}
}
-
/**
* Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
*
@@ -375,7 +391,8 @@ public final class Workspace extends Home {
homeAppIcon,
() -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
expectLongClickEvents,
- /* expectDropEvents= */null);
+ /* expectDropEvents= */null,
+ /* startsActivity = */ false);
launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
@@ -449,14 +466,25 @@ public final class Workspace extends Home {
o -> new FolderIcon(mLauncher, o)).collect(Collectors.toList());
}
+ private static void sendUp(LauncherInstrumentation launcher, Point dest,
+ long downTime) {
+ launcher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
+ LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+ }
+
private static void dropDraggedIcon(LauncherInstrumentation launcher, Point dest, long downTime,
- @Nullable Runnable expectedEvents) {
- launcher.runToState(
- () -> launcher.sendPointer(
- downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
- LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER),
- NORMAL_STATE_ORDINAL,
- "sending UP event");
+ @Nullable Runnable expectedEvents, boolean startsActivity) {
+ if (startsActivity) {
+ launcher.executeAndWaitForLauncherStop(
+ () -> sendUp(launcher, dest, downTime),
+ "sending UP event");
+ } else {
+ launcher.runToState(
+ () -> sendUp(launcher, dest, downTime),
+ NORMAL_STATE_ORDINAL,
+ "sending UP event");
+ }
if (expectedEvents != null) {
expectedEvents.run();
}
@@ -473,7 +501,8 @@ public final class Workspace extends Home {
LauncherInstrumentation.EVENT_START);
}
dragIconToWorkspace(
- launcher, launchable, dest, expectLongClickEvents, expectDropEvents);
+ launcher, launchable, dest, expectLongClickEvents, expectDropEvents,
+ startsActivity);
}
static void dragIconToWorkspaceCellPosition(LauncherInstrumentation launcher,
@@ -502,7 +531,8 @@ public final class Workspace extends Home {
destSupplier,
/* isDecelerating= */ false,
() -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
- /* expectDropEvents= */ null);
+ /* expectDropEvents= */ null,
+ /* startsActivity = */ false);
}
static void dragIconToWorkspace(
@@ -510,9 +540,10 @@ public final class Workspace extends Home {
Launchable launchable,
Supplier<Point> dest,
Runnable expectLongClickEvents,
- @Nullable Runnable expectDropEvents) {
+ @Nullable Runnable expectDropEvents,
+ boolean startsActivity) {
dragIconToWorkspace(launcher, launchable, dest, /* isDecelerating */ true,
- expectLongClickEvents, expectDropEvents);
+ expectLongClickEvents, expectDropEvents, startsActivity);
}
static void dragIconToWorkspace(
@@ -521,7 +552,8 @@ public final class Workspace extends Home {
Supplier<Point> dest,
boolean isDecelerating,
Runnable expectLongClickEvents,
- @Nullable Runnable expectDropEvents) {
+ @Nullable Runnable expectDropEvents,
+ boolean startsActivity) {
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
"want to drag icon to workspace")) {
final long downTime = SystemClock.uptimeMillis();
@@ -553,7 +585,7 @@ public final class Workspace extends Home {
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
+ dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, startsActivity);
}
}
@@ -586,7 +618,8 @@ public final class Workspace extends Home {
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
+ dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents,
+ /* startsActivity = */ false);
}
}
@@ -652,7 +685,8 @@ public final class Workspace extends Home {
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
+ dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents,
+ /* startsActivity = */ false);
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
index 141476c782..e5a2a2ef6c 100644
--- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
@@ -19,7 +19,7 @@ import android.graphics.Point;
import java.util.function.Supplier;
-/** Launchable that can serve as a source for dragging and dropping to the workspace. */
+/** {@link Launchable} that can serve as a source for dragging and dropping to the workspace. */
interface WorkspaceDragSource {
/**
@@ -81,7 +81,8 @@ interface WorkspaceDragSource {
launchable,
dest,
launchable::addExpectedEventsForLongClick,
- /*expectDropEvents= */ null);
+ /*expectDropEvents= */ null,
+ /* startsActivity = */ false);
try (LauncherInstrumentation.Closable ignore = launcher.addContextLayer("dragged")) {
WorkspaceAppIcon appIcon =