1 package com.android.quickstep;
2 
3 import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
4 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
5 
6 import android.app.Activity;
7 import android.content.Context;
8 import android.content.res.Resources;
9 import android.graphics.Rect;
10 import android.os.Bundle;
11 
12 import androidx.annotation.Nullable;
13 
14 import com.android.launcher3.taskbar.TaskbarActivityContext;
15 import com.android.launcher3.testing.TestInformationHandler;
16 import com.android.launcher3.testing.shared.TestProtocol;
17 import com.android.launcher3.util.DisplayController;
18 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
19 import com.android.quickstep.util.GroupTask;
20 import com.android.quickstep.util.LayoutUtils;
21 import com.android.quickstep.util.TISBindHelper;
22 import com.android.quickstep.views.RecentsView;
23 
24 import java.util.ArrayList;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.TimeUnit;
28 import java.util.function.Consumer;
29 import java.util.function.Function;
30 
31 public class QuickstepTestInformationHandler extends TestInformationHandler {
32 
33     protected final Context mContext;
34 
QuickstepTestInformationHandler(Context context)35     public QuickstepTestInformationHandler(Context context) {
36         mContext = context;
37     }
38 
39     @Override
call(String method, String arg, @Nullable Bundle extras)40     public Bundle call(String method, String arg, @Nullable Bundle extras) {
41         final Bundle response = new Bundle();
42         switch (method) {
43             case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
44                 ArrayList<String> taskBaseIntentComponents = new ArrayList<>();
45                 CountDownLatch latch = new CountDownLatch(1);
46                 RecentsModel.INSTANCE.get(mContext).getTasks((taskGroups) -> {
47                     for (GroupTask group : taskGroups) {
48                         taskBaseIntentComponents.add(
49                                 group.task1.key.baseIntent.getComponent().flattenToString());
50                         if (group.task2 != null) {
51                             taskBaseIntentComponents.add(
52                                     group.task2.key.baseIntent.getComponent().flattenToString());
53                         }
54                     }
55                     latch.countDown();
56                 });
57                 try {
58                     latch.await(2, TimeUnit.SECONDS);
59                 } catch (InterruptedException e) {
60                     throw new RuntimeException(e);
61                 }
62                 response.putStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD,
63                         taskBaseIntentComponents);
64                 return response;
65             }
66 
67             case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
68                 final float swipeHeight =
69                         LayoutUtils.getDefaultSwipeHeight(mContext, mDeviceProfile);
70                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
71                 return response;
72             }
73 
74             case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
75                 final float swipeHeight = mDeviceProfile.heightPx / 2f;
76                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
77                 return response;
78             }
79 
80             case TestProtocol.REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET: {
81                 if (!mDeviceProfile.isTablet) {
82                     return null;
83                 }
84                 Rect focusedTaskRect = new Rect();
85                 LauncherActivityInterface.INSTANCE.calculateTaskSize(mContext, mDeviceProfile,
86                         focusedTaskRect, RecentsPagedOrientationHandler.PORTRAIT);
87                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, focusedTaskRect.height());
88                 return response;
89             }
90 
91             case TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET: {
92                 if (!mDeviceProfile.isTablet) {
93                     return null;
94                 }
95                 Rect gridTaskRect = new Rect();
96                 LauncherActivityInterface.INSTANCE.calculateGridTaskSize(mContext, mDeviceProfile,
97                         gridTaskRect, RecentsPagedOrientationHandler.PORTRAIT);
98                 response.putParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD, gridTaskRect);
99                 return response;
100             }
101 
102             case TestProtocol.REQUEST_GET_OVERVIEW_PAGE_SPACING: {
103                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
104                         mDeviceProfile.overviewPageSpacing);
105                 return response;
106             }
107 
108             case TestProtocol.REQUEST_GET_OVERVIEW_CURRENT_PAGE_INDEX: {
109                 return getLauncherUIProperty(Bundle::putInt,
110                         launcher -> launcher.<RecentsView>getOverviewPanel().getCurrentPage());
111             }
112 
113             case TestProtocol.REQUEST_HAS_TIS: {
114                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true);
115                 return response;
116             }
117 
118             case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
119                 runOnTISBinder(tisBinder -> {
120                     // Allow null-pointer to catch illegal states.
121                     tisBinder.getTaskbarManager().getCurrentActivityContext()
122                             .unstashTaskbarIfStashed();
123                 });
124                 return response;
125 
126             case TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD: {
127                 final Resources resources = mContext.getResources();
128                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
129                         getFromNavThreshold(resources, mDeviceProfile));
130                 return response;
131             }
132 
133             case TestProtocol.REQUEST_STASHED_TASKBAR_SCALE: {
134                 runOnTISBinder(tisBinder -> {
135                     response.putFloat(TestProtocol.TEST_INFO_RESPONSE_FIELD,
136                             tisBinder.getTaskbarManager()
137                                     .getCurrentActivityContext()
138                                     .getStashedTaskbarScale());
139                 });
140                 return response;
141             }
142 
143             case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: {
144                 return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
145                         tisBinder.getTaskbarManager()
146                                 .getCurrentActivityContext()
147                                 .getTaskbarAllAppsTopPadding());
148             }
149 
150             case TestProtocol.REQUEST_TASKBAR_APPS_LIST_SCROLL_Y: {
151                 return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
152                         tisBinder.getTaskbarManager()
153                                 .getCurrentActivityContext()
154                                 .getTaskbarAllAppsScroll());
155             }
156 
157             case TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT:
158                 runOnTISBinder(tisBinder -> {
159                     enableBlockingTimeout(tisBinder, true);
160                 });
161                 return response;
162 
163             case TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT:
164                 runOnTISBinder(tisBinder -> {
165                     enableBlockingTimeout(tisBinder, false);
166                 });
167                 return response;
168 
169             case TestProtocol.REQUEST_ENABLE_TRANSIENT_TASKBAR:
170                 enableTransientTaskbar(true);
171                 return response;
172 
173             case TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR:
174                 enableTransientTaskbar(false);
175                 return response;
176 
177             case TestProtocol.REQUEST_SHELL_DRAG_READY:
178                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
179                         SystemUiProxy.INSTANCE.get(mContext).isDragAndDropReady());
180                 return response;
181 
182             case TestProtocol.REQUEST_REFRESH_OVERVIEW_TARGET:
183                 runOnTISBinder(TouchInteractionService.TISBinder::refreshOverviewTarget);
184                 return response;
185 
186             case TestProtocol.REQUEST_RECREATE_TASKBAR:
187                 // Allow null-pointer to catch illegal states.
188                 runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
189                 return response;
190             case TestProtocol.REQUEST_TASKBAR_IME_DOCKED:
191                 return getTISBinderUIProperty(Bundle::putBoolean, tisBinder ->
192                         tisBinder.getTaskbarManager()
193                                 .getCurrentActivityContext().isImeDocked());
194             case TestProtocol.REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED:
195                 runOnTISBinder(tisBinder -> {
196                     // Allow null-pointer to catch illegal states.
197                     tisBinder.getTaskbarManager().getCurrentActivityContext()
198                             .unstashBubbleBarIfStashed();
199                 });
200                 return response;
201             case TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD:
202                 runOnTISBinder(tisBinder -> tisBinder.injectFakeTrackpadForTesting());
203                 return response;
204             case TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD:
205                 runOnTISBinder(tisBinder -> tisBinder.ejectFakeTrackpadForTesting());
206                 return response;
207         }
208 
209         return super.call(method, arg, extras);
210     }
211 
212     @Override
getCurrentActivity()213     protected Activity getCurrentActivity() {
214         RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
215         OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
216         try {
217             return observer.getActivityInterface().getCreatedContainer();
218         } finally {
219             observer.onDestroy();
220             rads.destroy();
221         }
222     }
223 
224     @Override
isLauncherInitialized()225     protected boolean isLauncherInitialized() {
226         return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
227     }
228 
enableBlockingTimeout( TouchInteractionService.TISBinder tisBinder, boolean enable)229     private void enableBlockingTimeout(
230             TouchInteractionService.TISBinder tisBinder, boolean enable) {
231         TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext();
232         if (context == null) {
233             return;
234         }
235         context.enableBlockingTimeoutDuringTests(enable);
236     }
237 
enableTransientTaskbar(boolean enable)238     private void enableTransientTaskbar(boolean enable) {
239         DisplayController.INSTANCE.get(mContext).enableTransientTaskbarForTests(enable);
240     }
241 
242     /**
243      * Runs the given command on the UI thread, after ensuring we are connected to
244      * TouchInteractionService.
245      */
runOnTISBinder(Consumer<TouchInteractionService.TISBinder> connectionCallback)246     protected void runOnTISBinder(Consumer<TouchInteractionService.TISBinder> connectionCallback) {
247         try {
248             CountDownLatch countDownLatch = new CountDownLatch(1);
249             TISBindHelper helper = MAIN_EXECUTOR.submit(() ->
250                     new TISBindHelper(mContext, tisBinder -> {
251                         connectionCallback.accept(tisBinder);
252                         countDownLatch.countDown();
253                     })).get();
254             countDownLatch.await();
255             MAIN_EXECUTOR.execute(helper::onDestroy);
256         } catch (ExecutionException | InterruptedException e) {
257             throw new RuntimeException(e);
258         }
259     }
260 
getTISBinderUIProperty( BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider)261     private <T> Bundle getTISBinderUIProperty(
262             BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider) {
263         Bundle response = new Bundle();
264 
265         runOnTISBinder(tisBinder -> bundleSetter.set(
266                 response,
267                 TestProtocol.TEST_INFO_RESPONSE_FIELD,
268                 provider.apply(tisBinder)));
269 
270         return response;
271     }
272 }
273