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 static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; 20 21 import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT; 22 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; 23 24 import android.graphics.Point; 25 import android.os.SystemClock; 26 import android.view.MotionEvent; 27 28 import androidx.annotation.NonNull; 29 import androidx.test.uiautomator.UiObject2; 30 31 import com.android.launcher3.testing.TestProtocol; 32 33 import java.util.List; 34 import java.util.regex.Pattern; 35 36 /** 37 * Indicates the base state with a UI other than Overview running as foreground. It can also 38 * indicate Launcher as long as Launcher is not in Overview state. 39 */ 40 public class Background extends LauncherInstrumentation.VisibleContainer { 41 private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500; 42 private static final Pattern SQUARE_BUTTON_EVENT = Pattern.compile("onOverviewToggle"); 43 Background(LauncherInstrumentation launcher)44 Background(LauncherInstrumentation launcher) { 45 super(launcher); 46 } 47 48 @Override getContainerType()49 protected LauncherInstrumentation.ContainerType getContainerType() { 50 return LauncherInstrumentation.ContainerType.BACKGROUND; 51 } 52 53 /** 54 * Swipes up or presses the square button to switch to Overview. 55 * Returns the base overview, which can be either in Launcher or the fallback recents. 56 * 57 * @return the Overview panel object. 58 */ 59 @NonNull switchToOverview()60 public BaseOverview switchToOverview() { 61 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 62 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 63 "want to switch from background to overview")) { 64 verifyActiveContainer(); 65 goToOverviewUnchecked(); 66 return mLauncher.isFallbackOverview() 67 ? new BaseOverview(mLauncher) : new Overview(mLauncher); 68 } 69 } 70 71 zeroButtonToOverviewGestureStartsInLauncher()72 protected boolean zeroButtonToOverviewGestureStartsInLauncher() { 73 return mLauncher.isTablet(); 74 } 75 zeroButtonToOverviewGestureStateTransitionWhileHolding()76 protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() { 77 return false; 78 } 79 goToOverviewUnchecked()80 protected void goToOverviewUnchecked() { 81 switch (mLauncher.getNavigationModel()) { 82 case ZERO_BUTTON: { 83 sendDownPointerToEnterOverviewToLauncher(); 84 String swipeAndHoldToEnterOverviewActionName = 85 "swiping and holding to enter overview"; 86 // If swiping from an app (e.g. Overview is in Background), we pause and hold on 87 // swipe up to make overview appear, or else swiping without holding would take 88 // us to the Home state. If swiping up from Home (e.g. Overview in Home or 89 // Workspace state where the below condition is true), there is no need to pause, 90 // and we will not test for an intermediate carousel as one will not exist. 91 if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) { 92 mLauncher.runToState(this::sendSwipeUpAndHoldToEnterOverviewGestureToLauncher, 93 OVERVIEW_STATE_ORDINAL, swipeAndHoldToEnterOverviewActionName); 94 sendUpPointerToEnterOverviewToLauncher(); 95 } else { 96 // If swiping up from an app to overview, pause on intermediate carousel 97 // until snapshots are visible. No intermediate carousel when swiping from 98 // Home. The task swiped up is not a snapshot but the TaskViewSimulator. If 99 // only a single task exists, no snapshots will be available during swipe up. 100 mLauncher.executeAndWaitForLauncherEvent( 101 this::sendSwipeUpAndHoldToEnterOverviewGestureToLauncher, 102 event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals( 103 event.getClassName().toString()), 104 () -> "Pause wasn't detected", 105 swipeAndHoldToEnterOverviewActionName); 106 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 107 "paused on swipe up to overview")) { 108 if (mLauncher.getRecentTasks().size() > 1) { 109 // When swiping up to grid-overview for tablets, the swiped tab will be 110 // in the middle of the screen (TaskViewSimulator, not a snapshot), and 111 // all remaining snapshots will be to the left of that task. In 112 // non-tablet overview, snapshots can be on either side of the swiped 113 // task, but we still check that they become visible after swiping and 114 // pausing. 115 mLauncher.waitForOverviewObject("snapshot"); 116 if (mLauncher.isTablet()) { 117 List<UiObject2> tasks = mLauncher.getDevice().findObjects( 118 mLauncher.getOverviewObjectSelector("snapshot")); 119 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 120 mLauncher.assertTrue( 121 "All tasks not to the left of the swiped task", 122 tasks.stream() 123 .allMatch( 124 t -> t.getVisibleBounds().right < centerX)); 125 } 126 127 } 128 String upPointerToEnterOverviewActionName = 129 "sending UP pointer to enter overview"; 130 mLauncher.runToState(this::sendUpPointerToEnterOverviewToLauncher, 131 OVERVIEW_STATE_ORDINAL, upPointerToEnterOverviewActionName); 132 } 133 } 134 break; 135 } 136 137 case TWO_BUTTON: { 138 final int startX; 139 final int startY; 140 final int endX; 141 final int endY; 142 final int swipeLength = mLauncher.getTestInfo(getSwipeHeightRequestName()). 143 getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) + mLauncher.getTouchSlop(); 144 145 if (mLauncher.getDevice().isNaturalOrientation()) { 146 startX = endX = mLauncher.getDevice().getDisplayWidth() / 2; 147 startY = getSwipeStartY(); 148 endY = startY - swipeLength; 149 } else { 150 startX = getSwipeStartX(); 151 // TODO(b/184059820) make horizontal swipe use swipe width not height, for the 152 // moment just double the swipe length. 153 endX = startX - swipeLength * 2; 154 startY = endY = mLauncher.getDevice().getDisplayHeight() / 2; 155 } 156 157 mLauncher.swipeToState(startX, startY, endX, endY, 10, OVERVIEW_STATE_ORDINAL, 158 LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER); 159 break; 160 } 161 162 case THREE_BUTTON: 163 if (mLauncher.isTablet()) { 164 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, 165 LauncherInstrumentation.EVENT_TOUCH_DOWN); 166 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, 167 LauncherInstrumentation.EVENT_TOUCH_UP); 168 } 169 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); 170 mLauncher.runToState( 171 () -> mLauncher.waitForNavigationUiObject("recent_apps").click(), 172 OVERVIEW_STATE_ORDINAL, "clicking Recents button"); 173 break; 174 } 175 expectSwitchToOverviewEvents(); 176 } 177 expectSwitchToOverviewEvents()178 private void expectSwitchToOverviewEvents() { 179 } 180 sendDownPointerToEnterOverviewToLauncher()181 private void sendDownPointerToEnterOverviewToLauncher() { 182 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 183 final int startY = getSwipeStartY(); 184 final Point start = new Point(centerX, startY); 185 final long downTime = SystemClock.uptimeMillis(); 186 final LauncherInstrumentation.GestureScope gestureScope = 187 zeroButtonToOverviewGestureStartsInLauncher() 188 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE 189 : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; 190 191 mLauncher.sendPointer( 192 downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope); 193 } 194 sendSwipeUpAndHoldToEnterOverviewGestureToLauncher()195 private void sendSwipeUpAndHoldToEnterOverviewGestureToLauncher() { 196 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 197 final int startY = getSwipeStartY(); 198 final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt( 199 TestProtocol.TEST_INFO_RESPONSE_FIELD); 200 final Point start = new Point(centerX, startY); 201 final Point end = 202 new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop()); 203 final long downTime = SystemClock.uptimeMillis(); 204 final LauncherInstrumentation.GestureScope gestureScope = 205 zeroButtonToOverviewGestureStartsInLauncher() 206 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE 207 : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; 208 209 mLauncher.movePointer( 210 downTime, 211 downTime, 212 ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, 213 start, 214 end, 215 gestureScope); 216 } 217 sendUpPointerToEnterOverviewToLauncher()218 private void sendUpPointerToEnterOverviewToLauncher() { 219 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 220 final int startY = getSwipeStartY(); 221 final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt( 222 TestProtocol.TEST_INFO_RESPONSE_FIELD); 223 final Point end = 224 new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop()); 225 final long downTime = SystemClock.uptimeMillis(); 226 final LauncherInstrumentation.GestureScope gestureScope = 227 zeroButtonToOverviewGestureStartsInLauncher() 228 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE 229 : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; 230 231 mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(), 232 MotionEvent.ACTION_UP, end, gestureScope); 233 } 234 235 @NonNull quickSwitchToPreviousApp()236 public Background quickSwitchToPreviousApp() { 237 boolean toRight = true; 238 quickSwitch(toRight); 239 return new Background(mLauncher); 240 } 241 242 @NonNull quickSwitchToPreviousAppSwipeLeft()243 public Background quickSwitchToPreviousAppSwipeLeft() { 244 boolean toRight = false; 245 quickSwitch(toRight); 246 return new Background(mLauncher); 247 } 248 249 @NonNull quickSwitch(boolean toRight)250 private void quickSwitch(boolean toRight) { 251 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 252 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 253 "want to quick switch to the previous app")) { 254 verifyActiveContainer(); 255 final boolean launcherWasVisible = mLauncher.isLauncherVisible(); 256 boolean transposeInLandscape = false; 257 switch (mLauncher.getNavigationModel()) { 258 case TWO_BUTTON: 259 transposeInLandscape = true; 260 // Fall through, zero button and two button modes behave the same. 261 case ZERO_BUTTON: { 262 final int startX; 263 final int startY; 264 final int endX; 265 final int endY; 266 final int cornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); 267 if (toRight) { 268 if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) { 269 // Swipe from the bottom left to the bottom right of the screen. 270 startX = cornerRadius; 271 startY = getSwipeStartY(); 272 endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; 273 endY = startY; 274 } else { 275 // Swipe from the bottom right to the top right of the screen. 276 startX = getSwipeStartX(); 277 startY = mLauncher.getRealDisplaySize().y - 1 - cornerRadius; 278 endX = startX; 279 endY = cornerRadius; 280 } 281 } else { 282 if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) { 283 // Swipe from the bottom right to the bottom left of the screen. 284 startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; 285 startY = getSwipeStartY(); 286 endX = cornerRadius; 287 endY = startY; 288 } else { 289 // Swipe from the bottom left to the top left of the screen. 290 startX = getSwipeStartX(); 291 startY = cornerRadius; 292 endX = startX; 293 endY = mLauncher.getRealDisplaySize().y - 1 - cornerRadius; 294 } 295 } 296 297 final boolean isZeroButton = mLauncher.getNavigationModel() 298 == LauncherInstrumentation.NavigationModel.ZERO_BUTTON; 299 LauncherInstrumentation.GestureScope gestureScope = 300 launcherWasVisible && isZeroButton 301 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE 302 : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; 303 mLauncher.executeAndWaitForEvent( 304 () -> mLauncher.linearGesture( 305 startX, startY, endX, endY, 20, false, gestureScope), 306 event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, 307 () -> "Quick switch gesture didn't change window state", "swiping"); 308 break; 309 } 310 311 case THREE_BUTTON: 312 // Double press the recents button. 313 UiObject2 recentsButton = mLauncher.waitForNavigationUiObject("recent_apps"); 314 if (mLauncher.isTablet()) { 315 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, 316 LauncherInstrumentation.EVENT_TOUCH_DOWN); 317 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, 318 LauncherInstrumentation.EVENT_TOUCH_UP); 319 } 320 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); 321 mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL, 322 "clicking Recents button for the first time"); 323 mLauncher.getOverview(); 324 if (mLauncher.isTablet()) { 325 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, 326 LauncherInstrumentation.EVENT_TOUCH_DOWN); 327 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, 328 LauncherInstrumentation.EVENT_TOUCH_UP); 329 } 330 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); 331 mLauncher.executeAndWaitForEvent( 332 () -> recentsButton.click(), 333 event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, 334 () -> "Pressing recents button didn't change window state", 335 "clicking Recents button for the second time"); 336 break; 337 } 338 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT); 339 return; 340 } 341 } 342 getSwipeHeightRequestName()343 protected String getSwipeHeightRequestName() { 344 return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT; 345 } 346 getSwipeStartX()347 protected int getSwipeStartX() { 348 return mLauncher.getRealDisplaySize().x - 1; 349 } 350 getSwipeStartY()351 protected int getSwipeStartY() { 352 return mLauncher.getRealDisplaySize().y - 1; 353 } 354 } 355