diff options
author | Hyungtae Tim Kim <hyungtaekim@google.com> | 2016-09-20 20:43:42 +0900 |
---|---|---|
committer | Hyungtae Tim Kim <hyungtaekim@google.com> | 2016-09-26 12:00:36 +0900 |
commit | 079833855b826fde1661f52ec26cd813d466787d (patch) | |
tree | d92d541584120cf586a9e6efb8724062ed797d23 | |
parent | 72eae3aa0a7ff570a12613b5a47ac3c8d7288a71 (diff) | |
download | platform_testing-079833855b826fde1661f52ec26cd813d466787d.tar.gz |
Create dpad util for TV functional tests
Adds dpad util to inject key events so that it can be used
by system/app helpers
Bug: 31635441
Change-Id: I47a533055927541d0516a7e4a5f6c21046bf2e92
6 files changed, 228 insertions, 42 deletions
diff --git a/libraries/base-app-helpers/Android.mk b/libraries/base-app-helpers/Android.mk index 938ee4cba..ce06cc53e 100644 --- a/libraries/base-app-helpers/Android.mk +++ b/libraries/base-app-helpers/Android.mk @@ -17,7 +17,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := base-app-helpers -LOCAL_STATIC_JAVA_LIBRARIES := account-util +LOCAL_STATIC_JAVA_LIBRARIES := account-util dpad-util LOCAL_JAVA_LIBRARIES := ub-uiautomator launcher-helper-lib LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractLeanbackAppHelper.java b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractLeanbackAppHelper.java index 7d7b1b467..afe3c04b7 100644 --- a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractLeanbackAppHelper.java +++ b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractLeanbackAppHelper.java @@ -19,6 +19,7 @@ package android.platform.test.helpers; import android.app.Instrumentation; import android.platform.test.helpers.exceptions.UiTimeoutException; import android.platform.test.helpers.exceptions.UnknownUiException; +import android.platform.test.utils.DPadUtil; import android.support.test.launcherhelper.ILeanbackLauncherStrategy; import android.support.test.launcherhelper.LauncherStrategyFactory; import android.support.test.uiautomator.By; @@ -53,12 +54,16 @@ public abstract class AbstractLeanbackAppHelper extends AbstractStandardAppHelpe ERROR_FRAGMENT } + protected DPadUtil mDPadUtil; + // TODO: Delete DPadHelper once migrated to using DPadUtil protected DPadHelper mDPadHelper; public ILeanbackLauncherStrategy mLauncherStrategy; public AbstractLeanbackAppHelper(Instrumentation instr) { super(instr); + mDPadUtil = new DPadUtil(instr); + // TODO: Delete DPadHelper once migrated to using DPadUtil mDPadHelper = DPadHelper.getInstance(instr); mLauncherStrategy = LauncherStrategyFactory.getInstance( mDevice).getLeanbackLauncherStrategy(); @@ -218,10 +223,10 @@ public abstract class AbstractLeanbackAppHelper extends AbstractStandardAppHelpe } while (!focus.hasObject(target)) { UiObject2 prev = focus; - mDPadHelper.pressDPad(direction); + mDPadUtil.pressDPad(direction); focus = container.findObject(By.focused(true)); if (focus == null) { - mDPadHelper.pressDPad(Direction.reverse(direction)); + mDPadUtil.pressDPad(Direction.reverse(direction)); focus = container.findObject(By.focused(true)); } if (focus.equals(prev)) { @@ -321,7 +326,7 @@ public abstract class AbstractLeanbackAppHelper extends AbstractStandardAppHelpe throw new IllegalStateException("Failed to find a card in row content " + title); } } - mDPadHelper.pressDPadCenter(); + mDPadUtil.pressDPadCenter(); mDevice.wait(Until.gone(By.res(getPackage(), "title_text").text(title)), SELECT_WAIT_TIME_MS); } diff --git a/libraries/launcher-helper/Android.mk b/libraries/launcher-helper/Android.mk index 60c499be5..bd9d62f1c 100644 --- a/libraries/launcher-helper/Android.mk +++ b/libraries/launcher-helper/Android.mk @@ -19,6 +19,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := launcher-helper-lib LOCAL_JAVA_LIBRARIES := ub-uiautomator +LOCAL_STATIC_JAVA_LIBRARIES := dpad-util LOCAL_SDK_VERSION := 21 LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/LeanbackLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/LeanbackLauncherStrategy.java index 16c6b3b2a..59084ae3f 100644 --- a/libraries/launcher-helper/src/android/support/test/launcherhelper/LeanbackLauncherStrategy.java +++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/LeanbackLauncherStrategy.java @@ -19,6 +19,7 @@ package android.support.test.launcherhelper; import android.graphics.Point; import android.os.RemoteException; import android.os.SystemClock; +import android.platform.test.utils.DPadUtil; import android.support.test.uiautomator.*; import android.util.Log; @@ -37,6 +38,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { private static final int NOTIFICATION_WAIT_TIME = 30000; protected UiDevice mDevice; + protected DPadUtil mDPadUtil = new DPadUtil(); /** @@ -62,7 +64,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { public void open() { // if we see main list view, assume at home screen already if (!mDevice.hasObject(getWorkspaceSelector())) { - mDevice.pressHome(); + mDPadUtil.pressHome(); // ensure launcher is shown if (!mDevice.wait(Until.hasObject(getWorkspaceSelector()), SHORT_WAIT_TIME)) { // HACK: dump hierarchy to logcat @@ -205,12 +207,12 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { throw new RuntimeException("Could not find keyboard orb."); } if (orbButton.isFocused()) { - mDevice.pressDPadCenter(); + mDPadUtil.pressDPadCenter(); } else { // Move the focus to keyboard orb by DPad button. - mDevice.pressDPadRight(); + mDPadUtil.pressDPadRight(); if (orbButton.isFocused()) { - mDevice.pressDPadCenter(); + mDPadUtil.pressDPadCenter(); } } mDevice.wait(Until.gone(keyboardOrb), SHORT_WAIT_TIME); @@ -225,7 +227,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { SystemClock.sleep(SHORT_WAIT_TIME); // Note that Enter key is pressed instead of DPad keys to dismiss leanback IME - mDevice.pressEnter(); + mDPadUtil.pressEnter(); mDevice.wait(Until.gone(searchEditor), SHORT_WAIT_TIME); } @@ -239,7 +241,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { public UiObject2 selectNotificationRow() { if (!isNotificationRowSelected()) { open(); - mDevice.pressHome(); // Home key to move to the first card in the Notification row + mDPadUtil.pressHome(); // Home key to move to the first card in the Notification row } return mDevice.wait(Until.findObject( getNotificationRowSelector().hasDescendant(By.focused(true), 3)), SHORT_WAIT_TIME); @@ -252,7 +254,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { public UiObject2 selectSearchRow() { if (!isSearchRowSelected()) { selectNotificationRow(); - mDevice.pressDPadUp(); + mDPadUtil.pressDPadUp(); } return mDevice.wait(Until.findObject( getSearchRowSelector().hasDescendant(By.focused(true))), SHORT_WAIT_TIME); @@ -392,19 +394,19 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { // The sequence of moving should be kept in the following order so as not to // be stuck in case that the apps row are not even. if (dx < -MARGIN) { - mDevice.pressDPadLeft(); + mDPadUtil.pressDPadLeft(); continue; } if (dy < -MARGIN) { - mDevice.pressDPadUp(); + mDPadUtil.pressDPadUp(); continue; } if (dx > MARGIN) { - mDevice.pressDPadRight(); + mDPadUtil.pressDPadRight(); continue; } if (dy > MARGIN) { - mDevice.pressDPadDown(); + mDPadUtil.pressDPadDown(); continue; } throw new RuntimeException( @@ -419,7 +421,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { // The app icon is already found and focused. long ready = SystemClock.uptimeMillis(); - mDevice.pressDPadCenter(); + mDPadUtil.pressDPadCenter(); if (!mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), APP_LAUNCH_TIMEOUT)) { Log.w(LOG_TAG, "no new window detected after app launch attempt."); return ILauncherStrategy.LAUNCH_FAILED_TIMESTAMP; @@ -467,12 +469,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { appName, card.getContentDescription())); // Click and wait until the Notification card opens - return mDevice.performActionAndWait(new Runnable() { - @Override - public void run() { - mDevice.pressDPadCenter(); - } - }, Until.newWindow(), APP_LAUNCH_TIMEOUT); + return mDPadUtil.pressDPadCenterAndWait(Until.newWindow(), APP_LAUNCH_TIMEOUT); } protected boolean isSearchRowSelected() { @@ -539,7 +536,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { protected UiObject2 findNotificationCard(BySelector selector) { // Move to the first notification, Search to the right - mDevice.pressHome(); + mDPadUtil.pressHome(); // Find if a focused card matches a given selector UiObject2 currentFocus = mDevice.findObject(getNotificationRowSelector()) @@ -549,7 +546,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { if (currentFocus.hasObject(selector)) { return currentFocus; // Found } - mDevice.pressDPadRight(); + mDPadUtil.pressDPadRight(); previousFocus = currentFocus; currentFocus = mDevice.findObject(getNotificationRowSelector()) .findObject(By.res(getSupportedLauncherPackage(), "card").focused(true)); @@ -565,7 +562,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { String prevText = focusedIcon.getContentDescription(); String nextText; do { - mDevice.pressDPadLeft(); + mDPadUtil.pressDPadLeft(); appIcon = container.findObject(app); if (appIcon != null) { return appIcon; @@ -577,7 +574,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { // If we haven't found it yet, search by going right do { - mDevice.pressDPadRight(); + mDPadUtil.pressDPadRight(); appIcon = container.findObject(app); if (appIcon != null) { return appIcon; @@ -609,11 +606,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { return rowObject; // Found } - if (direction == Direction.DOWN) { - mDevice.pressDPadDown(); - } else if (direction == Direction.UP) { - mDevice.pressDPadUp(); - } + mDPadUtil.pressDPad(direction); prevFocused = currentFocused; currentFocused = mDevice.findObject(By.focused(true)); } @@ -641,12 +634,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { if (button == null) { throw new IllegalStateException("Restricted Profile not found on launcher"); } - mDevice.performActionAndWait(new Runnable() { - @Override - public void run() { - mDevice.pressDPadCenter(); - } - }, Until.newWindow(), APP_LAUNCH_TIMEOUT); + mDPadUtil.pressDPadCenterAndWait(Until.newWindow(), APP_LAUNCH_TIMEOUT); } protected UiObject2 findSettingInRow(BySelector selector, Direction direction) { @@ -665,11 +653,7 @@ public class LeanbackLauncherStrategy implements ILeanbackLauncherStrategy { return setting; } - if (direction == Direction.RIGHT) { - mDevice.pressDPadRight(); - } else if (direction == Direction.LEFT) { - mDevice.pressDPadLeft(); - } + mDPadUtil.pressDPad(direction); mDevice.waitForIdle(); prevFocused = currentFocused; currentFocused = mDevice.findObject(By.focused(true)); diff --git a/utils/dpad/Android.mk b/utils/dpad/Android.mk new file mode 100644 index 000000000..f211bc798 --- /dev/null +++ b/utils/dpad/Android.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2016 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------- +# The static library that platform/app helpers can link against +include $(CLEAR_VARS) + +LOCAL_MODULE := dpad-util +LOCAL_JAVA_LIBRARIES := ub-uiautomator android-support-test +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/utils/dpad/src/android/platform/test/utils/DPadUtil.java b/utils/dpad/src/android/platform/test/utils/DPadUtil.java new file mode 100644 index 000000000..dfd7fdf3b --- /dev/null +++ b/utils/dpad/src/android/platform/test/utils/DPadUtil.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2016 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 android.platform.test.utils; + +import android.app.Instrumentation; +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.EventCondition; +import android.support.test.uiautomator.UiDevice; +import android.util.Log; +import android.view.KeyEvent; + +import java.io.IOException; + + +public class DPadUtil { + + private static final String TAG = DPadUtil.class.getSimpleName(); + private static final long DPAD_DEFAULT_WAIT_TIME_MS = 1000; // 1 sec + private UiDevice mDevice; + + + public DPadUtil() { + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + } + + public DPadUtil(Instrumentation instrumentation) { + mDevice = UiDevice.getInstance(instrumentation); + } + + public boolean pressDPad(Direction direction) { + return pressDPad(direction, 1, DPAD_DEFAULT_WAIT_TIME_MS); + } + + public void pressDPad(Direction direction, long repeat) { + pressDPad(direction, repeat, DPAD_DEFAULT_WAIT_TIME_MS); + } + + /** + * Presses DPad button of the same direction for the count times. + * It sleeps between each press for DPAD_DEFAULT_WAIT_TIME_MS. + * + * @param direction the direction of the button to press. + * @param repeat the number of times to press the button. + * @param timeout the timeout for the wait. + * @return true if the last key simulation is successful, else return false + */ + public boolean pressDPad(Direction direction, long repeat, long timeout) { + int iteration = 0; + boolean result = false; + while (iteration++ < repeat) { + switch (direction) { + case LEFT: + result = mDevice.pressDPadLeft(); + break; + case RIGHT: + result = mDevice.pressDPadRight(); + break; + case UP: + result = mDevice.pressDPadUp(); + break; + case DOWN: + result = mDevice.pressDPadDown(); + break; + } + SystemClock.sleep(timeout); + } + return result; + } + + public boolean pressDPadLeft() { + return mDevice.pressDPadLeft(); + } + + public boolean pressDPadRight() { + return mDevice.pressDPadRight(); + } + + public boolean pressDPadUp() { + return mDevice.pressDPadUp(); + } + + public boolean pressDPadDown() { + return mDevice.pressDPadDown(); + } + + public boolean pressHome() { + return mDevice.pressHome(); + } + + public boolean pressBack() { + return mDevice.pressBack(); + } + + public boolean pressDPadCenter() { + return mDevice.pressDPadCenter(); + } + + public boolean pressEnter() { + return mDevice.pressEnter(); + } + + public boolean pressPipKey() { + return mDevice.pressKeyCode(KeyEvent.KEYCODE_WINDOW); + } + + public boolean pressKeyCode(int keyCode) { + return mDevice.pressKeyCode(keyCode); + } + + public boolean longPressKeyCode(int keyCode) { + try { + mDevice.executeShellCommand(String.format("input keyevent --longpress %d", keyCode)); + return true; + } catch (IOException e) { + // Ignore + Log.w(TAG, String.format("Failed to long press the key code: %d", keyCode)); + return false; + } + } + + /** + * Press the key code, and waits for the given condition to become true. + * @param condition + * @param keyCode + * @param timeout + * @param <R> + * @return + */ + public <R> R pressKeyCodeAndWait(int keyCode, EventCondition<R> condition, long timeout) { + return mDevice.performActionAndWait(new KeyEventRunnable(keyCode), condition, timeout); + } + + public <R> R pressDPadCenterAndWait(EventCondition<R> condition, long timeout) { + return mDevice.performActionAndWait(new KeyEventRunnable(KeyEvent.KEYCODE_DPAD_CENTER), + condition, timeout); + } + + public <R> R pressEnterAndWait(EventCondition<R> condition, long timeout) { + return mDevice.performActionAndWait(new KeyEventRunnable(KeyEvent.KEYCODE_ENTER), + condition, timeout); + } + + private class KeyEventRunnable implements Runnable { + private int mKeyCode; + public KeyEventRunnable(int keyCode) { + mKeyCode = keyCode; + } + @Override + public void run() { + mDevice.pressKeyCode(mKeyCode); + } + } +} |