summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Tryshev <vadimt@google.com>2018-05-31 14:33:09 -0700
committerVadim Tryshev <vadimt@google.com>2018-06-18 14:29:34 -0700
commit5a7d701d555c433a945ee102e94f431eb9c53767 (patch)
tree85bc1e538a2f51179b6887d945e2ec49338c648e
parent3871e7268607f446ed25bf475d13e230e51c64c3 (diff)
downloadandroid_packages_apps_Trebuchet-5a7d701d555c433a945ee102e94f431eb9c53767.tar.gz
android_packages_apps_Trebuchet-5a7d701d555c433a945ee102e94f431eb9c53767.tar.bz2
android_packages_apps_Trebuchet-5a7d701d555c433a945ee102e94f431eb9c53767.zip
UNREVIEWED: TAPL prototype for in-lab flake test
These sources didn't pass a normal CR, and mustn't be used in prod. The goal of landing them is to start in-lab flakiness tests ASAP. A normal code review will follow after landing cleanup CLs. Bug: 110103162 Test: Running TaplTests suite Change-Id: I051591c9af7822d4cbae4261e66563f1ccc1cfcc
-rw-r--r--tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java142
-rw-r--r--tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java63
-rw-r--r--tests/tapl/com/android/launcher3/tapl/AppIcon.java53
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Home.java187
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Launcher.java303
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Overview.java111
-rw-r--r--tests/tapl/com/android/launcher3/tapl/OverviewTask.java65
-rw-r--r--tests/tapl/com/android/launcher3/tapl/Widgets.java65
8 files changed, 989 insertions, 0 deletions
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java
new file mode 100644
index 000000000..02f8183c8
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 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 android.support.annotation.NonNull;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApps opened from Home.
+ */
+public final class AllAppsFromHome {
+ private static final int MAX_SCROLL_ATTEMPTS = 40;
+ private static final int MIN_INTERACT_SIZE = 100;
+ private static final int FLING_SPEED = 12000;
+
+ private final Launcher mLauncher;
+ private final int mHeight;
+
+ AllAppsFromHome(Launcher launcher) {
+ mLauncher = launcher;
+ final UiObject2 allAppsContainer = assertState();
+ mHeight = allAppsContainer.getVisibleBounds().height();
+ }
+
+ /**
+ * Asserts that we are in all apps.
+ *
+ * @return All apps container.
+ */
+ @NonNull
+ private UiObject2 assertState() {
+ return mLauncher.assertState(Launcher.State.ALL_APPS);
+ }
+
+ /**
+ * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
+ * sure the icon is visible.
+ *
+ * @param appName name of the app.
+ * @return The app.
+ */
+ @NonNull
+ public AppIcon getAppIcon(String appName) {
+ final UiObject2 allAppsContainer = assertState();
+ final BySelector appIconSelector = AppIcon.getAppIconSelector(appName);
+ if (!allAppsContainer.hasObject(appIconSelector)) {
+ scrollBackToBeginning();
+ int attempts = 0;
+ while (!allAppsContainer.hasObject(appIconSelector) &&
+ allAppsContainer.scroll(Direction.DOWN, 0.8f)) {
+ mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
+ ++attempts <= MAX_SCROLL_ATTEMPTS);
+ assertState();
+ }
+ }
+ assertState();
+
+ final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer, appIconSelector);
+ ensureIconVisible(appIcon, allAppsContainer);
+ return new AppIcon(mLauncher, appIcon);
+ }
+
+ private void scrollBackToBeginning() {
+ final UiObject2 allAppsContainer = assertState();
+
+ int attempts = 0;
+ allAppsContainer.setGestureMargins(5, 500, 5, 5);
+
+ while (allAppsContainer.scroll(Direction.UP, 0.5f)) {
+ mLauncher.waitForIdle();
+ assertState();
+
+ mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
+ ++attempts <= MAX_SCROLL_ATTEMPTS);
+ }
+
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ private void ensureIconVisible(UiObject2 appIcon, UiObject2 allAppsContainer) {
+ final int appHeight = appIcon.getVisibleBounds().height();
+ if (appHeight < MIN_INTERACT_SIZE) {
+ // Try to figure out how much percentage of the container needs to be scrolled in order
+ // to reveal the app icon to have the MIN_INTERACT_SIZE
+ final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f);
+ allAppsContainer.scroll(Direction.DOWN, pct);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+ }
+
+ /**
+ * Flings forward (down) and waits the fling's end.
+ */
+ public void flingForward() {
+ final UiObject2 allAppsContainer = assertState();
+ // Start the gesture in the center to avoid starting at elements near the top.
+ allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
+ allAppsContainer.fling(Direction.DOWN, FLING_SPEED);
+ assertState();
+ }
+
+ /**
+ * Flings backward (up) and waits the fling's end.
+ */
+ public void flingBackward() {
+ final UiObject2 allAppsContainer = assertState();
+ // Start the gesture in the center, for symmetry with forward.
+ allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
+ allAppsContainer.fling(Direction.UP, FLING_SPEED);
+ assertState();
+ }
+
+ /**
+ * Gets the UI object for AllApps.
+ * Used by NexusLauncherStrategy.openAllApps(). No one else should use it.
+ *
+ * @return container object.
+ */
+ @Deprecated
+ @NonNull
+ public UiObject2 getObjectDeprecated() {
+ return assertState();
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
new file mode 100644
index 000000000..cba708621
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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 android.graphics.Point;
+import android.support.annotation.NonNull;
+import android.support.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApps opened from Overview.
+ * Scroll gestures that are OK for {@link AllAppsFromHome} may close it, so they are not supported.
+ */
+public final class AllAppsFromOverview {
+ private final Launcher mLauncher;
+
+ AllAppsFromOverview(Launcher launcher) {
+ mLauncher = launcher;
+ assertState();
+ }
+
+ /**
+ * Asserts that we are in all apps.
+ *
+ * @return All apps container.
+ */
+ @NonNull
+ private UiObject2 assertState() {
+ return mLauncher.assertState(Launcher.State.ALL_APPS);
+ }
+
+ /**
+ * Swipes down to switch back to Overview whence we came from.
+ *
+ * @return the overview panel.
+ */
+ @NonNull
+ public Overview switchBackToOverview() {
+ final UiObject2 allAppsContainer = assertState();
+ // Swipe from the search box to the bottom.
+ final UiObject2 qsb = mLauncher.waitForObjectInContainer(
+ allAppsContainer, "search_container_all_apps");
+ final Point start = qsb.getVisibleCenter();
+ final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.6);
+ mLauncher.swipe(start.x, start.y, start.x, endY, (endY - start.y) / 100); // 100 px/step
+
+ return new Overview(mLauncher);
+ }
+
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
new file mode 100644
index 000000000..73a74f2dd
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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 android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.widget.TextView;
+
+/**
+ * App icon, whether in all apps or in workspace/
+ */
+public final class AppIcon {
+ private final Launcher mLauncher;
+ private final UiObject2 mIcon;
+
+ AppIcon(Launcher launcher, UiObject2 icon) {
+ mLauncher = launcher;
+ mIcon = icon;
+ }
+
+ static BySelector getAppIconSelector(String appName) {
+ return By.clazz(TextView.class).text(appName).pkg(Launcher.LAUNCHER_PKG);
+ }
+
+ /**
+ * Clicks the icon to launch its app.
+ */
+ public void launch() {
+ mLauncher.assertTrue("Launching an app didn't open a new window: " + mIcon.getText(),
+ mIcon.clickAndWait(Until.newWindow(), Launcher.APP_LAUNCH_TIMEOUT_MS));
+ mLauncher.assertState(Launcher.State.BACKGROUND);
+ }
+
+ UiObject2 getIcon() {
+ return mIcon;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
new file mode 100644
index 000000000..0ec1a644e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 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 junit.framework.TestCase.assertTrue;
+
+import android.graphics.Point;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
+import android.view.KeyEvent;
+
+/**
+ * Operations on the home screen.
+ */
+public final class Home {
+
+ private final Launcher mLauncher;
+ private final UiObject2 mHotseat;
+ private final int ICON_DRAG_SPEED = 2000;
+
+ Home(Launcher launcher) {
+ mLauncher = launcher;
+ assertState();
+ mHotseat = launcher.waitForLauncherObject("hotseat");
+ }
+
+ /**
+ * Asserts that we are in home.
+ *
+ * @return Workspace.
+ */
+ @NonNull
+ private UiObject2 assertState() {
+ return mLauncher.assertState(Launcher.State.HOME);
+ }
+
+ /**
+ * Swipes up or presses the square button to switch to Overview.
+ *
+ * @return the Overview panel object.
+ */
+ @NonNull
+ public Overview switchToOverview() {
+ assertState();
+ if (mLauncher.isSwipeUpEnabled()) {
+ final int height = mLauncher.getDevice().getDisplayHeight();
+ final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame");
+
+ // Swipe from nav bar to 2/3rd down the screen.
+ mLauncher.swipe(
+ navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
+ navBar.getVisibleBounds().centerX(), height * 2 / 3,
+ (navBar.getVisibleBounds().centerY() - height * 2 / 3) / 100); // 100 px/step
+ } else {
+ mLauncher.getSystemUiObject("recent_apps").click();
+ }
+
+ return new Overview(mLauncher);
+ }
+
+ /**
+ * Swipes up to All Apps.
+ *
+ * @return the App Apps object.
+ */
+ @NonNull
+ public AllAppsFromHome switchToAllApps() {
+ assertState();
+ if (mLauncher.isSwipeUpEnabled()) {
+ int midX = mLauncher.getDevice().getDisplayWidth() / 2;
+ int height = mLauncher.getDevice().getDisplayHeight();
+ // Swipe from 6/7ths down the screen to 1/7th down the screen.
+ mLauncher.swipe(
+ midX,
+ height * 6 / 7,
+ midX,
+ height / 7,
+ (height * 2 / 3) / 100); // 100 px/step
+ } else {
+ // Swipe from the hotseat to near the top, e.g. 10% of the screen.
+ final UiObject2 hotseat = mHotseat;
+ final Point start = hotseat.getVisibleCenter();
+ final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f);
+ mLauncher.swipe(
+ start.x,
+ start.y,
+ start.x,
+ endY,
+ (start.y - endY) / 100); // 100 px/step
+ }
+
+ return new AllAppsFromHome(mLauncher);
+ }
+
+ /**
+ * Returns an icon for the app, if currently visible.
+ *
+ * @param appName name of the app
+ * @return app icon, if found, null otherwise.
+ */
+ @Nullable
+ public AppIcon tryGetWorkspaceAppIcon(String appName) {
+ final UiObject2 workspace = assertState();
+ final UiObject2 icon = workspace.findObject(AppIcon.getAppIconSelector(appName));
+ return icon != null ? new AppIcon(mLauncher, icon) : null;
+ }
+
+ /**
+ * Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the
+ * second screen.
+ */
+ public void ensureWorkspaceIsScrollable() {
+ final UiObject2 workspace = assertState();
+ if (!isWorkspaceScrollable(workspace)) {
+ dragIconToNextScreen(getHotseatAppIcon("Messages"), workspace);
+ }
+ assertTrue("Home screen workspace didn't become scrollable",
+ isWorkspaceScrollable(workspace));
+ }
+
+ private boolean isWorkspaceScrollable(UiObject2 workspace) {
+ return workspace.isScrollable();
+ }
+
+ @NonNull
+ private AppIcon getHotseatAppIcon(String appName) {
+ return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
+ mHotseat, AppIcon.getAppIconSelector(appName)));
+ }
+
+ private void dragIconToNextScreen(AppIcon app, UiObject2 workspace) {
+ final Point dest = new Point(
+ mLauncher.getDevice().getDisplayWidth(), workspace.getVisibleBounds().centerY());
+ app.getIcon().drag(dest, ICON_DRAG_SPEED);
+ assertState();
+ }
+
+ /**
+ * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
+ * recoil to complete.
+ */
+ public void flingForward() {
+ final UiObject2 workspace = assertState();
+ workspace.fling(Direction.RIGHT);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ /**
+ * Flings to get to screens on the left. Waits for scrolling and a possible overscroll
+ * recoil to complete.
+ */
+ public void flingBackward() {
+ final UiObject2 workspace = assertState();
+ workspace.fling(Direction.LEFT);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ /**
+ * Opens widgets container by pressing Ctrl+W.
+ *
+ * @return the widgets container.
+ */
+ @NonNull
+ public Widgets openAllWidgets() {
+ assertState();
+ mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
+ return new Widgets(mLauncher);
+ }
+} \ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Launcher.java b/tests/tapl/com/android/launcher3/tapl/Launcher.java
new file mode 100644
index 000000000..5201dc8ba
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Launcher.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2018 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.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
+
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * The main tapl object. The only object that can be explicitly constructed by the using code. It
+ * produces all other objects.
+ */
+public final class Launcher {
+
+ private static final String WORKSPACE_RES_ID = "workspace";
+ private static final String APPS_RES_ID = "apps_view";
+ private static final String OVERVIEW_RES_ID = "overview_panel";
+ private static final String WIDGETS_RES_ID = "widgets_list_view";
+
+ enum State {HOME, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND}
+
+ static final String LAUNCHER_PKG = "com.google.android.apps.nexuslauncher";
+ static final int APP_LAUNCH_TIMEOUT_MS = 10000;
+ private static final int UI_OBJECT_WAIT_TIMEOUT_MS = 10000;
+ private static final String SWIPE_UP_SETTING_AVAILABLE_RES_NAME =
+ "config_swipe_up_gesture_setting_available";
+ private static final String SWIPE_UP_ENABLED_DEFAULT_RES_NAME =
+ "config_swipe_up_gesture_default";
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+ private static final String TAG = "tapl.Launcher";
+ private final UiDevice mDevice;
+ private final boolean mSwipeUpEnabled;
+
+ /**
+ * Constructs the root of TAPL hierarchy. You get all other object from it.
+ */
+ public Launcher(UiDevice device) {
+ mDevice = device;
+ final boolean swipeUpEnabledDefault =
+ !getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME) ||
+ getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME);
+ mSwipeUpEnabled = Settings.Secure.getInt(
+ InstrumentationRegistry.getTargetContext().getContentResolver(),
+ SWIPE_UP_SETTING_NAME,
+ swipeUpEnabledDefault ? 1 : 0) == 1;
+ }
+
+ private boolean getSystemBooleanRes(String resName) {
+ final Resources res = Resources.getSystem();
+ final int resId = res.getIdentifier(resName, "bool", "android");
+ assertTrue("Resource not found: " + resName, resId != 0);
+ return res.getBoolean(resId);
+ }
+
+ private void dumpViewHierarchy() {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ mDevice.dumpWindowHierarchy(stream);
+ stream.flush();
+ stream.close();
+ for (String line : stream.toString().split("\\r?\\n")) {
+ Log.e(TAG, line.trim());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "error dumping XML to logcat", e);
+ }
+ }
+
+ void fail(String message) {
+ dumpViewHierarchy();
+ Assert.fail(message);
+ }
+
+ void assertTrue(String message, boolean condition) {
+ if (!condition) {
+ fail(message);
+ }
+ }
+
+ void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+
+ private void failEquals(String message, Object actual) {
+ String formatted = "Values should be different. ";
+ if (message != null) {
+ formatted = message + ". ";
+ }
+
+ formatted += "Actual: " + actual;
+ fail(formatted);
+ }
+
+ void assertNotEquals(String message, int unexpected, int actual) {
+ if (unexpected == actual) {
+ failEquals(message, actual);
+ }
+ }
+
+ boolean isSwipeUpEnabled() {
+ return mSwipeUpEnabled;
+ }
+
+ UiObject2 assertState(State state) {
+ switch (state) {
+ case HOME: {
+ //waitUntilGone(APPS_RES_ID);
+ waitUntilGone(OVERVIEW_RES_ID);
+ waitUntilGone(WIDGETS_RES_ID);
+ return waitForLauncherObject(WORKSPACE_RES_ID);
+ }
+ case WIDGETS: {
+ waitUntilGone(WORKSPACE_RES_ID);
+ waitUntilGone(APPS_RES_ID);
+ waitUntilGone(OVERVIEW_RES_ID);
+ return waitForLauncherObject(WIDGETS_RES_ID);
+ }
+ case ALL_APPS: {
+ waitUntilGone(OVERVIEW_RES_ID);
+ waitUntilGone(WORKSPACE_RES_ID);
+ waitUntilGone(WIDGETS_RES_ID);
+ return waitForLauncherObject(APPS_RES_ID);
+ }
+ case OVERVIEW: {
+ //waitForLauncherObject(APPS_RES_ID);
+ waitUntilGone(WORKSPACE_RES_ID);
+ waitUntilGone(WIDGETS_RES_ID);
+ return waitForLauncherObject(OVERVIEW_RES_ID);
+ }
+ case BACKGROUND: {
+ waitUntilGone(WORKSPACE_RES_ID);
+ waitUntilGone(APPS_RES_ID);
+ waitUntilGone(OVERVIEW_RES_ID);
+ waitUntilGone(WIDGETS_RES_ID);
+ return null;
+ }
+ default:
+ fail("Invalid state: " + state);
+ return null;
+ }
+ }
+
+ /**
+ * Presses nav bar home button.
+ *
+ * @return the Home object.
+ */
+ public Home pressHome() {
+ getSystemUiObject("home").click();
+ return getHome();
+ }
+
+ /**
+ * Gets the Home object if the current state is "active home", i.e. workspace. Fails if the
+ * launcher is not in that state.
+ *
+ * @return Home object.
+ */
+ @NonNull
+ public Home getHome() {
+ return new Home(this);
+ }
+
+ /**
+ * Gets the Widgets object if the current state is showing all widgets. Fails if the launcher is
+ * not in that state.
+ *
+ * @return Widgets object.
+ */
+ @NonNull
+ public Widgets getAllWidgets() {
+ return new Widgets(this);
+ }
+
+ /**
+ * Gets the Overview object if the current state is showing the overview panel. Fails if the
+ * launcher is not in that state.
+ *
+ * @return Overview object.
+ */
+ @NonNull
+ public Overview getOverview() {
+ return new Overview(this);
+ }
+
+ /**
+ * Gets the All Apps object if the current state is showing the all apps panel. Fails if the
+ * launcher is not in that state.
+ *
+ * @return All Aps object.
+ */
+ @NonNull
+ public AllAppsFromHome getAllApps() {
+ return new AllAppsFromHome(this);
+ }
+
+ /**
+ * Gets the All Apps object if the current state is showing the all apps panel. Returns null if
+ * the launcher is not in that state.
+ *
+ * @return All Aps object or null.
+ */
+ @Nullable
+ public AllAppsFromHome tryGetAllApps() {
+ return tryGetLauncherObject(APPS_RES_ID) != null ? getAllApps() : null;
+ }
+
+ private void waitUntilGone(String resId) {
+// assertTrue("Unexpected launcher object visible: " + resId,
+// mDevice.wait(Until.gone(getLauncherObjectSelector(resId)),
+// UI_OBJECT_WAIT_TIMEOUT_MS));
+ }
+
+ @NonNull
+ UiObject2 getSystemUiObject(String resId) {
+ try {
+ mDevice.wakeUp();
+ } catch (RemoteException e) {
+ fail("Failed to wake up the device: " + e);
+ }
+ final UiObject2 object = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, resId));
+ assertNotNull("Can't find a systemui object with id: " + resId, object);
+ return object;
+ }
+
+ @NonNull
+ UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
+ final UiObject2 object = container.findObject(selector);
+ assertNotNull("Can't find an object with selector: " + selector, object);
+ return object;
+ }
+
+ @Nullable
+ private UiObject2 tryGetLauncherObject(String resName) {
+ return mDevice.findObject(getLauncherObjectSelector(resName));
+ }
+
+ @NonNull
+ UiObject2 waitForObjectInContainer(UiObject2 container, String resName) {
+ final UiObject2 object = container.wait(
+ Until.findObject(getLauncherObjectSelector(resName)),
+ UI_OBJECT_WAIT_TIMEOUT_MS);
+ assertNotNull("Can find a launcher object id: " + resName + " in container: " +
+ container.getResourceName(), object);
+ return object;
+ }
+
+ @NonNull
+ UiObject2 waitForLauncherObject(String resName) {
+ final UiObject2 object = mDevice.wait(Until.findObject(getLauncherObjectSelector(resName)),
+ UI_OBJECT_WAIT_TIMEOUT_MS);
+ assertNotNull("Can find a launcher object; id: " + resName, object);
+ return object;
+ }
+
+ static BySelector getLauncherObjectSelector(String resName) {
+ return By.res(LAUNCHER_PKG, resName);
+ }
+
+ @NonNull
+ UiDevice getDevice() {
+ return mDevice;
+ }
+
+ void swipe(int startX, int startY, int endX, int endY, int steps) {
+ mDevice.swipe(startX, startY, endX, endY, steps);
+ waitForIdle();
+ }
+
+ void waitForIdle() {
+ mDevice.waitForIdle();
+ }
+} \ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
new file mode 100644
index 000000000..225165571
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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 android.graphics.Point;
+import android.support.annotation.NonNull;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Overview pane.
+ */
+public final class Overview {
+ private static final int DEFAULT_FLING_SPEED = 15000;
+
+ private final Launcher mLauncher;
+
+ Overview(Launcher launcher) {
+ mLauncher = launcher;
+ assertState();
+ }
+
+ /**
+ * Asserts that we are in overview.
+ *
+ * @return Overview panel.
+ */
+ @NonNull
+ private UiObject2 assertState() {
+ return mLauncher.assertState(Launcher.State.OVERVIEW);
+ }
+
+ /**
+ * Flings forward (left) and waits the fling's end.
+ */
+ public void flingForward() {
+ final UiObject2 overview = assertState();
+ overview.fling(Direction.LEFT, DEFAULT_FLING_SPEED);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ /**
+ * Flings backward (right) and waits the fling's end.
+ */
+ public void flingBackward() {
+ final UiObject2 overview = assertState();
+ overview.fling(Direction.RIGHT, DEFAULT_FLING_SPEED);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ /**
+ * Gets the current task in the carousel, or fails if the carousel is empty.
+ *
+ * @return the task in the middle of the visible tasks list.
+ */
+ @NonNull
+ public OverviewTask getCurrentTask() {
+ assertState();
+ final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
+ Launcher.getLauncherObjectSelector("snapshot"));
+ 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(t1.getVisibleBounds().width(),
+ t2.getVisibleBounds().width()));
+
+ return new OverviewTask(mLauncher, widestTask);
+ }
+
+ /**
+ * Swipes up to All Apps.
+ *
+ * @return the App Apps object.
+ */
+ @NonNull
+ public AllAppsFromOverview switchToAllApps() {
+ assertState();
+
+ // Swipe from the hotseat to near the top, e.g. 10% of the screen.
+ final UiObject2 predictionRow = mLauncher.waitForLauncherObject(
+ "prediction_row");
+ final Point start = predictionRow.getVisibleCenter();
+ final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f);
+ mLauncher.swipe(
+ start.x, start.y, start.x, endY, (start.y - endY) / 100); // 100 px/step
+
+ return new AllAppsFromOverview(mLauncher);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
new file mode 100644
index 000000000..68d308251
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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 android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+/**
+ * A recent task in the overview panel carousel.
+ */
+public final class OverviewTask {
+ private final Launcher mLauncher;
+ private final UiObject2 mTask;
+
+ OverviewTask(Launcher launcher, UiObject2 task) {
+ mLauncher = launcher;
+ assertState();
+ mTask = task;
+ }
+
+ /**
+ * Asserts that we are in overview.
+ *
+ * @return Overview panel.
+ */
+ private void assertState() {
+ mLauncher.assertState(Launcher.State.OVERVIEW);
+ }
+
+ /**
+ * Swipes the task up.
+ */
+ public void dismiss() {
+ assertState();
+ // Dismiss the task via flinging it up.
+ mTask.fling(Direction.DOWN);
+ mLauncher.waitForIdle();
+ }
+
+ /**
+ * Clicks at the task.
+ */
+ public void open() {
+ assertState();
+ mLauncher.assertTrue("Launching task didn't open a new window: " +
+ mTask.getParent().getContentDescription(),
+ mTask.clickAndWait(Until.newWindow(), Launcher.APP_LAUNCH_TIMEOUT_MS));
+ mLauncher.assertState(Launcher.State.BACKGROUND);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
new file mode 100644
index 000000000..7a5198a80
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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 android.support.annotation.NonNull;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
+
+/**
+ * All widgets container.
+ */
+public final class Widgets {
+ private static final int FLING_SPEED = 12000;
+
+ private final Launcher mLauncher;
+
+ Widgets(Launcher launcher) {
+ mLauncher = launcher;
+ assertState();
+ }
+
+ /**
+ * Flings forward (down) and waits the fling's end.
+ */
+ public void flingForward() {
+ final UiObject2 widgetsContainer = assertState();
+ widgetsContainer.fling(Direction.DOWN, FLING_SPEED);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ /**
+ * Flings backward (up) and waits the fling's end.
+ */
+ public void flingBackward() {
+ final UiObject2 widgetsContainer = assertState();
+ widgetsContainer.fling(Direction.UP, FLING_SPEED);
+ mLauncher.waitForIdle();
+ assertState();
+ }
+
+ /**
+ * Asserts that we are in widgets.
+ *
+ * @return Widgets container.
+ */
+ @NonNull
+ private UiObject2 assertState() {
+ return mLauncher.assertState(Launcher.State.WIDGETS);
+ }
+}