Merge "UNREVIEWED: TAPL prototype for in-lab flake test" into ub-launcher3-edmonton-polish

This commit is contained in:
TreeHugger Robot 2018-06-18 23:10:06 +00:00 committed by Android (Google) Code Review
commit 3471127e8b
8 changed files with 989 additions and 0 deletions

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}