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