1 /* 2 * Copyright (C) 2022 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.tapl.LauncherInstrumentation.DEFAULT_POLL_INTERVAL; 20 import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID; 21 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS; 22 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT; 23 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT; 24 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_SHELL_DRAG_READY; 25 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_SCALE; 26 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD; 27 import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD; 28 29 import android.graphics.Point; 30 import android.graphics.Rect; 31 import android.os.SystemClock; 32 import android.view.InputDevice; 33 import android.view.MotionEvent; 34 import android.view.ViewConfiguration; 35 36 import androidx.annotation.NonNull; 37 import androidx.test.uiautomator.Condition; 38 import androidx.test.uiautomator.UiDevice; 39 40 import com.android.launcher3.testing.shared.ResourceUtils; 41 import com.android.launcher3.testing.shared.TestProtocol; 42 43 /** 44 * Background state operations specific to when an app has been launched. 45 */ 46 public final class LaunchedAppState extends Background { 47 48 // More drag steps than Launchables to give the window manager time to register the drag. 49 private static final int DEFAULT_DRAG_STEPS = 35; 50 51 // UNSTASHED_TASKBAR_HANDLE_HINT_SCALE value from TaskbarStashController. 52 private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f; 53 54 private static final int STASHED_TASKBAR_BOTTOM_EDGE_DP = 1; 55 56 private final Condition<UiDevice, Boolean> mStashedTaskbarHintScaleCondition = 57 device -> Math.abs(mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat( 58 TestProtocol.TEST_INFO_RESPONSE_FIELD) - UNSTASHED_TASKBAR_HANDLE_HINT_SCALE) 59 < 0.00001f; 60 61 private final Condition<UiDevice, Boolean> mStashedTaskbarDefaultScaleCondition = 62 device -> Math.abs(mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat( 63 TestProtocol.TEST_INFO_RESPONSE_FIELD) - 1f) < 0.00001f; 64 65 LaunchedAppState(LauncherInstrumentation launcher) { 66 super(launcher); 67 } 68 69 @Override 70 protected LauncherInstrumentation.ContainerType getContainerType() { 71 return LauncherInstrumentation.ContainerType.LAUNCHED_APP; 72 } 73 74 @Override 75 public boolean isHomeState() { 76 return false; 77 } 78 79 @NonNull 80 @Override 81 public BaseOverview switchToOverview() { 82 try (LauncherInstrumentation.Closable ignored = mLauncher.eventsCheck(); 83 LauncherInstrumentation.Closable ignored1 = mLauncher.addContextLayer( 84 "want to switch from background to overview")) { 85 verifyActiveContainer(); 86 goToOverviewUnchecked(); 87 return mLauncher.is3PLauncher() 88 ? new BaseOverview(mLauncher, /*launchedFromApp=*/true) 89 : new Overview(mLauncher, /*launchedFromApp=*/true); 90 } 91 } 92 93 /** 94 * Returns the taskbar. 95 * 96 * The taskbar must already be visible when calling this method. 97 */ 98 public Taskbar getTaskbar() { 99 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 100 "want to get the taskbar")) { 101 return new Taskbar(mLauncher); 102 } 103 } 104 105 /** 106 * Waits for the taskbar to be hidden, or fails. 107 */ 108 public void assertTaskbarHidden() { 109 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 110 "waiting for taskbar to be hidden")) { 111 mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); 112 } 113 } 114 115 /** 116 * Waits for the taskbar to be visible, or fails. 117 */ 118 public void assertTaskbarVisible() { 119 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 120 "waiting for taskbar to be visible")) { 121 mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); 122 } 123 } 124 125 /** 126 * Returns the Taskbar in a visible state. 127 * 128 * The taskbar must already be hidden and in transient mode when calling this method. 129 */ 130 public Taskbar swipeUpToUnstashTaskbar() { 131 mLauncher.assertTrue("Taskbar is not transient, swipe up not supported", 132 mLauncher.isTransientTaskbar()); 133 134 mLauncher.getTestInfo(REQUEST_ENABLE_BLOCK_TIMEOUT); 135 136 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 137 LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( 138 "want to swipe up to unstash the taskbar")) { 139 mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID); 140 141 int taskbarFromNavThreshold = mLauncher.getTestInfo(REQUEST_TASKBAR_FROM_NAV_THRESHOLD) 142 .getInt(TEST_INFO_RESPONSE_FIELD); 143 int startX = mLauncher.getRealDisplaySize().x / 2; 144 int startY = mLauncher.getRealDisplaySize().y - 1; 145 int endX = startX; 146 int endY = startY - taskbarFromNavThreshold; 147 148 mLauncher.executeAndWaitForLauncherStop( 149 () -> mLauncher.linearGesture(startX, startY, endX, endY, 10, 150 /* slowDown= */ true, 151 LauncherInstrumentation.GestureScope.EXPECT_PILFER), 152 "swiping"); 153 LauncherInstrumentation.log("swipeUpToUnstashTaskbar: sent linear swipe up gesture"); 154 155 return new Taskbar(mLauncher); 156 } finally { 157 mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT); 158 } 159 } 160 161 static void dragToSplitscreen( 162 LauncherInstrumentation launcher, 163 Launchable launchable, 164 String expectedNewPackageName, 165 String expectedExistingPackageName) { 166 try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer( 167 "want to drag taskbar item to splitscreen")) { 168 final Point displaySize = launcher.getRealDisplaySize(); 169 // Drag to the center of the top-left quadrant of the screen, this point will work in 170 // both portrait and landscape. 171 final Point endPoint = new Point(displaySize.x / 4, displaySize.y / 4); 172 final long downTime = SystemClock.uptimeMillis(); 173 // Use mObject before starting drag since the system drag and drop moves the original 174 // view. 175 Point itemVisibleCenter = launchable.mObject.getVisibleCenter(); 176 Rect itemVisibleBounds = launcher.getVisibleBounds(launchable.mObject); 177 String itemLabel = launchable.mObject.getText(); 178 179 Point dragStart = launchable.startDrag( 180 downTime, 181 launchable::addExpectedEventsForLongClick, 182 /* runToSpringLoadedState= */ false); 183 184 try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer( 185 "started item drag")) { 186 launcher.assertTrue("Shell drag not marked as ready", launcher.waitAndGet(() -> { 187 LauncherInstrumentation.log("Checking shell drag ready"); 188 return launcher.getTestInfo(REQUEST_SHELL_DRAG_READY) 189 .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, false); 190 }, WAIT_TIME_MS, DEFAULT_POLL_INTERVAL)); 191 192 launcher.movePointer( 193 dragStart, 194 endPoint, 195 DEFAULT_DRAG_STEPS, 196 /* isDecelerating= */ true, 197 downTime, 198 SystemClock.uptimeMillis(), 199 /* slowDown= */ false, 200 LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); 201 202 try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer( 203 "moved pointer to drop point")) { 204 LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: " 205 + "before drop " + itemVisibleCenter + " in " + itemVisibleBounds); 206 launcher.sendPointer( 207 downTime, 208 SystemClock.uptimeMillis(), 209 MotionEvent.ACTION_UP, 210 endPoint, 211 LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); 212 LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: " 213 + "after drop"); 214 215 try (LauncherInstrumentation.Closable c4 = launcher.addContextLayer( 216 "dropped item")) { 217 launcher.assertAppLaunched(expectedNewPackageName); 218 launcher.assertAppLaunched(expectedExistingPackageName); 219 } 220 } 221 } 222 } 223 } 224 225 /** 226 * Emulate the cursor hovering the screen edge to unstash the taskbar. 227 * 228 * <p>This unstashing occurs when not actively hovering the taskbar. 229 */ 230 public Taskbar hoverScreenBottomEdgeToUnstashTaskbar() { 231 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 232 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 233 "cursor hover entering screen edge to unstash taskbar")) { 234 mLauncher.getDevice().wait(mStashedTaskbarDefaultScaleCondition, 235 ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT); 236 237 long downTime = SystemClock.uptimeMillis(); 238 int leftEdge = 10; 239 Point taskbarUnstashArea = new Point(leftEdge, mLauncher.getRealDisplaySize().y - 1); 240 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 241 new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null, 242 InputDevice.SOURCE_MOUSE); 243 244 mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); 245 246 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, 247 new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null, 248 InputDevice.SOURCE_MOUSE); 249 250 return new Taskbar(mLauncher); 251 } 252 } 253 254 /** 255 * Emulate the cursor hovering the taskbar to get unstash hint, then hovering below to unstash. 256 */ 257 public Taskbar hoverBelowHintedTaskbarToUnstash() { 258 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 259 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 260 "cursor hover entering stashed taskbar")) { 261 long downTime = SystemClock.uptimeMillis(); 262 Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, 263 mLauncher.getRealDisplaySize().y - 1); 264 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 265 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null, 266 InputDevice.SOURCE_MOUSE); 267 268 mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, 269 LauncherInstrumentation.WAIT_TIME_MS); 270 271 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( 272 "cursor hover enter below taskbar to unstash")) { 273 downTime = SystemClock.uptimeMillis(); 274 Point taskbarUnstashArea = new Point(mLauncher.getRealDisplaySize().x / 2, 275 mLauncher.getRealDisplaySize().y - 1); 276 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, 277 new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null, 278 InputDevice.SOURCE_MOUSE); 279 280 mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); 281 return new Taskbar(mLauncher); 282 } 283 } 284 } 285 286 /** 287 * Emulate the cursor entering and exiting a hover over the taskbar. 288 */ 289 public void hoverToShowTaskbarUnstashHint() { 290 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 291 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 292 "cursor hover entering stashed taskbar")) { 293 long downTime = SystemClock.uptimeMillis(); 294 Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, 295 mLauncher.getRealDisplaySize().y - 1); 296 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 297 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null, 298 InputDevice.SOURCE_MOUSE); 299 300 mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, 301 LauncherInstrumentation.WAIT_TIME_MS); 302 303 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( 304 "cursor hover exiting stashed taskbar")) { 305 Point outsideStashedTaskbarHintArea = new Point( 306 mLauncher.getRealDisplaySize().x / 2, 307 mLauncher.getRealDisplaySize().y - 500); 308 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, 309 new Point(outsideStashedTaskbarHintArea.x, outsideStashedTaskbarHintArea.y), 310 null, InputDevice.SOURCE_MOUSE); 311 312 mLauncher.getDevice().wait(mStashedTaskbarDefaultScaleCondition, 313 LauncherInstrumentation.WAIT_TIME_MS); 314 } 315 } 316 } 317 318 /** 319 * Emulate the cursor clicking the stashed taskbar to go home. 320 */ 321 public Workspace clickStashedTaskbarToGoHome() { 322 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 323 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 324 "cursor hover entering stashed taskbar")) { 325 long downTime = SystemClock.uptimeMillis(); 326 int stashedTaskbarBottomEdge = ResourceUtils.pxFromDp(STASHED_TASKBAR_BOTTOM_EDGE_DP, 327 mLauncher.getResources().getDisplayMetrics()); 328 Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, 329 mLauncher.getRealDisplaySize().y - stashedTaskbarBottomEdge - 1); 330 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, 331 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null, 332 InputDevice.SOURCE_MOUSE); 333 334 mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, 335 LauncherInstrumentation.WAIT_TIME_MS); 336 337 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( 338 "cursor clicking stashed taskbar to go home")) { 339 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, 340 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), 341 null, InputDevice.SOURCE_MOUSE); 342 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, 343 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), 344 LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER, 345 InputDevice.SOURCE_MOUSE); 346 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_PRESS, 347 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), 348 null, InputDevice.SOURCE_MOUSE); 349 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_RELEASE, 350 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), 351 null, InputDevice.SOURCE_MOUSE); 352 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, 353 new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), 354 LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER, 355 InputDevice.SOURCE_MOUSE); 356 357 return mLauncher.getWorkspace(); 358 } 359 } 360 } 361 362 /** Send the "back" gesture to go to workspace. */ 363 public Workspace pressBackToWorkspace() { 364 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 365 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 366 "want to press back from launched app to workspace")) { 367 if (mLauncher.isLauncher3()) { 368 mLauncher.pressBackImpl(); 369 } else { 370 mLauncher.executeAndWaitForWallpaperAnimation( 371 () -> mLauncher.pressBackImpl(), 372 "pressing back" 373 ); 374 } 375 return new Workspace(mLauncher); 376 } 377 } 378 } 379