blob: 6306f0e16375eb20f65994ef456a6a5a1c605ede [file] [log] [blame]
Vishnu Nair8248b7c2018-08-01 10:13:36 -07001/*
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
17package com.android.server.wm.flicker;
18
19import static android.os.SystemClock.sleep;
20import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
21import static android.view.Surface.ROTATION_0;
22
23import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
24
25import static org.junit.Assert.assertNotNull;
26import static org.junit.Assert.fail;
27
28import android.content.Context;
29import android.content.pm.PackageManager;
30import android.graphics.Point;
31import android.graphics.Rect;
32import android.os.RemoteException;
33import android.support.test.InstrumentationRegistry;
34import android.support.test.launcherhelper.LauncherStrategyFactory;
35import android.support.test.uiautomator.By;
36import android.support.test.uiautomator.BySelector;
37import android.support.test.uiautomator.Configurator;
38import android.support.test.uiautomator.UiDevice;
39import android.support.test.uiautomator.UiObject2;
40import android.support.test.uiautomator.Until;
41import android.util.Log;
42import android.util.Rational;
43import android.view.View;
44import android.view.ViewConfiguration;
45
46/**
47 * Collection of UI Automation helper functions.
48 */
49public class AutomationUtils {
50 private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
51 private static final long FIND_TIMEOUT = 10000;
52 private static final long LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2L;
53 private static final String TAG = "FLICKER";
54
55 public static void wakeUpAndGoToHomeScreen() {
56 UiDevice device = UiDevice.getInstance(InstrumentationRegistry
57 .getInstrumentation());
58 try {
59 device.wakeUp();
60 } catch (RemoteException e) {
61 throw new RuntimeException(e);
62 }
63 device.pressHome();
64 }
65
66 /**
67 * Sets {@link android.app.UiAutomation#waitForIdle(long, long)} global timeout to 0 causing
68 * the {@link android.app.UiAutomation#waitForIdle(long, long)} function to timeout instantly.
69 * This removes some delays when using the UIAutomator library required to create fast UI
70 * transitions.
71 */
72 static void setFastWait() {
73 Configurator.getInstance().setWaitForIdleTimeout(0);
74 }
75
76 /**
77 * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
78 */
79 static void setDefaultWait() {
80 Configurator.getInstance().setWaitForIdleTimeout(10000);
81 }
82
83 public static boolean isQuickstepEnabled(UiDevice device) {
84 return device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null;
85 }
86
87 public static void openQuickstep(UiDevice device) {
88 if (isQuickstepEnabled(device)) {
89 int height = device.getDisplayHeight();
90 UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
91
92 Rect navBarVisibleBounds;
93
94 // TODO(vishnun) investigate why this object cannot be found.
95 if (navBar != null) {
96 navBarVisibleBounds = navBar.getVisibleBounds();
97 } else {
98 Log.e(TAG, "Could not find nav bar, infer location");
99 navBarVisibleBounds = WindowUtils.getNavigationBarPosition(ROTATION_0);
100 }
101
102 // Swipe from nav bar to 2/3rd down the screen.
103 device.swipe(
104 navBarVisibleBounds.centerX(), navBarVisibleBounds.centerY(),
105 navBarVisibleBounds.centerX(), height * 2 / 3,
106 (navBarVisibleBounds.centerY() - height * 2 / 3) / 100); // 100 px/step
107 } else {
108 try {
109 device.pressRecentApps();
110 } catch (RemoteException e) {
111 throw new RuntimeException(e);
112 }
113 }
114 BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
115
116 // use a long timeout to wait until recents populated
117 if (device.wait(
118 Until.findObject(isRecentsInLauncher()
119 ? getLauncherOverviewSelector(device) : RECENTS),
120 10000) == null) {
121 fail("Recents didn't appear");
122 }
123 device.waitForIdle();
124 }
125
126 static void clearRecents(UiDevice device) {
127 if (isQuickstepEnabled(device)) {
128 openQuickstep(device);
129
130 for (int i = 0; i < 5; i++) {
131 device.swipe(device.getDisplayWidth() / 2,
132 device.getDisplayHeight() / 2, device.getDisplayWidth(),
133 device.getDisplayHeight() / 2,
134 5);
135
136 BySelector clearAllSelector = By.res("com.google.android.apps.nexuslauncher",
137 "clear_all_button");
138 UiObject2 clearAllButton = device.wait(Until.findObject(clearAllSelector), 100);
139 if (clearAllButton != null) {
140 clearAllButton.click();
141 return;
142 }
143 }
144 }
145 }
146
147 private static BySelector getLauncherOverviewSelector(UiDevice device) {
148 return By.res(device.getLauncherPackageName(), "overview_panel");
149 }
150
151 private static void longPressRecents(UiDevice device) {
152 BySelector recentsSelector = By.res(SYSTEMUI_PACKAGE, "recent_apps");
153 UiObject2 recentsButton = device.wait(Until.findObject(recentsSelector), FIND_TIMEOUT);
154 assertNotNull("Unable to find recents button", recentsButton);
155 recentsButton.click(LONG_PRESS_TIMEOUT);
156 }
157
158 public static void launchSplitScreen(UiDevice device) {
159 String mLauncherPackage = LauncherStrategyFactory.getInstance(device)
160 .getLauncherStrategy().getSupportedLauncherPackage();
161
162 if (isQuickstepEnabled(device)) {
163 // Quickstep enabled
164 openQuickstep(device);
165
166 BySelector overviewIconSelector = By.res(mLauncherPackage, "icon")
167 .clazz(View.class);
168 UiObject2 overviewIcon = device.wait(Until.findObject(overviewIconSelector),
169 FIND_TIMEOUT);
170 assertNotNull("Unable to find app icon in Overview", overviewIcon);
171 overviewIcon.click();
172
173 BySelector splitscreenButtonSelector = By.text("Split screen");
174 UiObject2 splitscreenButton = device.wait(Until.findObject(splitscreenButtonSelector),
175 FIND_TIMEOUT);
Nataniel Borges7b3ec112019-01-30 17:29:13 -0800176 assertNotNull("Unable to find Split screen button in Overview", splitscreenButton);
Vishnu Nair8248b7c2018-08-01 10:13:36 -0700177 splitscreenButton.click();
178 } else {
179 // Classic long press recents
180 longPressRecents(device);
181 }
182 // Wait for animation to complete.
183 sleep(2000);
184 }
185
186 public static void exitSplitScreen(UiDevice device) {
187 if (isQuickstepEnabled(device)) {
188 // Quickstep enabled
189 BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
190 UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
191 assertNotNull("Unable to find Split screen divider", divider);
192
193 // Drag the split screen divider to the top of the screen
194 divider.drag(new Point(device.getDisplayWidth() / 2, 0), 400);
195 } else {
196 // Classic long press recents
197 longPressRecents(device);
198 }
199 // Wait for animation to complete.
200 sleep(2000);
201 }
202
203 static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
204 BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
205 UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
206 assertNotNull("Unable to find Split screen divider", divider);
207 int destHeight =
208 (int) (WindowUtils.getDisplayBounds().height() * windowHeightRatio.floatValue());
209 // Drag the split screen divider to so that the ratio of top window height and bottom
210 // window height is windowHeightRatio
211 device.drag(divider.getVisibleBounds().centerX(), divider.getVisibleBounds().centerY(),
212 device.getDisplayWidth() / 2, destHeight, 10);
213 //divider.drag(new Point(device.getDisplayWidth() / 2, destHeight), 400)
214 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
215
216 // Wait for animation to complete.
217 sleep(2000);
218 }
219
220 static void closePipWindow(UiDevice device) {
221 UiObject2 pipWindow = device.findObject(
222 By.res(SYSTEMUI_PACKAGE, "background"));
223 pipWindow.click();
224 UiObject2 exitPipObject = device.findObject(
225 By.res(SYSTEMUI_PACKAGE, "dismiss"));
226 exitPipObject.click();
227 // Wait for animation to complete.
228 sleep(2000);
229 }
230
231 static void expandPipWindow(UiDevice device) {
232 UiObject2 pipWindow = device.findObject(
233 By.res(SYSTEMUI_PACKAGE, "background"));
234 pipWindow.click();
235 pipWindow.click();
236 }
237
238 public static void stopPackage(Context context, String packageName) {
239 runShellCommand("am force-stop " + packageName);
240 int packageUid;
241 try {
242 packageUid = context.getPackageManager().getPackageUid(packageName, /* flags= */0);
243 } catch (PackageManager.NameNotFoundException e) {
244 return;
245 }
246 while (targetPackageIsRunning(packageUid)) {
247 try {
248 Thread.sleep(100);
249 } catch (InterruptedException e) {
250 //ignore
251 }
252 }
253 }
254
255 private static boolean targetPackageIsRunning(int uid) {
256 final String result = runShellCommand(
257 String.format("cmd activity get-uid-state %d", uid));
258 return !result.contains("(NONEXISTENT)");
259 }
260}