• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.tapl;
18 
19 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
20 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
21 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
22 
23 import static junit.framework.TestCase.assertTrue;
24 
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.os.SystemClock;
28 import android.view.KeyEvent;
29 import android.view.MotionEvent;
30 
31 import androidx.annotation.NonNull;
32 import androidx.annotation.Nullable;
33 import androidx.test.uiautomator.By;
34 import androidx.test.uiautomator.Direction;
35 import androidx.test.uiautomator.UiObject2;
36 
37 import com.android.launcher3.ResourceUtils;
38 import com.android.launcher3.testing.TestProtocol;
39 
40 import java.util.regex.Pattern;
41 
42 /**
43  * Operations on the workspace screen.
44  */
45 public final class Workspace extends Home {
46     private static final int FLING_STEPS = 10;
47 
48     static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile(
49             "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W"
50                     + ".*?metaState=META_CTRL_ON");
51     static final Pattern EVENT_CTRL_W_UP = Pattern.compile(
52             "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_W"
53                     + ".*?metaState=META_CTRL_ON");
54     private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick");
55 
56     private final UiObject2 mHotseat;
57 
Workspace(LauncherInstrumentation launcher)58     Workspace(LauncherInstrumentation launcher) {
59         super(launcher);
60         mHotseat = launcher.waitForLauncherObject("hotseat");
61     }
62 
63     /**
64      * Swipes up to All Apps.
65      *
66      * @return the App Apps object.
67      */
68     @NonNull
switchToAllApps()69     public AllApps switchToAllApps() {
70         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
71              LauncherInstrumentation.Closable c =
72                      mLauncher.addContextLayer("want to switch from workspace to all apps")) {
73             verifyActiveContainer();
74             final int deviceHeight = mLauncher.getDevice().getDisplayHeight();
75             final int bottomGestureMargin = ResourceUtils.getNavbarSize(
76                     ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources());
77             final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
78             final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
79             final int swipeHeight = mLauncher.getTestInfo(
80                     TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
81                     getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
82             LauncherInstrumentation.log(
83                     "switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY
84                             + ", swipeHeight = " + swipeHeight + ", slop = "
85                             + mLauncher.getTouchSlop());
86 
87             mLauncher.swipeToState(
88                     0,
89                     startY,
90                     0,
91                     startY - swipeHeight - mLauncher.getTouchSlop(),
92                     12,
93                     ALL_APPS_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE);
94 
95             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
96                     "swiped to all apps")) {
97                 return new AllApps(mLauncher);
98             }
99         }
100     }
101 
102     /**
103      * Returns an icon for the app, if currently visible.
104      *
105      * @param appName name of the app
106      * @return app icon, if found, null otherwise.
107      */
108     @Nullable
tryGetWorkspaceAppIcon(String appName)109     public AppIcon tryGetWorkspaceAppIcon(String appName) {
110         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
111                 "want to get a workspace icon")) {
112             final UiObject2 workspace = verifyActiveContainer();
113             final UiObject2 icon = workspace.findObject(
114                     AppIcon.getAppIconSelector(appName, mLauncher));
115             return icon != null ? new AppIcon(mLauncher, icon) : null;
116         }
117     }
118 
119 
120     /**
121      * Returns an icon for the app; fails if the icon doesn't exist.
122      *
123      * @param appName name of the app
124      * @return app icon.
125      */
126     @NonNull
getWorkspaceAppIcon(String appName)127     public AppIcon getWorkspaceAppIcon(String appName) {
128         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
129                 "want to get a workspace icon")) {
130             return new AppIcon(mLauncher,
131                     mLauncher.waitForObjectInContainer(
132                             verifyActiveContainer(),
133                             AppIcon.getAppIconSelector(appName, mLauncher)));
134         }
135     }
136 
137     /**
138      * Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the
139      * second screen.
140      */
ensureWorkspaceIsScrollable()141     public void ensureWorkspaceIsScrollable() {
142         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
143             final UiObject2 workspace = verifyActiveContainer();
144             if (!isWorkspaceScrollable(workspace)) {
145                 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
146                         "dragging icon to a second page of workspace to make it scrollable")) {
147                     dragIconToWorkspace(
148                             mLauncher,
149                             getHotseatAppIcon("Chrome"),
150                             new Point(mLauncher.getDevice().getDisplayWidth(),
151                                     mLauncher.getVisibleBounds(workspace).centerY()),
152                             "popup_container",
153                             false,
154                             false,
155                             () -> mLauncher.expectEvent(
156                                     TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT));
157                     verifyActiveContainer();
158                 }
159             }
160             assertTrue("Home screen workspace didn't become scrollable",
161                     isWorkspaceScrollable(workspace));
162         }
163     }
164 
isWorkspaceScrollable(UiObject2 workspace)165     private boolean isWorkspaceScrollable(UiObject2 workspace) {
166         return workspace.getChildCount() > 1;
167     }
168 
169     @NonNull
getHotseatAppIcon(String appName)170     public AppIcon getHotseatAppIcon(String appName) {
171         return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
172                 mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
173     }
174 
dragIconToWorkspace( LauncherInstrumentation launcher, Launchable launchable, Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut, Runnable expectLongClickEvents)175     static void dragIconToWorkspace(
176             LauncherInstrumentation launcher, Launchable launchable, Point dest,
177             String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
178             Runnable expectLongClickEvents) {
179         LauncherInstrumentation.log("dragIconToWorkspace: begin");
180         final Point launchableCenter = launchable.getObject().getVisibleCenter();
181         final long downTime = SystemClock.uptimeMillis();
182         launcher.runToState(
183                 () -> {
184                     launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
185                             launchableCenter, LauncherInstrumentation.GestureScope.INSIDE);
186                     LauncherInstrumentation.log("dragIconToWorkspace: sent down");
187                     expectLongClickEvents.run();
188                     launcher.waitForLauncherObject(longPressIndicator);
189                     LauncherInstrumentation.log("dragIconToWorkspace: indicator");
190                     launcher.movePointer(launchableCenter, dest, 10, downTime, true,
191                             LauncherInstrumentation.GestureScope.INSIDE);
192                 },
193                 SPRING_LOADED_STATE_ORDINAL,
194                 "long-pressing and moving");
195         LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
196         launcher.runToState(
197                 () -> launcher.sendPointer(
198                         downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
199                         LauncherInstrumentation.GestureScope.INSIDE),
200                 NORMAL_STATE_ORDINAL,
201                 "sending UP event");
202         if (startsActivity || isWidgetShortcut) {
203             launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
204         }
205         LauncherInstrumentation.log("dragIconToWorkspace: end");
206         launcher.waitUntilLauncherObjectGone("drop_target_bar");
207     }
208 
209     /**
210      * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
211      * recoil to complete.
212      */
flingForward()213     public void flingForward() {
214         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
215             final UiObject2 workspace = verifyActiveContainer();
216             mLauncher.scroll(workspace, Direction.RIGHT,
217                     new Rect(0, 0, mLauncher.getEdgeSensitivityWidth() + 1, 0),
218                     FLING_STEPS, false);
219             verifyActiveContainer();
220         }
221     }
222 
223     /**
224      * Flings to get to screens on the left.  Waits for scrolling and a possible overscroll
225      * recoil to complete.
226      */
flingBackward()227     public void flingBackward() {
228         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
229             final UiObject2 workspace = verifyActiveContainer();
230             mLauncher.scroll(workspace, Direction.LEFT,
231                     new Rect(mLauncher.getEdgeSensitivityWidth() + 1, 0, 0, 0),
232                     FLING_STEPS, false);
233             verifyActiveContainer();
234         }
235     }
236 
237     /**
238      * Opens widgets container by pressing Ctrl+W.
239      *
240      * @return the widgets container.
241      */
242     @NonNull
openAllWidgets()243     public Widgets openAllWidgets() {
244         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
245             verifyActiveContainer();
246             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_DOWN);
247             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_UP);
248             mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
249             try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
250                 return new Widgets(mLauncher);
251             }
252         }
253     }
254 
255     @Override
getSwipeHeightRequestName()256     protected String getSwipeHeightRequestName() {
257         return TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT;
258     }
259 
260     @Override
getSwipeStartY()261     protected int getSwipeStartY() {
262         return mLauncher.getRealDisplaySize().y - 1;
263     }
264 
265     @Nullable
tryGetWidget(String label, long timeout)266     public Widget tryGetWidget(String label, long timeout) {
267         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
268                 "getting widget " + label + " on workspace with timeout " + timeout)) {
269             final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
270                     By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView").desc(label),
271                     timeout);
272             return widget != null ? new Widget(mLauncher, widget) : null;
273         }
274     }
275 
276     @Nullable
tryGetPendingWidget(long timeout)277     public Widget tryGetPendingWidget(long timeout) {
278         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
279                 "getting pending widget on workspace with timeout " + timeout)) {
280             final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
281                     By.clazz("com.android.launcher3.widget.PendingAppWidgetHostView"), timeout);
282             return widget != null ? new Widget(mLauncher, widget) : null;
283         }
284     }
285 }