Tapl library
The public API of the library has finalized; flakiness has been removed; code polished. Bug: 110103162 Test: TaplTests suite Change-Id: Ic156bbfeedb1cb9c4a48ef907f97e396e8e81936
This commit is contained in:
@@ -57,6 +57,7 @@ import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.uioverrides.FastOverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
@@ -230,6 +231,8 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
||||
|
||||
// Optimization, hide the all apps view to prevent layout while initializing
|
||||
activity.getAppsView().getContentView().setVisibility(View.GONE);
|
||||
|
||||
AccessibilityManagerCompat.sendEventToTest(activity, "TAPL_WENT_TO_STATE");
|
||||
}
|
||||
|
||||
return new AnimationFactory() {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -130,6 +131,9 @@ public final class Utilities {
|
||||
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
|
||||
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
|
||||
|
||||
public static final boolean IS_RUNNING_IN_TEST_HARNESS =
|
||||
ActivityManager.isRunningInTestHarness();
|
||||
|
||||
public static boolean isPropertyEnabled(String propertyName) {
|
||||
return Log.isLoggable(propertyName, Log.VERBOSE);
|
||||
}
|
||||
|
||||
@@ -1034,7 +1034,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
}
|
||||
|
||||
protected void onScrollInteractionBegin() {
|
||||
super.onScrollInteractionEnd();
|
||||
super.onScrollInteractionBegin();
|
||||
mScrollInteractionBegan = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package com.android.launcher3.compat;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
public class AccessibilityManagerCompat {
|
||||
|
||||
public static boolean isAccessibilityEnabled(Context context) {
|
||||
@@ -44,4 +47,19 @@ public class AccessibilityManagerCompat {
|
||||
private static AccessibilityManager getManager(Context context) {
|
||||
return (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
}
|
||||
|
||||
public static void sendEventToTest(Context context, String eventTag) {
|
||||
if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return;
|
||||
|
||||
final AccessibilityManager accessibilityManager = getManager(context);
|
||||
if (accessibilityManager.isEnabled() &&
|
||||
accessibilityManager.getEnabledAccessibilityServiceList(
|
||||
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).size() == 0) {
|
||||
|
||||
final AccessibilityEvent e = AccessibilityEvent.obtain(
|
||||
AccessibilityEvent.TYPE_ANNOUNCEMENT);
|
||||
e.setClassName(eventTag);
|
||||
accessibilityManager.sendAccessibilityEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
@@ -515,6 +516,8 @@ public abstract class AbstractStateChangeTouchController
|
||||
logReachedState(logAction, targetState);
|
||||
}
|
||||
mLauncher.getStateManager().goToState(targetState, false /* animated */);
|
||||
|
||||
AccessibilityManagerCompat.sendEventToTest(mLauncher, "TAPL_WENT_TO_STATE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AttributeSet;
|
||||
@@ -133,6 +134,11 @@ public class OptionsPopupView extends ArrowPopup
|
||||
popup.reorderAndShow(popup.getChildCount());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static OptionsPopupView getOptionsPopup(Launcher launcher) {
|
||||
return launcher.findViewById(R.id.deep_shortcuts_container);
|
||||
}
|
||||
|
||||
public static void showDefaultOptions(Launcher launcher, float x, float y) {
|
||||
float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
|
||||
if (x < 0 || y < 0) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -224,6 +225,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
|
||||
return launcher.findViewById(R.id.widgets_list_view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getElementsRowCount() {
|
||||
return mAdapter.getItemCount();
|
||||
|
||||
+29
-29
@@ -16,36 +16,32 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
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.
|
||||
* Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
|
||||
*/
|
||||
public final class AllAppsFromHome {
|
||||
public class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
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();
|
||||
AllApps(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
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);
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.ALL_APPS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,41 +53,45 @@ public final class AllAppsFromHome {
|
||||
*/
|
||||
@NonNull
|
||||
public AppIcon getAppIcon(String appName) {
|
||||
final UiObject2 allAppsContainer = assertState();
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
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,
|
||||
assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
|
||||
++attempts <= MAX_SCROLL_ATTEMPTS);
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
|
||||
final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer, appIconSelector);
|
||||
ensureIconVisible(appIcon, allAppsContainer);
|
||||
return new AppIcon(mLauncher, appIcon);
|
||||
}
|
||||
|
||||
protected int getBottomMarginForSwipeUp() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
private void scrollBackToBeginning() {
|
||||
final UiObject2 allAppsContainer = assertState();
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
|
||||
int attempts = 0;
|
||||
allAppsContainer.setGestureMargins(5, 500, 5, 5);
|
||||
allAppsContainer.setGestureMargins(5, 600, 5, getBottomMarginForSwipeUp());
|
||||
|
||||
while (allAppsContainer.scroll(Direction.UP, 0.5f)) {
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
|
||||
mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
|
||||
assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
|
||||
++attempts <= MAX_SCROLL_ATTEMPTS);
|
||||
}
|
||||
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
private void ensureIconVisible(UiObject2 appIcon, UiObject2 allAppsContainer) {
|
||||
@@ -102,7 +102,7 @@ public final class AllAppsFromHome {
|
||||
final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f);
|
||||
allAppsContainer.scroll(Direction.DOWN, pct);
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,22 +110,22 @@ public final class AllAppsFromHome {
|
||||
* Flings forward (down) and waits the fling's end.
|
||||
*/
|
||||
public void flingForward() {
|
||||
final UiObject2 allAppsContainer = assertState();
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
// 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();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings backward (up) and waits the fling's end.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
final UiObject2 allAppsContainer = assertState();
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
// Start the gesture in the center, for symmetry with forward.
|
||||
allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
|
||||
allAppsContainer.fling(Direction.UP, FLING_SPEED);
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,6 +137,6 @@ public final class AllAppsFromHome {
|
||||
@Deprecated
|
||||
@NonNull
|
||||
public UiObject2 getObjectDeprecated() {
|
||||
return assertState();
|
||||
return verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
@@ -22,24 +22,17 @@ 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;
|
||||
public final class AllAppsFromOverview extends AllApps {
|
||||
|
||||
AllAppsFromOverview(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
assertState();
|
||||
AllAppsFromOverview(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that we are in all apps.
|
||||
*
|
||||
* @return All apps container.
|
||||
*/
|
||||
@NonNull
|
||||
private UiObject2 assertState() {
|
||||
return mLauncher.assertState(Launcher.State.ALL_APPS);
|
||||
@Override
|
||||
protected int getBottomMarginForSwipeUp() {
|
||||
return 600;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,13 +42,13 @@ public final class AllAppsFromOverview {
|
||||
*/
|
||||
@NonNull
|
||||
public Overview switchBackToOverview() {
|
||||
final UiObject2 allAppsContainer = assertState();
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
// 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
|
||||
mLauncher.swipe(start.x, start.y, start.x, endY);
|
||||
|
||||
return new Overview(mLauncher);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.support.test.uiautomator.By;
|
||||
import android.support.test.uiautomator.BySelector;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
@@ -26,25 +28,25 @@ import android.widget.TextView;
|
||||
* App icon, whether in all apps or in workspace/
|
||||
*/
|
||||
public final class AppIcon {
|
||||
private final Launcher mLauncher;
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mIcon;
|
||||
|
||||
AppIcon(Launcher launcher, UiObject2 icon) {
|
||||
AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
|
||||
mLauncher = launcher;
|
||||
mIcon = icon;
|
||||
}
|
||||
|
||||
static BySelector getAppIconSelector(String appName) {
|
||||
return By.clazz(TextView.class).text(appName).pkg(Launcher.LAUNCHER_PKG);
|
||||
return By.clazz(TextView.class).text(appName).pkg(LauncherInstrumentation.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);
|
||||
public Background launch() {
|
||||
assertTrue("Launching an app didn't open a new window: " + mIcon.getText(),
|
||||
mIcon.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
|
||||
return new Background(mLauncher);
|
||||
}
|
||||
|
||||
UiObject2 getIcon() {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Operations on a state when Launcher is inactive because some other app is active.
|
||||
*/
|
||||
public final class Background extends Home {
|
||||
|
||||
Background(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.BACKGROUND;
|
||||
}
|
||||
}
|
||||
@@ -16,38 +16,22 @@
|
||||
|
||||
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.
|
||||
*
|
||||
* Launcher can be invoked both when its activity is in the foreground and when it is in the
|
||||
* background. This class is a parent of the two classes {@link Background} and {@link Workspace}
|
||||
* that essentially represents these two activity states. Any gestures (e.g., switchToOverview) that
|
||||
* can be performed in both of these states can be defined here.
|
||||
*/
|
||||
public final class Home {
|
||||
public abstract class Home extends LauncherInstrumentation.VisibleContainer {
|
||||
|
||||
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);
|
||||
protected Home(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,131 +41,19 @@ public final class Home {
|
||||
*/
|
||||
@NonNull
|
||||
public Overview switchToOverview() {
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
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
|
||||
navBar.getVisibleBounds().centerX(), height - 300
|
||||
);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
+129
-96
@@ -16,10 +16,13 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.UiAutomation;
|
||||
import android.content.res.Resources;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -29,50 +32,76 @@ 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;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
public final class LauncherInstrumentation {
|
||||
|
||||
// Types for launcher containers that the user is interacting with. "Background" is a
|
||||
// pseudo-container corresponding to inactive launcher covered by another app.
|
||||
enum ContainerType {
|
||||
WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND
|
||||
}
|
||||
|
||||
// Base class for launcher containers.
|
||||
static abstract class VisibleContainer {
|
||||
protected final LauncherInstrumentation mLauncher;
|
||||
|
||||
protected VisibleContainer(LauncherInstrumentation launcher) {
|
||||
mLauncher = launcher;
|
||||
launcher.setActiveContainer(this);
|
||||
}
|
||||
|
||||
protected abstract ContainerType getContainerType();
|
||||
|
||||
/**
|
||||
* Asserts that the launcher is in the mode matching 'this' object.
|
||||
*
|
||||
* @return UI object for the container.
|
||||
*/
|
||||
final UiObject2 verifyActiveContainer() {
|
||||
assertTrue("Attempt to use a stale container", this == sActiveContainer.get());
|
||||
return mLauncher.verifyContainerType(getContainerType());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
static final int WAIT_TIME_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 static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
|
||||
|
||||
private final UiDevice mDevice;
|
||||
private final boolean mSwipeUpEnabled;
|
||||
|
||||
/**
|
||||
* Constructs the root of TAPL hierarchy. You get all other object from it.
|
||||
* Constructs the root of TAPL hierarchy. You get all other objects from it.
|
||||
*/
|
||||
public Launcher(UiDevice device) {
|
||||
public LauncherInstrumentation(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,
|
||||
"swipe_up_to_switch_apps_enabled",
|
||||
swipeUpEnabledDefault ? 1 : 0) == 1;
|
||||
assertTrue("Device must run in a test harness", ActivityManager.isRunningInTestHarness());
|
||||
}
|
||||
|
||||
private boolean getSystemBooleanRes(String resName) {
|
||||
@@ -82,59 +111,18 @@ public final class Launcher {
|
||||
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);
|
||||
}
|
||||
void setActiveContainer(VisibleContainer container) {
|
||||
sActiveContainer = new WeakReference<>(container);
|
||||
}
|
||||
|
||||
boolean isSwipeUpEnabled() {
|
||||
return mSwipeUpEnabled;
|
||||
}
|
||||
|
||||
UiObject2 assertState(State state) {
|
||||
switch (state) {
|
||||
case HOME: {
|
||||
//waitUntilGone(APPS_RES_ID);
|
||||
private UiObject2 verifyContainerType(ContainerType containerType) {
|
||||
switch (containerType) {
|
||||
case WORKSPACE: {
|
||||
waitUntilGone(APPS_RES_ID);
|
||||
waitUntilGone(OVERVIEW_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
return waitForLauncherObject(WORKSPACE_RES_ID);
|
||||
@@ -147,13 +135,11 @@ public final class Launcher {
|
||||
}
|
||||
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);
|
||||
waitForLauncherObject(APPS_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
return waitForLauncherObject(OVERVIEW_RES_ID);
|
||||
}
|
||||
@@ -165,30 +151,62 @@ public final class Launcher {
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
fail("Invalid state: " + state);
|
||||
fail("Invalid state: " + containerType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAndWaitForEvent(Runnable command,
|
||||
UiAutomation.AccessibilityEventFilter eventFilter, String message) {
|
||||
try {
|
||||
assertNotNull("executeAndWaitForEvent returned null (this can't happen)",
|
||||
InstrumentationRegistry.getInstrumentation().getUiAutomation()
|
||||
.executeAndWaitForEvent(
|
||||
command, eventFilter, WAIT_TIME_MS));
|
||||
} catch (TimeoutException e) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Presses nav bar home button.
|
||||
*
|
||||
* @return the Home object.
|
||||
* @return the Workspace object.
|
||||
*/
|
||||
public Home pressHome() {
|
||||
getSystemUiObject("home").click();
|
||||
return getHome();
|
||||
public Workspace pressHome() {
|
||||
// Click home, then wait for any accessibility event, then wait until accessibility events
|
||||
// stop.
|
||||
// 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(
|
||||
() -> getSystemUiObject("home").click(),
|
||||
event -> true,
|
||||
"Pressing Home didn't produce any events");
|
||||
mDevice.waitForIdle();
|
||||
return getWorkspace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Home object if the current state is "active home", i.e. workspace. Fails if the
|
||||
* Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the
|
||||
* launcher is not in that state.
|
||||
*
|
||||
* @return Home object.
|
||||
* @return Workspace object.
|
||||
*/
|
||||
@NonNull
|
||||
public Home getHome() {
|
||||
return new Home(this);
|
||||
public Workspace getWorkspace() {
|
||||
return new Workspace(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Workspace object if the current state is "background home", i.e. some other app is
|
||||
* active. Fails if the launcher is not in that state.
|
||||
*
|
||||
* @return Background object.
|
||||
*/
|
||||
@NonNull
|
||||
public Background getBackground() {
|
||||
return new Background(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,40 +232,52 @@ public final class Launcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the All Apps object if the current state is showing the all apps panel. Fails if the
|
||||
* launcher is not in that state.
|
||||
* Gets the All Apps object if the current state is showing the all apps panel opened by swiping
|
||||
* from workspace. Fails if the launcher is not in that state. Please don't call this method if
|
||||
* App Apps was opened by swiping up from Overview, as it won't fail and will return an
|
||||
* incorrect object.
|
||||
*
|
||||
* @return All Aps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllAppsFromHome getAllApps() {
|
||||
return new AllAppsFromHome(this);
|
||||
public AllApps getAllApps() {
|
||||
return new AllApps(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.
|
||||
* Gets the All Apps object if the current state is showing the all apps panel opened by swiping
|
||||
* from overview. Fails if the launcher is not in that state. Please don't call this method if
|
||||
* App Apps was opened by swiping up from home, as it won't fail and will return an
|
||||
* incorrect object.
|
||||
*
|
||||
* @return All Aps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllAppsFromOverview getAllAppsFromOverview() {
|
||||
return new AllAppsFromOverview(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the All Apps object if the current state is showing the all apps panel opened by swiping
|
||||
* from workspace. Returns null if launcher is not in that state. Please don't call this method
|
||||
* if App Apps was opened by swiping up from Overview, as it won't fail and will return an
|
||||
* incorrect object.
|
||||
*
|
||||
* @return All Aps object or null.
|
||||
*/
|
||||
@Nullable
|
||||
public AllAppsFromHome tryGetAllApps() {
|
||||
public AllApps 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));
|
||||
assertTrue("Unexpected launcher object visible: " + resId,
|
||||
mDevice.wait(Until.gone(getLauncherObjectSelector(resId)),
|
||||
WAIT_TIME_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;
|
||||
@@ -269,8 +299,8 @@ public final class Launcher {
|
||||
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: " +
|
||||
WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a launcher object id: " + resName + " in container: " +
|
||||
container.getResourceName(), object);
|
||||
return object;
|
||||
}
|
||||
@@ -278,8 +308,8 @@ public final class Launcher {
|
||||
@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);
|
||||
WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a launcher object; id: " + resName, object);
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -292,9 +322,12 @@ public final class Launcher {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
void swipe(int startX, int startY, int endX, int endY, int steps) {
|
||||
mDevice.swipe(startX, startY, endX, endY, steps);
|
||||
waitForIdle();
|
||||
void swipe(int startX, int startY, int endX, int endY) {
|
||||
executeAndWaitForEvent(
|
||||
() -> mDevice.swipe(startX, startY, endX, endY, 60),
|
||||
event -> "TAPL_WENT_TO_STATE".equals(event.getClassName()),
|
||||
"Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
|
||||
+ ", " + endX + ", " + endY);
|
||||
}
|
||||
|
||||
void waitForIdle() {
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.test.uiautomator.Direction;
|
||||
@@ -27,44 +29,37 @@ import java.util.List;
|
||||
/**
|
||||
* Overview pane.
|
||||
*/
|
||||
public final class Overview {
|
||||
public final class Overview extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int DEFAULT_FLING_SPEED = 15000;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
Overview(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
assertState();
|
||||
Overview(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that we are in overview.
|
||||
*
|
||||
* @return Overview panel.
|
||||
*/
|
||||
@NonNull
|
||||
private UiObject2 assertState() {
|
||||
return mLauncher.assertState(Launcher.State.OVERVIEW);
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.OVERVIEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings forward (left) and waits the fling's end.
|
||||
*/
|
||||
public void flingForward() {
|
||||
final UiObject2 overview = assertState();
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
overview.fling(Direction.LEFT, DEFAULT_FLING_SPEED);
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings backward (right) and waits the fling's end.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
final UiObject2 overview = assertState();
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
overview.fling(Direction.RIGHT, DEFAULT_FLING_SPEED);
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,10 +69,10 @@ public final class Overview {
|
||||
*/
|
||||
@NonNull
|
||||
public OverviewTask getCurrentTask() {
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
|
||||
Launcher.getLauncherObjectSelector("snapshot"));
|
||||
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
|
||||
LauncherInstrumentation.getLauncherObjectSelector("snapshot"));
|
||||
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
|
||||
@@ -86,7 +81,7 @@ public final class Overview {
|
||||
(t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
|
||||
t2.getVisibleBounds().width()));
|
||||
|
||||
return new OverviewTask(mLauncher, widestTask);
|
||||
return new OverviewTask(mLauncher, widestTask, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +91,7 @@ public final class Overview {
|
||||
*/
|
||||
@NonNull
|
||||
public AllAppsFromOverview switchToAllApps() {
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
|
||||
// Swipe from the hotseat to near the top, e.g. 10% of the screen.
|
||||
final UiObject2 predictionRow = mLauncher.waitForLauncherObject(
|
||||
@@ -104,7 +99,7 @@ public final class Overview {
|
||||
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
|
||||
start.x, start.y, start.x, endY);
|
||||
|
||||
return new AllAppsFromOverview(mLauncher);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.support.test.uiautomator.Direction;
|
||||
import android.support.test.uiautomator.UiObject2;
|
||||
import android.support.test.uiautomator.Until;
|
||||
@@ -24,29 +26,26 @@ import android.support.test.uiautomator.Until;
|
||||
* A recent task in the overview panel carousel.
|
||||
*/
|
||||
public final class OverviewTask {
|
||||
private final Launcher mLauncher;
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mTask;
|
||||
private final Overview mOverview;
|
||||
|
||||
OverviewTask(Launcher launcher, UiObject2 task) {
|
||||
OverviewTask(LauncherInstrumentation launcher, UiObject2 task, Overview overview) {
|
||||
mLauncher = launcher;
|
||||
assertState();
|
||||
mTask = task;
|
||||
mOverview = overview;
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that we are in overview.
|
||||
*
|
||||
* @return Overview panel.
|
||||
*/
|
||||
private void assertState() {
|
||||
mLauncher.assertState(Launcher.State.OVERVIEW);
|
||||
private void verifyActiveContainer() {
|
||||
mOverview.verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes the task up.
|
||||
*/
|
||||
public void dismiss() {
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
// Dismiss the task via flinging it up.
|
||||
mTask.fling(Direction.DOWN);
|
||||
mLauncher.waitForIdle();
|
||||
@@ -55,11 +54,11 @@ public final class OverviewTask {
|
||||
/**
|
||||
* Clicks at the task.
|
||||
*/
|
||||
public void open() {
|
||||
assertState();
|
||||
mLauncher.assertTrue("Launching task didn't open a new window: " +
|
||||
public Background open() {
|
||||
verifyActiveContainer();
|
||||
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);
|
||||
mTask.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
|
||||
return new Background(mLauncher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,50 +16,43 @@
|
||||
|
||||
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 {
|
||||
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int FLING_SPEED = 12000;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
Widgets(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
assertState();
|
||||
Widgets(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings forward (down) and waits the fling's end.
|
||||
*/
|
||||
public void flingForward() {
|
||||
final UiObject2 widgetsContainer = assertState();
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
widgetsContainer.setGestureMargin(100);
|
||||
widgetsContainer.fling(Direction.DOWN, FLING_SPEED);
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings backward (up) and waits the fling's end.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
final UiObject2 widgetsContainer = assertState();
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
widgetsContainer.setGestureMargin(100);
|
||||
widgetsContainer.fling(Direction.UP, FLING_SPEED);
|
||||
mLauncher.waitForIdle();
|
||||
assertState();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that we are in widgets.
|
||||
*
|
||||
* @return Widgets container.
|
||||
*/
|
||||
@NonNull
|
||||
private UiObject2 assertState() {
|
||||
return mLauncher.assertState(Launcher.State.WIDGETS);
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.WIDGETS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 workspace screen.
|
||||
*/
|
||||
public final class Workspace extends Home {
|
||||
private final UiObject2 mHotseat;
|
||||
private final int ICON_DRAG_SPEED = 2000;
|
||||
|
||||
Workspace(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
mHotseat = launcher.waitForLauncherObject("hotseat");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.WORKSPACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes up to All Apps.
|
||||
*
|
||||
* @return the App Apps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllApps switchToAllApps() {
|
||||
verifyActiveContainer();
|
||||
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
|
||||
);
|
||||
} 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
|
||||
);
|
||||
}
|
||||
|
||||
return new AllApps(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 = verifyActiveContainer();
|
||||
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 = verifyActiveContainer();
|
||||
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);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings to get to screens on the right. Waits for scrolling and a possible overscroll
|
||||
* recoil to complete.
|
||||
*/
|
||||
public void flingForward() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
workspace.fling(Direction.RIGHT);
|
||||
mLauncher.waitForIdle();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings to get to screens on the left. Waits for scrolling and a possible overscroll
|
||||
* recoil to complete.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
workspace.fling(Direction.LEFT);
|
||||
mLauncher.waitForIdle();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens widgets container by pressing Ctrl+W.
|
||||
*
|
||||
* @return the widgets container.
|
||||
*/
|
||||
@NonNull
|
||||
public Widgets openAllWidgets() {
|
||||
verifyActiveContainer();
|
||||
mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
|
||||
return new Widgets(mLauncher);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user