• 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 android.graphics.Rect;
20 
21 import androidx.annotation.NonNull;
22 import androidx.test.uiautomator.By;
23 import androidx.test.uiautomator.BySelector;
24 import androidx.test.uiautomator.Direction;
25 import androidx.test.uiautomator.UiObject2;
26 
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.stream.Collectors;
30 
31 /**
32  * Common overview panel for both Launcher and fallback recents
33  */
34 public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
35     private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
36 
BaseOverview(LauncherInstrumentation launcher)37     BaseOverview(LauncherInstrumentation launcher) {
38         super(launcher);
39         verifyActiveContainer();
40         verifyActionsViewVisibility();
41     }
42 
43     @Override
getContainerType()44     protected LauncherInstrumentation.ContainerType getContainerType() {
45         return LauncherInstrumentation.ContainerType.FALLBACK_OVERVIEW;
46     }
47 
48     /**
49      * Flings forward (left) and waits the fling's end.
50      */
flingForward()51     public void flingForward() {
52         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
53             flingForwardImpl();
54         }
55     }
56 
flingForwardImpl()57     private void flingForwardImpl() {
58         try (LauncherInstrumentation.Closable c =
59                      mLauncher.addContextLayer("want to fling forward in overview")) {
60             LauncherInstrumentation.log("Overview.flingForward before fling");
61             final UiObject2 overview = verifyActiveContainer();
62             final int leftMargin =
63                     mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth();
64             mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20,
65                     false);
66             try (LauncherInstrumentation.Closable c2 =
67                          mLauncher.addContextLayer("flung forwards")) {
68                 verifyActiveContainer();
69                 verifyActionsViewVisibility();
70             }
71         }
72     }
73 
74     /**
75      * Flings backward (right) and waits the fling's end.
76      */
flingBackward()77     public void flingBackward() {
78         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
79             flingBackwardImpl();
80         }
81     }
82 
flingBackwardImpl()83     private void flingBackwardImpl() {
84         try (LauncherInstrumentation.Closable c =
85                      mLauncher.addContextLayer("want to fling backward in overview")) {
86             LauncherInstrumentation.log("Overview.flingBackward before fling");
87             final UiObject2 overview = verifyActiveContainer();
88             final int rightMargin =
89                     mLauncher.getTargetInsets().right + mLauncher.getEdgeSensitivityWidth();
90             mLauncher.scroll(
91                     overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
92             try (LauncherInstrumentation.Closable c2 =
93                          mLauncher.addContextLayer("flung backwards")) {
94                 verifyActiveContainer();
95                 verifyActionsViewVisibility();
96             }
97         }
98     }
99 
flingToFirstTask()100     private OverviewTask flingToFirstTask() {
101         OverviewTask currentTask = getCurrentTask();
102 
103         while (mLauncher.getRealDisplaySize().x - currentTask.getUiObject().getVisibleBounds().right
104                 <= mLauncher.getOverviewPageSpacing()) {
105             flingBackwardImpl();
106             currentTask = getCurrentTask();
107         }
108 
109         return currentTask;
110     }
111 
112     /**
113      * Dismissed all tasks by scrolling to Clear-all button and pressing it.
114      */
dismissAllTasks()115     public void dismissAllTasks() {
116         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
117              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
118                      "dismissing all tasks")) {
119             final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all");
120             for (int i = 0;
121                     i < FLINGS_FOR_DISMISS_LIMIT
122                             && !verifyActiveContainer().hasObject(clearAllSelector);
123                     ++i) {
124                 flingForwardImpl();
125             }
126 
127             mLauncher.clickLauncherObject(
128                     mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector));
129 
130             mLauncher.waitUntilLauncherObjectGone(clearAllSelector);
131         }
132     }
133 
134     /**
135      * Touch to the right of current task. This should dismiss overview and go back to Workspace.
136      */
touchOutsideFirstTask()137     public Workspace touchOutsideFirstTask() {
138         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
139              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
140                      "touching outside the focused task")) {
141 
142             if (getTaskCount() < 2) {
143                 throw new IllegalStateException(
144                         "Need to have at least 2 tasks");
145             }
146 
147             OverviewTask currentTask = flingToFirstTask();
148 
149             mLauncher.touchOutsideContainer(currentTask.getUiObject(),
150                     /* tapRight= */ true,
151                     /* halfwayToEdge= */ false);
152 
153             return new Workspace(mLauncher);
154         }
155     }
156 
157     /**
158      * Touch between two tasks
159      */
touchBetweenTasks()160     public void touchBetweenTasks() {
161         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
162              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
163                      "touching outside the focused task")) {
164             if (getTaskCount() < 2) {
165                 throw new IllegalStateException(
166                         "Need to have at least 2 tasks");
167             }
168 
169             OverviewTask currentTask = flingToFirstTask();
170 
171             mLauncher.touchOutsideContainer(currentTask.getUiObject(),
172                     /* tapRight= */ false,
173                     /* halfwayToEdge= */ false);
174         }
175     }
176 
177     /**
178      * Touch either on the right or the left corner of the screen, 1 pixel from the bottom and
179      * from the sides.
180      */
touchTaskbarBottomCorner(boolean tapRight)181     public void touchTaskbarBottomCorner(boolean tapRight) {
182         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
183             Taskbar taskbar = new Taskbar(mLauncher);
184             taskbar.touchBottomCorner(tapRight);
185             verifyActiveContainer();
186         }
187     }
188 
189     /**
190      * Scrolls the current task via flinging forward until it is off screen.
191      *
192      * If only one task is present, it is only partially scrolled off screen and will still be
193      * the current task.
194      */
scrollCurrentTaskOffScreen()195     public void scrollCurrentTaskOffScreen() {
196         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
197              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
198                      "want to scroll current task off screen in overview")) {
199             verifyActiveContainer();
200 
201             OverviewTask task = getCurrentTask();
202             mLauncher.assertNotNull("current task is null", task);
203             mLauncher.scrollLeftByDistance(verifyActiveContainer(),
204                     task.getVisibleWidth() + mLauncher.getOverviewPageSpacing());
205 
206             try (LauncherInstrumentation.Closable c2 =
207                          mLauncher.addContextLayer("scrolled task off screen")) {
208                 verifyActiveContainer();
209                 verifyActionsViewVisibility();
210 
211                 if (getTaskCount() > 1) {
212                     if (mLauncher.isTablet()) {
213                         mLauncher.assertTrue("current task is not grid height",
214                                 getCurrentTask().getVisibleHeight() == mLauncher
215                                         .getGridTaskRectForTablet().height());
216                     }
217                     mLauncher.assertTrue("Current task not scrolled off screen",
218                             !getCurrentTask().equals(task));
219                 }
220             }
221         }
222     }
223 
224     /**
225      * Gets the current task in the carousel, or fails if the carousel is empty.
226      *
227      * @return the task in the middle of the visible tasks list.
228      */
229     @NonNull
getCurrentTask()230     public OverviewTask getCurrentTask() {
231         final List<UiObject2> taskViews = getTasks();
232         mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
233 
234         // taskViews contains up to 3 task views: the 'main' (having the widest visible part) one
235         // in the center, and parts of its right and left siblings. Find the main task view by
236         // its width.
237         final UiObject2 widestTask = Collections.max(taskViews,
238                 (t1, t2) -> Integer.compare(mLauncher.getVisibleBounds(t1).width(),
239                         mLauncher.getVisibleBounds(t2).width()));
240 
241         return new OverviewTask(mLauncher, widestTask, this);
242     }
243 
244     /** Returns an overview task matching TestActivity {@param activityNumber}. */
245     @NonNull
getTestActivityTask(int activityNumber)246     public OverviewTask getTestActivityTask(int activityNumber) {
247         final List<UiObject2> taskViews = getTasks();
248         mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
249 
250         final String activityName = "TestActivity" + activityNumber;
251         UiObject2 task = null;
252         for (UiObject2 taskView : taskViews) {
253             // TODO(b/239452415): Use equals instead of descEndsWith
254             if (taskView.getParent().hasObject(By.descEndsWith(activityName))) {
255                 task = taskView;
256                 break;
257             }
258         }
259         mLauncher.assertNotNull(
260                 "Unable to find a task with " + activityName + " from the task list", task);
261 
262         return new OverviewTask(mLauncher, task, this);
263     }
264 
265     /**
266      * Returns a list of all tasks fully visible in the tablet grid overview.
267      */
268     @NonNull
getCurrentTasksForTablet()269     public List<OverviewTask> getCurrentTasksForTablet() {
270         final List<UiObject2> taskViews = getTasks();
271         mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
272 
273         final int gridTaskWidth = mLauncher.getGridTaskRectForTablet().width();
274 
275         return taskViews.stream().filter(t -> t.getVisibleBounds().width() == gridTaskWidth).map(
276                 t -> new OverviewTask(mLauncher, t, this)).collect(Collectors.toList());
277     }
278 
279     @NonNull
getTasks()280     private List<UiObject2> getTasks() {
281         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
282                 "want to get overview tasks")) {
283             verifyActiveContainer();
284             return mLauncher.getDevice().findObjects(
285                     mLauncher.getOverviewObjectSelector("snapshot"));
286         }
287     }
288 
getTaskCount()289     int getTaskCount() {
290         return getTasks().size();
291     }
292 
293     /**
294      * Returns whether Overview has tasks.
295      */
hasTasks()296     public boolean hasTasks() {
297         return getTasks().size() > 0;
298     }
299 
300     /**
301      * Gets Overview Actions.
302      *
303      * @return The Overview Actions
304      */
305     @NonNull
getOverviewActions()306     public OverviewActions getOverviewActions() {
307         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
308                 "want to get overview actions")) {
309             verifyActiveContainer();
310             UiObject2 overviewActions = mLauncher.waitForOverviewObject("action_buttons");
311             return new OverviewActions(overviewActions, mLauncher);
312         }
313     }
314 
315     /**
316      * Returns if clear all button is visible.
317      */
isClearAllVisible()318     public boolean isClearAllVisible() {
319         return verifyActiveContainer().hasObject(
320                 mLauncher.getOverviewObjectSelector("clear_all"));
321     }
322 
isActionsViewVisible()323     protected boolean isActionsViewVisible() {
324         if (!hasTasks() || isClearAllVisible()) {
325             return false;
326         }
327         boolean isTablet = mLauncher.isTablet();
328         if (isTablet && mLauncher.isGridOnlyOverviewEnabled()) {
329             return false;
330         }
331         OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask();
332         if (task == null) {
333             return false;
334         }
335         // In tablets, if focused task is not in center, overview actions aren't visible.
336         if (isTablet && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
337             return false;
338         }
339         // Overview actions aren't visible for split screen tasks.
340         return !task.isTaskSplit();
341     }
342 
verifyActionsViewVisibility()343     private void verifyActionsViewVisibility() {
344         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
345                 "want to assert overview actions view visibility")) {
346             if (isActionsViewVisible()) {
347                 mLauncher.waitForOverviewObject("action_buttons");
348             } else {
349                 mLauncher.waitUntilOverviewObjectGone("action_buttons");
350             }
351         }
352     }
353 
354     /**
355      * Returns Overview focused task if it exists.
356      *
357      * @throws IllegalStateException if not run on a tablet device.
358      */
getFocusedTaskForTablet()359     OverviewTask getFocusedTaskForTablet() {
360         if (!mLauncher.isTablet()) {
361             throw new IllegalStateException("Must be run on tablet device.");
362         }
363         final List<UiObject2> taskViews = getTasks();
364         if (taskViews.size() == 0) {
365             return null;
366         }
367         int focusedTaskHeight = mLauncher.getFocusedTaskHeightForTablet();
368         for (UiObject2 task : taskViews) {
369             if (task.getVisibleBounds().height() == focusedTaskHeight) {
370                 return new OverviewTask(mLauncher, task, this);
371             }
372         }
373         return null;
374     }
375 }
376