diff options
Diffstat (limited to 'tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java')
-rw-r--r-- | tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java | 374 |
1 files changed, 295 insertions, 79 deletions
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); + } } |