diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java new file mode 100644 index 0000000000..74f37a493c --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java @@ -0,0 +1,187 @@ +/* + * 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.quickstep; + +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.tapl.KeyboardQuickSwitch; +import com.android.launcher3.ui.TaplTestsLauncher3; + +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class TaplTestsKeyboardQuickSwitch extends AbstractQuickStepTest { + + private enum TestSurface { + HOME, LAUNCHED_APP, HOME_ALL_APPS, WIDGETS, + } + + private enum TestCase { + DISMISS(0), + LAUNCH_LAST_APP(0), + LAUNCH_SELECTED_APP(1), + LAUNCH_OVERVIEW(5); + + private final int mNumAdditionalRunningTasks; + + TestCase(int numAdditionalRunningTasks) { + mNumAdditionalRunningTasks = numAdditionalRunningTasks; + } + } + + private static final String CALCULATOR_APP_PACKAGE = + resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); + + @Override + public void setUp() throws Exception { + Assume.assumeTrue(mLauncher.isTablet()); + super.setUp(); + TaplTestsLauncher3.initialize(this); + startAppFast(CALCULATOR_APP_PACKAGE); + startTestActivity(2); + } + + @Test + public void testDismiss_fromHome() { + runTest(TestSurface.HOME, TestCase.DISMISS); + } + + @Test + public void testDismiss_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.DISMISS); + } + + @Test + public void testDismiss_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.DISMISS); + } + + @Test + public void testDismiss_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.DISMISS); + } + + @Test + public void testLaunchLastTask_fromHome() { + runTest(TestSurface.HOME, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchLastTask_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchLastTask_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchLastTask_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.LAUNCH_LAST_APP); + } + + @Test + public void testLaunchSelectedTask_fromHome() { + runTest(TestSurface.HOME, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchSelectedTask_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchSelectedTask_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchSelectedTask_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.LAUNCH_SELECTED_APP); + } + + @Test + public void testLaunchOverviewTask_fromHome() { + runTest(TestSurface.HOME, TestCase.LAUNCH_OVERVIEW); + } + + @Test + public void testLaunchOverviewTask_fromApp() { + runTest(TestSurface.LAUNCHED_APP, TestCase.LAUNCH_OVERVIEW); + } + + @Test + public void testLaunchOverviewTask_fromHomeAllApps() { + runTest(TestSurface.HOME_ALL_APPS, TestCase.LAUNCH_OVERVIEW); + } + + @Test + public void testLaunchOverviewTask_fromWidgets() { + runTest(TestSurface.WIDGETS, TestCase.LAUNCH_OVERVIEW); + } + + private void runTest(@NonNull TestSurface testSurface, @NonNull TestCase testCase) { + for (int i = 0; i < testCase.mNumAdditionalRunningTasks; i++) { + startTestActivity(3 + i); + } + + KeyboardQuickSwitch kqs; + switch (testSurface) { + case HOME: + kqs = mLauncher.goHome().showQuickSwitchView(); + break; + case LAUNCHED_APP: + mLauncher.setIgnoreTaskbarVisibility(true); + kqs = mLauncher.getLaunchedAppState().showQuickSwitchView(); + break; + case HOME_ALL_APPS: + kqs = mLauncher.goHome().switchToAllApps().showQuickSwitchView(); + break; + case WIDGETS: + kqs = mLauncher.goHome().openAllWidgets().showQuickSwitchView(); + break; + default: + throw new IllegalStateException( + "KeyboardQuickSwitch could not be initialized for test surface: " + + testSurface); + } + + switch (testCase) { + case DISMISS: + kqs.dismiss(); + break; + case LAUNCH_LAST_APP: + kqs.launchFocusedAppTask(CALCULATOR_APP_PACKAGE); + break; + case LAUNCH_SELECTED_APP: + kqs.moveFocusForward().launchFocusedAppTask(CALCULATOR_APP_PACKAGE); + break; + case LAUNCH_OVERVIEW: + kqs.moveFocusBackward().moveFocusBackward().launchFocusedOverviewTask(); + break; + default: + throw new IllegalStateException("Cannot run test case: " + testCase); + } + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 44875d5b04..b82fa35b14 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -42,7 +42,8 @@ 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; @@ -65,6 +66,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; 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/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 8542f91257..d9b179c6e7 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java @@ -113,4 +113,9 @@ public class HomeAllApps extends AllApps { protected void verifyVisibleContainerOnDismiss() { mLauncher.getWorkspace(); } + + @Override + public boolean isHomeState() { + return true; + } } 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..2a98a243bc --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java @@ -0,0 +1,212 @@ +/* + * 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_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. + *
+ * Tasks are ordered left-to-right in LTR, and vice versa in RLT, in a carousel. + *
+ * Tasks are ordered left-to-right in LTR, and vice versa in RLT, in a carousel. + *
+ * 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")) { + mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID); + + 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.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); + if (mExpectHomeKeyEventsOnDismiss) { + mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_LEFT_UP); + } + mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0); + + // Verify the final state is the same as the initial state + mLauncher.verifyContainerType(mStartingContainerType); + } + } + } + } + + /** + * Launches the currently-focused app task. + *
+ * 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) { + return (LaunchedAppState) launchFocusedTask(expectedPackageName); + } + + /** + * Launches the currently-focused overview task. + *
+ * This method only should be used if the focused task is for overview, otherwise use + * {@link #launchFocusedAppTask(String)}. + */ + public Overview launchFocusedOverviewTask() { + return (Overview) launchFocusedTask(null); + } + + private LauncherInstrumentation.VisibleContainer launchFocusedTask( + @Nullable String expectedPackageName) { + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to launch focused task: " + + (expectedPackageName == null ? "Overview" : expectedPackageName))) { + mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0); + 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..b7e3d386eb --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitchSource.java @@ -0,0 +1,57 @@ +/* + * 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")) { + 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 3450ea7caf..f6fcfa64a9 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -20,15 +20,10 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CH import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL; -import android.app.UiAutomation; import android.graphics.Point; import android.view.MotionEvent; -import android.view.accessibility.AccessibilityEvent; -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; @@ -57,7 +52,19 @@ public abstract class Launchable { */ 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)); + + mLauncher.clickLauncherObject(mObject); + + try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) { + expectActivityStartEvents(); + return mLauncher.assertAppLaunched(expectedPackageName); + } + } } } @@ -65,21 +72,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 @@ -107,14 +99,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..9f8fb92df5 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -34,7 +34,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; @@ -72,6 +71,11 @@ public final class LaunchedAppState extends Background { return LauncherInstrumentation.ContainerType.LAUNCHED_APP; } + @Override + public boolean isHomeState() { + return false; + } + /** * Returns the taskbar. * @@ -200,8 +204,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); } } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 71ca77f066..202c078cee 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -20,7 +20,10 @@ 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; @@ -49,6 +52,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; @@ -170,6 +176,7 @@ 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"; @@ -755,7 +762,7 @@ public final class LauncherInstrumentation { return isTablet() ? getLauncherPackageName() : SYSTEMUI_PACKAGE; } - private UiObject2 verifyContainerType(ContainerType containerType) { + UiObject2 verifyContainerType(ContainerType containerType) { waitForLauncherInitialized(); if (mExpectedRotationCheckEnabled && mExpectedRotation != null) { @@ -784,6 +791,7 @@ 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()) { waitForSystemLauncherObject(TASKBAR_RES_ID); @@ -798,6 +806,7 @@ public final class LauncherInstrumentation { waitUntilLauncherObjectGone(APPS_RES_ID); waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID); waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID); + waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID); if (is3PLauncher() && isTablet()) { waitForSystemLauncherObject(TASKBAR_RES_ID); @@ -813,6 +822,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); } @@ -821,6 +831,7 @@ 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()) { waitForSystemLauncherObject(TASKBAR_RES_ID); @@ -841,6 +852,7 @@ public final class LauncherInstrumentation { waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); } waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID); + waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID); return waitForSystemLauncherObject(OVERVIEW_RES_ID); } @@ -855,6 +867,7 @@ public final class LauncherInstrumentation { } waitForSystemLauncherObject(SPLIT_PLACEHOLDER_RES_ID); + waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID); return waitForSystemLauncherObject(OVERVIEW_RES_ID); } case LAUNCHED_APP: { @@ -863,6 +876,7 @@ 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; @@ -1170,6 +1184,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)); } @@ -1735,6 +1757,12 @@ public final class LauncherInstrumentation { InputDevice.SOURCE_TOUCHSCREEN); } + private void injectEvent(InputEvent event) { + assertTrue("injectInputEvent failed: event=" + event, + mInstrumentation.getUiAutomation().injectInputEvent(event, true, false)); + event.recycle(); + } + public void sendPointer(long downTime, long currentTime, int action, Point point, GestureScope gestureScope, int source) { final boolean hasTIS = hasTIS(); @@ -1772,9 +1800,39 @@ public final class LauncherInstrumentation { || action == MotionEvent.ACTION_BUTTON_RELEASE) { event.setActionButton(MotionEvent.BUTTON_PRIMARY); } - assertTrue("injectInputEvent failed", - mInstrumentation.getUiAutomation().injectInputEvent(event, true, false)); - event.recycle(); + 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, 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/TaskbarAllApps.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java index c1234fe764..3d39041124 100644 --- a/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java @@ -73,4 +73,9 @@ public class TaskbarAllApps extends AllApps { protected void verifyVisibleContainerOnDismiss() { mLauncher.getLaunchedAppState().assertTaskbarVisible(); } + + @Override + public boolean isHomeState() { + return false; + } } 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/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java index 141476c782..5a4d562474 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 { /**