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 com.android.launcher3.tapl.BaseOverview.TASK_RES_ID; 20 import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT; 21 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL; 22 23 import android.graphics.Point; 24 import android.os.SystemClock; 25 import android.view.MotionEvent; 26 27 import androidx.annotation.NonNull; 28 import androidx.test.uiautomator.UiObject2; 29 30 import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; 31 import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType; 32 import com.android.launcher3.testing.shared.TestProtocol; 33 34 import java.util.List; 35 import java.util.regex.Pattern; 36 37 /** 38 * Indicates the base state with a UI other than Overview running as foreground. It can also 39 * indicate Launcher as long as Launcher is not in Overview state. 40 */ 41 public abstract class Background extends LauncherInstrumentation.VisibleContainer 42 implements KeyboardQuickSwitchSource { 43 private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500; 44 private static final Pattern SQUARE_BUTTON_EVENT = Pattern.compile("onOverviewToggle"); 45 Background(LauncherInstrumentation launcher)46 Background(LauncherInstrumentation launcher) { 47 super(launcher); 48 } 49 50 @Override getLauncher()51 public LauncherInstrumentation getLauncher() { 52 return mLauncher; 53 } 54 55 @Override getStartingContainerType()56 public LauncherInstrumentation.ContainerType getStartingContainerType() { 57 return getContainerType(); 58 } 59 60 /** 61 * Swipes up or presses the square button to switch to Overview. 62 * Returns the base overview, which can be either in Launcher or the fallback recents. 63 * 64 * @return the Overview panel object. 65 */ 66 @NonNull switchToOverview()67 public BaseOverview switchToOverview() { 68 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 69 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 70 "want to switch from background to overview")) { 71 verifyActiveContainer(); 72 goToOverviewUnchecked(); 73 return mLauncher.is3PLauncher() 74 ? new BaseOverview(mLauncher) : new Overview(mLauncher); 75 } 76 } 77 78 zeroButtonToOverviewGestureStateTransitionWhileHolding()79 protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() { 80 return false; 81 } 82 goToOverviewUnchecked()83 protected void goToOverviewUnchecked() { 84 if (mLauncher.getNavigationModel() == NavigationModel.ZERO_BUTTON 85 || mLauncher.getTrackpadGestureType() == TrackpadGestureType.THREE_FINGER) { 86 final long downTime = SystemClock.uptimeMillis(); 87 sendDownPointerToEnterOverviewToLauncher(downTime); 88 String swipeAndHoldToEnterOverviewActionName = 89 "swiping and holding to enter overview"; 90 // If swiping from an app (e.g. Overview is in Background), we pause and hold on 91 // swipe up to make overview appear, or else swiping without holding would take 92 // us to the Home state. If swiping up from Home (e.g. Overview in Home or 93 // Workspace state where the below condition is true), there is no need to pause, 94 // and we will not test for an intermediate carousel as one will not exist. 95 if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) { 96 mLauncher.runToState( 97 () -> sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(downTime), 98 OVERVIEW_STATE_ORDINAL, swipeAndHoldToEnterOverviewActionName); 99 sendUpPointerToEnterOverviewToLauncher(downTime); 100 } else { 101 // If swiping up from an app to overview, pause on intermediate carousel 102 // until snapshots are visible. No intermediate carousel when swiping from 103 // Home. The task swiped up is not a snapshot but the TaskViewSimulator. If 104 // only a single task exists, no snapshots will be available during swipe up. 105 mLauncher.executeAndWaitForLauncherEvent( 106 () -> sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(downTime), 107 event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals( 108 event.getClassName().toString()), 109 () -> "Pause wasn't detected", 110 swipeAndHoldToEnterOverviewActionName); 111 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 112 "paused on swipe up to overview")) { 113 if (mLauncher.getRecentTasks().size() > 1) { 114 // When swiping up to grid-overview for tablets, the swiped tab will be 115 // in the middle of the screen (TaskViewSimulator, not a snapshot), and 116 // all remaining snapshots will be to the left of that task. In 117 // non-tablet overview, snapshots can be on either side of the swiped 118 // task, but we still check that they become visible after swiping and 119 // pausing. 120 mLauncher.waitForOverviewObject(TASK_RES_ID); 121 if (mLauncher.isTablet()) { 122 List<UiObject2> tasks = mLauncher.getDevice().findObjects( 123 mLauncher.getOverviewObjectSelector(TASK_RES_ID)); 124 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 125 mLauncher.assertTrue( 126 "All tasks not to the left of the swiped task", 127 tasks.stream() 128 .allMatch( 129 t -> t.getVisibleBounds().right < centerX)); 130 } 131 132 } 133 String upPointerToEnterOverviewActionName = 134 "sending UP pointer to enter overview"; 135 mLauncher.runToState(() -> sendUpPointerToEnterOverviewToLauncher(downTime), 136 OVERVIEW_STATE_ORDINAL, upPointerToEnterOverviewActionName); 137 } 138 } 139 } else { 140 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); 141 mLauncher.runToState( 142 () -> mLauncher.waitForNavigationUiObject("recent_apps").click(), 143 OVERVIEW_STATE_ORDINAL, "clicking Recents button"); 144 } 145 expectSwitchToOverviewEvents(); 146 } 147 expectSwitchToOverviewEvents()148 private void expectSwitchToOverviewEvents() { 149 } 150 sendDownPointerToEnterOverviewToLauncher(long downTime)151 private void sendDownPointerToEnterOverviewToLauncher(long downTime) { 152 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 153 final int startY = getSwipeStartY(); 154 final Point start = new Point(centerX, startY); 155 156 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, 157 LauncherInstrumentation.GestureScope.EXPECT_PILFER); 158 159 if (!mLauncher.isLauncher3()) { 160 mLauncher.expectEvent(TestProtocol.SEQUENCE_PILFER, 161 LauncherInstrumentation.EVENT_PILFER_POINTERS); 162 } 163 } 164 sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(long downTime)165 private void sendSwipeUpAndHoldToEnterOverviewGestureToLauncher(long downTime) { 166 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 167 final int startY = getSwipeStartY(); 168 final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt( 169 TestProtocol.TEST_INFO_RESPONSE_FIELD); 170 final Point start = new Point(centerX, startY); 171 final Point end = 172 new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop()); 173 174 mLauncher.movePointer( 175 downTime, 176 downTime, 177 ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, 178 start, 179 end, 180 LauncherInstrumentation.GestureScope.EXPECT_PILFER); 181 } 182 sendUpPointerToEnterOverviewToLauncher(long downTime)183 private void sendUpPointerToEnterOverviewToLauncher(long downTime) { 184 final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; 185 final int startY = getSwipeStartY(); 186 final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt( 187 TestProtocol.TEST_INFO_RESPONSE_FIELD); 188 final Point end = 189 new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop()); 190 191 mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(), 192 MotionEvent.ACTION_UP, end, 193 LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER); 194 } 195 196 /** 197 * Quick switching to the app with swiping to right. 198 */ 199 @NonNull quickSwitchToPreviousApp()200 public LaunchedAppState quickSwitchToPreviousApp() { 201 quickSwitch(true /* toRight */); 202 return new LaunchedAppState(mLauncher); 203 } 204 205 /** 206 * Quick switching to the app with swiping to left. 207 */ 208 @NonNull quickSwitchToPreviousAppSwipeLeft()209 public LaunchedAppState quickSwitchToPreviousAppSwipeLeft() { 210 quickSwitch(false /* toRight */); 211 return new LaunchedAppState(mLauncher); 212 } 213 214 /** 215 * Making swipe gesture to quick-switch app tasks. 216 * 217 * @param toRight {@code true} means swiping right, {@code false} means swiping left. 218 * @throws {@link AssertionError} when failing to verify the visible UI in the container. 219 */ quickSwitch(boolean toRight)220 private void quickSwitch(boolean toRight) { 221 try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); 222 LauncherInstrumentation.Closable c = mLauncher.addContextLayer( 223 "want to quick switch to the previous app")) { 224 verifyActiveContainer(); 225 if (mLauncher.getNavigationModel() == NavigationModel.ZERO_BUTTON 226 || mLauncher.getTrackpadGestureType() == TrackpadGestureType.FOUR_FINGER) { 227 final int startX; 228 final int startY; 229 final int endX; 230 final int endY; 231 final int cornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius()); 232 if (toRight) { 233 // Swipe from the bottom left to the bottom right of the screen. 234 startX = cornerRadius; 235 startY = getSwipeStartY(); 236 endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; 237 endY = startY; 238 } else { 239 // Swipe from the bottom right to the bottom left of the screen. 240 startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius; 241 startY = getSwipeStartY(); 242 endX = cornerRadius; 243 endY = startY; 244 } 245 246 mLauncher.executeAndWaitForLauncherStop( 247 () -> mLauncher.linearGesture( 248 startX, startY, endX, endY, 20, false, 249 LauncherInstrumentation.GestureScope.EXPECT_PILFER), 250 "swiping"); 251 } else { 252 // Double press the recents button. 253 UiObject2 recentsButton = mLauncher.waitForNavigationUiObject("recent_apps"); 254 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); 255 mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL, 256 "clicking Recents button for the first time"); 257 mLauncher.getOverview(); 258 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT); 259 mLauncher.executeAndWaitForLauncherStop( 260 () -> recentsButton.click(), 261 "clicking Recents button for the second time"); 262 } 263 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT); 264 } 265 } 266 getSwipeHeightRequestName()267 protected String getSwipeHeightRequestName() { 268 return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT; 269 } 270 getSwipeStartX()271 protected int getSwipeStartX() { 272 return mLauncher.getRealDisplaySize().x - 1; 273 } 274 getSwipeStartY()275 protected int getSwipeStartY() { 276 return mLauncher.getTrackpadGestureType() == TrackpadGestureType.THREE_FINGER 277 ? mLauncher.getDevice().getDisplayHeight() * 3 / 4 278 : mLauncher.getRealDisplaySize().y - 1; 279 } 280 } 281