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.quickstep;
18 
19 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.content.Intent;
26 
27 import androidx.test.filters.LargeTest;
28 import androidx.test.runner.AndroidJUnit4;
29 import androidx.test.uiautomator.By;
30 import androidx.test.uiautomator.Until;
31 
32 import com.android.launcher3.Launcher;
33 import com.android.launcher3.LauncherState;
34 import com.android.launcher3.tapl.AllApps;
35 import com.android.launcher3.tapl.Background;
36 import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
37 import com.android.launcher3.tapl.Overview;
38 import com.android.launcher3.tapl.OverviewActions;
39 import com.android.launcher3.tapl.OverviewTask;
40 import com.android.launcher3.tapl.TestHelpers;
41 import com.android.launcher3.ui.TaplTestsLauncher3;
42 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
43 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
44 import com.android.quickstep.views.RecentsView;
45 
46 import org.junit.After;
47 import org.junit.Before;
48 import org.junit.Ignore;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 @LargeTest
53 @RunWith(AndroidJUnit4.class)
54 public class TaplTestsQuickstep extends AbstractQuickStepTest {
55 
56     private static final String APP_NAME = "LauncherTestApp";
57 
58     @Before
setUp()59     public void setUp() throws Exception {
60         super.setUp();
61         TaplTestsLauncher3.initialize(this);
62         executeOnLauncher(launcher -> {
63             RecentsView recentsView = launcher.getOverviewPanel();
64             recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(true);
65         });
66     }
67 
68     @After
tearDown()69     public void tearDown() {
70         executeOnLauncher(launcher -> {
71             RecentsView recentsView = launcher.getOverviewPanel();
72             recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false);
73         });
74     }
75 
startTestApps()76     public static void startTestApps() throws Exception {
77         startAppFast(getAppPackageName());
78         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
79         startTestActivity(2);
80     }
81 
startTestAppsWithCheck()82     private void startTestAppsWithCheck() throws Exception {
83         startTestApps();
84         executeOnLauncher(launcher -> assertTrue(
85                 "Launcher activity is the top activity; expecting another activity to be the top "
86                         + "one",
87                 isInBackground(launcher)));
88     }
89 
90     @Test
91     @NavigationModeSwitch
92     @PortraitLandscape
testWorkspaceSwitchToAllApps()93     public void testWorkspaceSwitchToAllApps() {
94         assertNotNull("switchToAllApps() returned null",
95                 mLauncher.getWorkspace().switchToAllApps());
96         assertTrue("Launcher internal state is not All Apps",
97                 isInState(() -> LauncherState.ALL_APPS));
98     }
99 
100     @Test
101     @PortraitLandscape
testOverview()102     public void testOverview() throws Exception {
103         startTestAppsWithCheck();
104         // mLauncher.pressHome() also tests an important case of pressing home while in background.
105         Overview overview = mLauncher.pressHome().switchToOverview();
106         assertTrue("Launcher internal state didn't switch to Overview",
107                 isInState(() -> LauncherState.OVERVIEW));
108         executeOnLauncher(
109                 launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
110 
111         // Test flinging forward and backward.
112         executeOnLauncher(launcher -> assertEquals("Current task in Overview is not 0",
113                 0, getCurrentOverviewPage(launcher)));
114 
115         overview.flingForward();
116         assertTrue("Launcher internal state is not Overview",
117                 isInState(() -> LauncherState.OVERVIEW));
118         final Integer currentTaskAfterFlingForward = getFromLauncher(
119                 launcher -> getCurrentOverviewPage(launcher));
120         executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
121                 currentTaskAfterFlingForward > 0));
122 
123         overview.flingBackward();
124         assertTrue("Launcher internal state is not Overview",
125                 isInState(() -> LauncherState.OVERVIEW));
126         executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
127                 getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
128 
129         // Test opening a task.
130         OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
131         assertNotNull("overview.getCurrentTask() returned null (1)", task);
132         assertNotNull("OverviewTask.open returned null", task.open());
133         assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
134                 By.pkg(getAppPackageName()).text("TestActivity2")),
135                 DEFAULT_UI_TIMEOUT));
136         executeOnLauncher(launcher -> assertTrue(
137                 "Launcher activity is the top activity; expecting another activity to be the top "
138                         + "one",
139                 isInBackground(launcher)));
140 
141         // Test dismissing a task.
142         overview = mLauncher.pressHome().switchToOverview();
143         assertTrue("Launcher internal state didn't switch to Overview",
144                 isInState(() -> LauncherState.OVERVIEW));
145         final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
146         task = overview.getCurrentTask();
147         assertNotNull("overview.getCurrentTask() returned null (2)", task);
148         task.dismiss();
149         executeOnLauncher(
150                 launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
151                         numTasks - 1, getTaskCount(launcher)));
152 
153         // Test dismissing all tasks.
154         mLauncher.pressHome().switchToOverview().dismissAllTasks();
155         assertTrue("Launcher internal state is not Home",
156                 isInState(() -> LauncherState.NORMAL));
157         executeOnLauncher(
158                 launcher -> assertEquals("Still have tasks after dismissing all",
159                         0, getTaskCount(launcher)));
160     }
161 
162     /**
163      * Smoke test for action buttons: Presses all the buttons and makes sure no crashes occur.
164      */
165     @Test
166     @NavigationModeSwitch
167     @PortraitLandscape
168     @ScreenRecord // b/195673272
testOverviewActions()169     public void testOverviewActions() throws Exception {
170         // Experimenting for b/165029151:
171         final Overview overview = mLauncher.pressHome().switchToOverview();
172         if (overview.hasTasks()) overview.dismissAllTasks();
173         mLauncher.pressHome();
174         //
175 
176         startTestAppsWithCheck();
177         OverviewActions actionsView =
178                 mLauncher.pressHome().switchToOverview().getOverviewActions();
179         actionsView.clickAndDismissScreenshot();
180     }
181 
getCurrentOverviewPage(Launcher launcher)182     private int getCurrentOverviewPage(Launcher launcher) {
183         return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
184     }
185 
getTaskCount(Launcher launcher)186     private int getTaskCount(Launcher launcher) {
187         return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
188     }
189 
getTopRowTaskCountForTablet(Launcher launcher)190     private int getTopRowTaskCountForTablet(Launcher launcher) {
191         return launcher.<RecentsView>getOverviewPanel().getTopRowTaskCountForTablet();
192     }
193 
getBottomRowTaskCountForTablet(Launcher launcher)194     private int getBottomRowTaskCountForTablet(Launcher launcher) {
195         return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
196     }
197 
198     @Test
199     @NavigationModeSwitch
200     @PortraitLandscape
testSwitchToOverview()201     public void testSwitchToOverview() throws Exception {
202         assertNotNull("Workspace.switchToOverview() returned null",
203                 mLauncher.pressHome().switchToOverview());
204         assertTrue("Launcher internal state didn't switch to Overview",
205                 isInState(() -> LauncherState.OVERVIEW));
206     }
207 
208     @Test
209     @NavigationModeSwitch
210     @PortraitLandscape
testBackground()211     public void testBackground() throws Exception {
212         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
213         final Background background = getAndAssertBackground();
214 
215         assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
216         assertTrue("Launcher internal state didn't switch to Overview",
217                 isInState(() -> LauncherState.OVERVIEW));
218     }
219 
getAndAssertBackground()220     private Background getAndAssertBackground() {
221         final Background background = mLauncher.getBackground();
222         assertNotNull("Launcher.getBackground() returned null", background);
223         executeOnLauncher(launcher -> assertTrue(
224                 "Launcher activity is the top activity; expecting another activity to be the top "
225                         + "one",
226                 isInBackground(launcher)));
227         return background;
228     }
229 
230     @Test
231     @PortraitLandscape
testAllAppsFromHome()232     public void testAllAppsFromHome() throws Exception {
233         // Test opening all apps
234         assertNotNull("switchToAllApps() returned null",
235                 mLauncher.getWorkspace().switchToAllApps());
236 
237         TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps());
238 
239         // Testing pressHome.
240         assertTrue("Launcher internal state is not All Apps",
241                 isInState(() -> LauncherState.ALL_APPS));
242         assertNotNull("pressHome returned null", mLauncher.pressHome());
243         assertTrue("Launcher internal state is not Home",
244                 isInState(() -> LauncherState.NORMAL));
245         assertNotNull("getHome returned null", mLauncher.getWorkspace());
246     }
247 
248     @Test
249     @NavigationModeSwitch
250     @PortraitLandscape
testQuickSwitchFromApp()251     public void testQuickSwitchFromApp() throws Exception {
252         startTestActivity(2);
253         startTestActivity(3);
254         startTestActivity(4);
255 
256         Background background = getAndAssertBackground();
257         background.quickSwitchToPreviousApp();
258         assertTrue("The first app we should have quick switched to is not running",
259                 isTestActivityRunning(3));
260 
261         background = getAndAssertBackground();
262         background.quickSwitchToPreviousApp();
263         if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) {
264             // 3-button mode toggles between 2 apps, rather than going back further.
265             assertTrue("Second quick switch should have returned to the first app.",
266                     isTestActivityRunning(4));
267         } else {
268             assertTrue("The second app we should have quick switched to is not running",
269                     isTestActivityRunning(2));
270         }
271         background = getAndAssertBackground();
272         background.quickSwitchToPreviousAppSwipeLeft();
273         assertTrue("The 2nd app we should have quick switched to is not running",
274                 isTestActivityRunning(3));
275 
276         background = getAndAssertBackground();
277         background.switchToOverview();
278     }
279 
isTestActivityRunning(int activityNumber)280     private boolean isTestActivityRunning(int activityNumber) {
281         return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName())
282                         .text("TestActivity" + activityNumber)),
283                 DEFAULT_UI_TIMEOUT);
284     }
285 
286     @Test
287     @NavigationModeSwitch
288     @PortraitLandscape
testQuickSwitchFromHome()289     public void testQuickSwitchFromHome() throws Exception {
290         startTestActivity(2);
291         mLauncher.pressHome().quickSwitchToPreviousApp();
292         assertTrue("The most recent task is not running after quick switching from home",
293                 isTestActivityRunning(2));
294         getAndAssertBackground();
295     }
296 
297     // TODO(b/204830798): test with all navigation modes(add @NavigationModeSwitch annotation)
298     //  after the bug resolved.
299     @Ignore("b/205027405")
300     @Test
301     @PortraitLandscape
302     @ScreenRecord
testPressBack()303     public void testPressBack() throws Exception {
304         mLauncher.getWorkspace().switchToAllApps();
305         mLauncher.pressBack();
306         mLauncher.getWorkspace();
307         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
308 
309         AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
310         allApps.freeze();
311         try {
312             allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
313         } finally {
314             allApps.unfreeze();
315         }
316         mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
317         mLauncher.pressBack();
318         mLauncher.getWorkspace();
319         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
320     }
321 
322     @Test
323     @PortraitLandscape
testOverviewForTablet()324     public void testOverviewForTablet() throws Exception {
325         // TODO(b/210158657): Re-enable for OOP
326         if (!mLauncher.isTablet() || !TestHelpers.isInLauncherProcess()) {
327             return;
328         }
329         for (int i = 2; i <= 14; i++) {
330             startTestActivity(i);
331         }
332 
333         Overview overview = mLauncher.pressHome().switchToOverview();
334         executeOnLauncher(
335                 launcher -> assertTrue("Don't have at least 13 tasks",
336                         getTaskCount(launcher) >= 13));
337 
338         // Test scroll the first task off screen
339         overview.scrollCurrentTaskOffScreen();
340         assertTrue("Launcher internal state is not Overview",
341                 isInState(() -> LauncherState.OVERVIEW));
342         executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
343                 getCurrentOverviewPage(launcher) > 0));
344 
345         // Test opening the task.
346         overview.getCurrentTask().open();
347         assertTrue("Test activity didn't open from Overview",
348                 mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity8")),
349                         DEFAULT_UI_TIMEOUT));
350 
351         // Scroll the task offscreen as it is now first
352         overview = mLauncher.pressHome().switchToOverview();
353         overview.scrollCurrentTaskOffScreen();
354         assertTrue("Launcher internal state is not Overview",
355                 isInState(() -> LauncherState.OVERVIEW));
356         executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
357                 getCurrentOverviewPage(launcher) > 0));
358 
359         // Test dismissing the later task.
360         final Integer numTasks = getFromLauncher(this::getTaskCount);
361         overview.getCurrentTask().dismiss();
362         executeOnLauncher(
363                 launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
364                         numTasks - 1, getTaskCount(launcher)));
365         executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after dismissal",
366                 (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
367                         launcher)) <= 1)));
368 
369         // Test dismissing more tasks.
370         assertTrue("Launcher internal state didn't remain in Overview",
371                 isInState(() -> LauncherState.OVERVIEW));
372         overview.getCurrentTask().dismiss();
373         assertTrue("Launcher internal state didn't remain in Overview",
374                 isInState(() -> LauncherState.OVERVIEW));
375         overview.getCurrentTask().dismiss();
376         executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals",
377                 (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
378                         launcher)) <= 1)));
379 
380         // Test dismissing all tasks.
381         mLauncher.pressHome().switchToOverview().dismissAllTasks();
382         assertTrue("Launcher internal state is not Home",
383                 isInState(() -> LauncherState.NORMAL));
384         executeOnLauncher(
385                 launcher -> assertEquals("Still have tasks after dismissing all",
386                         0, getTaskCount(launcher)));
387     }
388 }
389