Initial implementation for TAPL support for gestural navigation
It cuts some corners, flaky and has magic constants, which will be
addressed later.
Change-Id: I0acc75338a3c6d0ead3afda01e25ba43eb095913
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index d39a38e..fbeb3a2 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import android.graphics.Point;
+import android.os.SystemClock;
import android.view.MotionEvent;
import android.widget.TextView;
@@ -41,10 +42,12 @@
*/
public AppIconMenu openMenu() {
final Point iconCenter = mObject.getVisibleCenter();
- mLauncher.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
+ final long downTime = SystemClock.uptimeMillis();
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, iconCenter);
final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
"deep_shortcuts_container");
- mLauncher.sendPointer(MotionEvent.ACTION_UP, iconCenter);
+ mLauncher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter);
return new AppIconMenu(mLauncher, deepShortcutsContainer);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 2cde8ec..ef509ed 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -22,6 +22,10 @@
import static org.junit.Assert.assertTrue;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Until;
@@ -33,6 +37,8 @@
* indicate Launcher as long as Launcher is not in Overview state.
*/
public class Background extends LauncherInstrumentation.VisibleContainer {
+ private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500;
+ private static final int ZERO_BUTTON_SWIPE_UP_HOLD_DURATION = 400;
Background(LauncherInstrumentation launcher) {
super(launcher);
@@ -59,19 +65,41 @@
}
protected void goToOverviewUnchecked(int expectedState) {
- if (mLauncher.isSwipeUpEnabled()) {
- final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
- final int startY = getSwipeStartY();
- final int swipeHeight = mLauncher.getTestInfo(
- getSwipeHeightRequestName()).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ switch (mLauncher.getNavigationModel()) {
+ case ZERO_BUTTON: {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final Point start = new Point(centerX, startY);
+ final Point end =
+ new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
- mLauncher.swipe(
- centerX, startY, centerX,
- startY - swipeHeight - mLauncher.getTouchSlop(),
- expectedState);
- } else {
- mLauncher.waitForSystemUiObject("recent_apps").click();
+ final long downTime = SystemClock.uptimeMillis();
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
+ mLauncher.movePointer(downTime, ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, start, end);
+ LauncherInstrumentation.sleep(ZERO_BUTTON_SWIPE_UP_HOLD_DURATION);
+ mLauncher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end);
+ break;
+ }
+
+ case TWO_BUTTON: {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+
+ mLauncher.swipe(
+ centerX, startY, centerX,
+ startY - swipeHeight - mLauncher.getTouchSlop(),
+ expectedState);
+ break;
+ }
+
+ case THREE_BUTTON:
+ mLauncher.waitForSystemUiObject("recent_apps").click();
+ break;
}
}
@@ -80,6 +108,6 @@
}
protected int getSwipeStartY() {
- return mLauncher.waitForSystemUiObject("home").getVisibleBounds().centerY();
+ return mLauncher.waitForSystemUiObject("navigation_bar_frame").getVisibleBounds().centerY();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 6e92dad..8cf1262 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -44,8 +44,10 @@
* Flings forward (left) and waits the fling's end.
*/
public void flingForward() {
- final UiObject2 overview = verifyActiveContainer();
LauncherInstrumentation.log("Overview.flingForward before fling");
+ final UiObject2 overview = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ overview.setGestureMargins(margin, 0, 0, 0);
overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
@@ -71,8 +73,10 @@
* Flings backward (right) and waits the fling's end.
*/
public void flingBackward() {
- final UiObject2 overview = verifyActiveContainer();
LauncherInstrumentation.log("Overview.flingBackward before fling");
+ final UiObject2 overview = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ overview.setGestureMargins(0, 0, margin, 0);
overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index e3850ff..f44022b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -16,6 +16,7 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
import android.app.ActivityManager;
@@ -30,14 +31,17 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Configurator;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
@@ -59,6 +63,7 @@
public final class LauncherInstrumentation {
private static final String TAG = "Tapl";
+ private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
@@ -66,6 +71,8 @@
WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, BASE_OVERVIEW
}
+ public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
+
// Base class for launcher containers.
static abstract class VisibleContainer {
protected final LauncherInstrumentation mLauncher;
@@ -150,7 +157,11 @@
sActiveContainer = new WeakReference<>(container);
}
- public boolean isSwipeUpEnabled() {
+ public NavigationModel getNavigationModel() {
+ return isSwipeUpEnabled() ? NavigationModel.TWO_BUTTON : NavigationModel.THREE_BUTTON;
+ }
+
+ private boolean isSwipeUpEnabled() {
final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
return SwipeUpSetting.isSwipeUpSettingAvailable() ?
Settings.Secure.getInt(
@@ -286,24 +297,49 @@
// We need waiting for any accessibility event generated after pressing Home because
// otherwise waitForIdle may return immediately in case when there was a big enough pause in
// accessibility events prior to pressing Home.
- executeAndWaitForEvent(
- () -> {
- log("LauncherInstrumentation.pressHome before clicking");
- waitForSystemUiObject("home").click();
- },
- event -> true,
- "Pressing Home didn't produce any events");
- mDevice.waitForIdle();
+ if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
+ if (hasLauncherObject(WORKSPACE_RES_ID)) {
+ log("0-button pressHome: already in workspace");
+ } else if (hasLauncherObject(OVERVIEW_RES_ID)) {
+ log("0-button pressHome: overview");
+ mDevice.pressHome();
+ } else if (hasLauncherObject(WIDGETS_RES_ID)) {
+ log("0-button pressHome: widgets");
+ mDevice.pressHome();
+ } else if (hasLauncherObject(APPS_RES_ID)) {
+ log("0-button pressHome: all apps");
+ mDevice.pressHome();
+ } else {
+ log("0-button pressHome: another app");
+ assertTrue("Launcher is visible, don't know how to go home",
+ !mDevice.hasObject(By.pkg(getLauncherPackageName())));
+ final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
- // Temporarily press home twice as the first click sometimes gets ignored (b/124239413)
- executeAndWaitForEvent(
- () -> {
- log("LauncherInstrumentation.pressHome before clicking");
- waitForSystemUiObject("home").click();
- },
- event -> true,
- "Pressing Home didn't produce any events");
- mDevice.waitForIdle();
+ swipe(
+ navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
+ navBar.getVisibleBounds().centerX(), 0,
+ BACKGROUND_APP_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+ }
+ } else {
+ executeAndWaitForEvent(
+ () -> {
+ log("LauncherInstrumentation.pressHome before clicking");
+ waitForSystemUiObject("home").click();
+ },
+ event -> true,
+ "Pressing Home didn't produce any events");
+ mDevice.waitForIdle();
+
+ // Temporarily press home twice as the first click sometimes gets ignored (b/124239413)
+ executeAndWaitForEvent(
+ () -> {
+ log("LauncherInstrumentation.pressHome before clicking");
+ waitForSystemUiObject("home").click();
+ },
+ event -> true,
+ "Pressing Home didn't produce any events");
+ mDevice.waitForIdle();
+ }
return getWorkspace();
}
@@ -424,6 +460,11 @@
return object;
}
+ @Nullable
+ private boolean hasLauncherObject(String resId) {
+ return mDevice.hasObject(getLauncherObjectSelector(resId));
+ }
+
@NonNull
UiObject2 waitForLauncherObject(String resName) {
final BySelector selector = getLauncherObjectSelector(resName);
@@ -446,8 +487,12 @@
}
void swipe(int startX, int startY, int endX, int endY, int expectedState) {
+ swipe(startX, startY, endX, endY, expectedState, 60);
+ }
+
+ void swipe(int startX, int startY, int endX, int endY, int expectedState, int steps) {
final Bundle parcel = (Bundle) executeAndWaitForEvent(
- () -> mDevice.swipe(startX, startY, endX, endY, 60),
+ () -> mDevice.swipe(startX, startY, endX, endY, steps),
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
"Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
@@ -459,13 +504,6 @@
mDevice.waitForIdle();
}
- void sendPointer(int action, Point point) {
- final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
- SystemClock.uptimeMillis(), action, point.x, point.y, 0);
- mInstrumentation.sendPointerSync(event);
- event.recycle();
- }
-
float getDisplayDensity() {
return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
}
@@ -473,4 +511,51 @@
int getTouchSlop() {
return ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
+
+ private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
+ float x, float y) {
+ MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
+ properties.id = 0;
+ properties.toolType = Configurator.getInstance().getToolType();
+
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.pressure = 1;
+ coords.size = 1;
+ coords.x = x;
+ coords.y = y;
+
+ return MotionEvent.obtain(downTime, eventTime, action, 1,
+ new MotionEvent.PointerProperties[]{properties},
+ new MotionEvent.PointerCoords[]{coords},
+ 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
+ }
+
+ void sendPointer(long downTime, long currentTime, int action, Point point) {
+ final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
+ mInstrumentation.getUiAutomation().injectInputEvent(event, true);
+ event.recycle();
+ }
+
+ void movePointer(long downTime, long duration, Point from, Point to) {
+ final Point point = new Point();
+ for (; ; ) {
+ sleep(16);
+
+ final long currentTime = SystemClock.uptimeMillis();
+ final float progress = (currentTime - downTime) / (float) duration;
+ if (progress > 1) return;
+
+ point.x = from.x + (int) (progress * (to.x - from.x));
+ point.y = from.y + (int) (progress * (to.y - from.y));
+
+ sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point);
+ }
+ }
+
+ static void sleep(int duration) {
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index e3ef74a..7d97acd 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -136,6 +136,8 @@
*/
public void flingForward() {
final UiObject2 workspace = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ workspace.setGestureMargins(0, 0, margin, 0);
workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
@@ -147,6 +149,8 @@
*/
public void flingBackward() {
final UiObject2 workspace = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ workspace.setGestureMargins(margin, 0, 0, 0);
workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();