• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 package com.android.launcher3.tapl;
17 
18 import static com.android.launcher3.tapl.LauncherInstrumentation.KEYBOARD_QUICK_SWITCH_RES_ID;
19 
20 import android.view.KeyEvent;
21 
22 import androidx.annotation.NonNull;
23 import androidx.annotation.Nullable;
24 
25 import com.android.launcher3.testing.shared.TestProtocol;
26 
27 import java.util.regex.Pattern;
28 
29 /**
30  * Operations on the Keyboard Quick Switch View
31  */
32 public final class KeyboardQuickSwitch {
33 
34     private static final Pattern EVENT_ALT_TAB_DOWN = Pattern.compile(
35             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_TAB"
36                     + ".*?metaState=META_ALT_ON");
37     private static final Pattern EVENT_ALT_TAB_UP = Pattern.compile(
38             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
39                     + ".*?metaState=META_ALT_ON");
40     private static final Pattern EVENT_ALT_SHIFT_TAB_UP = Pattern.compile(
41             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
42                     + ".*?metaState=META_ALT_ON|META_SHIFT_ON");
43     private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
44             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
45                     + ".*?keyCode=KEYCODE_ESCAPE.*?metaState=META_ALT_ON");
46     private static final Pattern EVENT_KQS_ALT_LEFT_UP = Pattern.compile(
47             "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
48                     + ".*?keyCode=KEYCODE_ALT_LEFT");
49     private static final Pattern EVENT_HOME_ALT_LEFT_UP = Pattern.compile(
50             "Key event: KeyEvent.*?action=ACTION_UP"
51                     + ".*?keyCode=KEYCODE_ALT_LEFT");
52 
53     private final LauncherInstrumentation mLauncher;
54     private final LauncherInstrumentation.ContainerType mStartingContainerType;
55     private final boolean mIsHomeState;
56 
KeyboardQuickSwitch( LauncherInstrumentation launcher, LauncherInstrumentation.ContainerType startingContainerType, boolean isHomeState)57     KeyboardQuickSwitch(
58             LauncherInstrumentation launcher,
59             LauncherInstrumentation.ContainerType startingContainerType,
60             boolean isHomeState) {
61         mLauncher = launcher;
62         mStartingContainerType = startingContainerType;
63         mIsHomeState = isHomeState;
64     }
65 
66     /**
67      * Focuses the next task in the Keyboard quick switch view.
68      * <p>
69      * Tasks are ordered left-to-right in LTR, and vice versa in RLT, in a carousel.
70      * <ul>
71      *      <li>If no task has been focused yet, and there is only one task, then that task will be
72      *          focused</li>
73      *      <li>If no task has been focused yet, and there are two or more tasks, then the second
74      *          task will be focused</li>
75      *      <li>If the currently-focused task is at the end of the list, the first task will be
76      *          focused</li>
77      * </ul>
78      */
moveFocusForward()79     public KeyboardQuickSwitch moveFocusForward() {
80         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
81                 "want to move keyboard quick switch focus forward");
82              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
83             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
84             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_UP);
85             mLauncher.assertTrue("Failed to press alt+tab",
86                     mLauncher.getDevice().pressKeyCode(
87                             KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
88 
89             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
90                     "pressed alt+tab")) {
91                 mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
92 
93                 return this;
94             }
95         }
96     }
97 
98     /**
99      * Focuses the next task in the Keyboard quick switch view.
100      * <p>
101      * Tasks are ordered left-to-right in LTR, and vice versa in RLT, in a carousel.
102      * <ul>
103      *      <li>If no task has been focused yet, and there is only one task, then that task will be
104      *          focused</li>
105      *      <li>If no task has been focused yet, and there are two or more tasks, then the second
106      *          task will be focused</li>
107      *      <li>If the currently-focused task is at the start of the list, the last task will be
108      *          focused</li>
109      * </ul>
110      */
moveFocusBackward()111     public KeyboardQuickSwitch moveFocusBackward() {
112         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
113                 "want to move keyboard quick switch focus backward");
114              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
115             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
116 
117             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_UP);
118             mLauncher.assertTrue("Failed to press alt+shift+tab",
119                     mLauncher.getDevice().pressKeyCode(
120                             KeyEvent.KEYCODE_TAB,
121                             KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON));
122 
123             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
124                     "pressed alt+shift+tab")) {
125                 mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
126 
127                 return this;
128             }
129         }
130     }
131 
132     /**
133      * Dismisses the Keyboard Quick Switch view without launching the focused task.
134      * <p>
135      * The device will return to the same state it started in before displaying the Keyboard Quick
136      * Switch view.
137      */
dismiss()138     public void dismiss() {
139         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
140                 "want to dismiss keyboard quick switch view");
141              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
142             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
143 
144             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
145             mLauncher.assertTrue("Failed to press alt+tab",
146                     mLauncher.getDevice().pressKeyCode(
147                             KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_ALT_ON));
148 
149             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
150                     "pressed alt+esc")) {
151                 mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
152 
153                 // Verify the final state is the same as the initial state
154                 mLauncher.verifyContainerType(mStartingContainerType);
155 
156                 // Wait until the device has fully settled before unpressing the key code
157                 if (mIsHomeState) {
158                     mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_HOME_ALT_LEFT_UP);
159                 }
160                 mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
161             }
162         }
163     }
164 
165     /**
166      * Dismisses the Keyboard Quick Switch view by going home. After the Keyboard Quick Switch view
167      * gets hidden, it unpresses ALT key, which is generally used to keep the view visible.
168      */
dismissByGoingHome()169     public Workspace dismissByGoingHome() {
170         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
171                 "verifying keyboard quick switch view is shown")) {
172             mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
173         }
174 
175         mLauncher.goHome();
176 
177         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
178                 "waiting for keyboard quick switch dismissal");
179              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
180             mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
181         }
182 
183         try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
184                 "get workspace after releasing ALT key")) {
185             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_HOME_ALT_LEFT_UP);
186             mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
187             return mLauncher.getWorkspace();
188         }
189     }
190 
191     /**
192      * Launches the currently-focused app task.
193      * <p>
194      * This method should only be used if the focused task is for a recent running app, otherwise
195      * use {@link #launchFocusedOverviewTask()}.
196      *
197      * @param expectedPackageName the package name of the expected launched app
198      */
launchFocusedAppTask(@onNull String expectedPackageName)199     public LaunchedAppState launchFocusedAppTask(@NonNull String expectedPackageName) {
200         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
201             return (LaunchedAppState) launchFocusedTask(expectedPackageName);
202         }
203     }
204 
205     /**
206      * Launches the currently-focused overview task.
207      * <p>
208      * This method only should be used if the focused task is for overview, otherwise use
209      * {@link #launchFocusedAppTask(String)}.
210      */
launchFocusedOverviewTask()211     public Overview launchFocusedOverviewTask() {
212         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
213             return (Overview) launchFocusedTask(null);
214         }
215     }
216 
launchFocusedTask( @ullable String expectedPackageName)217     private LauncherInstrumentation.VisibleContainer launchFocusedTask(
218             @Nullable String expectedPackageName) {
219         try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
220                 "want to launch focused task: "
221                         + (expectedPackageName == null ? "Overview" : expectedPackageName))) {
222             mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KQS_ALT_LEFT_UP);
223 
224             if (expectedPackageName == null || !mIsHomeState) {
225                 mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0);
226             } else {
227                 mLauncher.executeAndWaitForLauncherStop(
228                         () -> mLauncher.unpressKeyCode(KeyEvent.KEYCODE_ALT_LEFT, 0),
229                         "unpressing left alt");
230             }
231 
232             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
233                     "un-pressed left alt")) {
234                 mLauncher.waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
235 
236                 if (expectedPackageName != null) {
237                     mLauncher.assertAppLaunched(expectedPackageName);
238                     return mLauncher.getLaunchedAppState();
239                 } else {
240                     return mLauncher.getOverview();
241                 }
242             }
243         }
244     }
245 }
246