1 package com.android.quickstep; 2 3 import static android.view.Display.DEFAULT_DISPLAY; 4 5 import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold; 6 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 7 8 import android.content.Context; 9 import android.content.res.Resources; 10 import android.os.Bundle; 11 import android.view.WindowInsets; 12 13 import androidx.annotation.Nullable; 14 15 import com.android.launcher3.taskbar.TaskbarActivityContext; 16 import com.android.launcher3.testing.TestInformationHandler; 17 import com.android.launcher3.testing.shared.TestProtocol; 18 import com.android.launcher3.util.DisplayController; 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 import com.android.quickstep.views.RecentsViewContainer; 24 import com.android.systemui.shared.recents.model.Task; 25 26 import java.util.ArrayList; 27 import java.util.concurrent.CountDownLatch; 28 import java.util.concurrent.ExecutionException; 29 import java.util.concurrent.TimeUnit; 30 import java.util.function.Consumer; 31 import java.util.function.Function; 32 33 public class QuickstepTestInformationHandler extends TestInformationHandler { 34 35 protected final Context mContext; 36 QuickstepTestInformationHandler(Context context)37 public QuickstepTestInformationHandler(Context context) { 38 mContext = context; 39 } 40 41 @Override call(String method, String arg, @Nullable Bundle extras)42 public Bundle call(String method, String arg, @Nullable Bundle extras) { 43 final Bundle response = new Bundle(); 44 switch (method) { 45 case TestProtocol.REQUEST_RECENT_TASKS_LIST: { 46 ArrayList<String> taskBaseIntentComponents = new ArrayList<>(); 47 CountDownLatch latch = new CountDownLatch(1); 48 RecentsModel.INSTANCE.get(mContext).getTasks((taskGroups) -> { 49 for (GroupTask group : taskGroups) { 50 for (Task t : group.getTasks()) { 51 taskBaseIntentComponents.add( 52 t.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_OVERVIEW_TASK_SIZE: { 81 return getUIProperty(Bundle::putParcelable, 82 recentsViewContainer -> 83 recentsViewContainer.<RecentsView<?, ?>>getOverviewPanel() 84 .getLastComputedTaskSize(), 85 this::getRecentsViewContainer); 86 } 87 88 case TestProtocol.REQUEST_GET_OVERVIEW_GRID_TASK_SIZE: { 89 return getUIProperty(Bundle::putParcelable, 90 recentsViewContainer -> 91 recentsViewContainer.<RecentsView<?, ?>>getOverviewPanel() 92 .getLastComputedGridTaskSize(), 93 this::getRecentsViewContainer); 94 } 95 96 case TestProtocol.REQUEST_GET_OVERVIEW_PAGE_SPACING: { 97 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, 98 mDeviceProfile.overviewPageSpacing); 99 return response; 100 } 101 102 case TestProtocol.REQUEST_GET_OVERVIEW_CURRENT_PAGE_INDEX: { 103 return getLauncherUIProperty(Bundle::putInt, 104 launcher -> launcher.<RecentsView>getOverviewPanel().getCurrentPage()); 105 } 106 107 case TestProtocol.REQUEST_HAS_TIS: { 108 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true); 109 return response; 110 } 111 112 case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED: 113 runOnTISBinder(tisBinder -> { 114 // Allow null-pointer to catch illegal states. 115 tisBinder.getTaskbarManager().getCurrentActivityContext() 116 .unstashTaskbarIfStashed(); 117 }); 118 return response; 119 120 case TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD: { 121 final Resources resources = mContext.getResources(); 122 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, 123 getFromNavThreshold(resources, mDeviceProfile)); 124 return response; 125 } 126 127 case TestProtocol.REQUEST_STASHED_TASKBAR_SCALE: { 128 runOnTISBinder(tisBinder -> { 129 response.putFloat(TestProtocol.TEST_INFO_RESPONSE_FIELD, 130 tisBinder.getTaskbarManager() 131 .getCurrentActivityContext() 132 .getStashedTaskbarScale()); 133 }); 134 return response; 135 } 136 137 case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: { 138 return getTISBinderUIProperty(Bundle::putInt, tisBinder -> 139 tisBinder.getTaskbarManager() 140 .getCurrentActivityContext() 141 .getTaskbarAllAppsTopPadding()); 142 } 143 144 case TestProtocol.REQUEST_TASKBAR_APPS_LIST_SCROLL_Y: { 145 return getTISBinderUIProperty(Bundle::putInt, tisBinder -> 146 tisBinder.getTaskbarManager() 147 .getCurrentActivityContext() 148 .getTaskbarAllAppsScroll()); 149 } 150 151 case TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT: 152 runOnTISBinder(tisBinder -> { 153 enableBlockingTimeout(tisBinder, true); 154 }); 155 return response; 156 157 case TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT: 158 runOnTISBinder(tisBinder -> { 159 enableBlockingTimeout(tisBinder, false); 160 }); 161 return response; 162 163 case TestProtocol.REQUEST_ENABLE_TRANSIENT_TASKBAR: 164 enableTransientTaskbar(true); 165 return response; 166 167 case TestProtocol.REQUEST_DISABLE_TRANSIENT_TASKBAR: 168 enableTransientTaskbar(false); 169 return response; 170 171 case TestProtocol.REQUEST_SHELL_DRAG_READY: 172 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, 173 SystemUiProxy.INSTANCE.get(mContext).isDragAndDropReady()); 174 return response; 175 176 case TestProtocol.REQUEST_REFRESH_OVERVIEW_TARGET: 177 runOnTISBinder(TouchInteractionService.TISBinder::refreshOverviewTarget); 178 return response; 179 180 case TestProtocol.REQUEST_RECREATE_TASKBAR: 181 // Allow null-pointer to catch illegal states. 182 runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbars()); 183 return response; 184 case TestProtocol.REQUEST_TASKBAR_IME_DOCKED: 185 return getTISBinderUIProperty(Bundle::putBoolean, tisBinder -> 186 tisBinder.getTaskbarManager() 187 .getCurrentActivityContext().isImeDocked()); 188 case TestProtocol.REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED: 189 runOnTISBinder(tisBinder -> { 190 // Allow null-pointer to catch illegal states. 191 tisBinder.getTaskbarManager().getCurrentActivityContext() 192 .unstashBubbleBarIfStashed(); 193 }); 194 return response; 195 case TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD: 196 runOnTISBinder(tisBinder -> tisBinder.injectFakeTrackpadForTesting()); 197 return response; 198 case TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD: 199 runOnTISBinder(tisBinder -> tisBinder.ejectFakeTrackpadForTesting()); 200 return response; 201 } 202 203 return super.call(method, arg, extras); 204 } 205 206 @Override getWindowInsets()207 protected WindowInsets getWindowInsets() { 208 RecentsViewContainer container = getRecentsViewContainer(); 209 WindowInsets insets = container == null 210 ? null : container.getRootView().getRootWindowInsets(); 211 return insets == null ? super.getWindowInsets() : insets; 212 } 213 getRecentsViewContainer()214 private RecentsViewContainer getRecentsViewContainer() { 215 // TODO (b/400647896): support per-display container in e2e tests 216 return OverviewComponentObserver.INSTANCE.get(mContext) 217 .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer(); 218 } 219 220 @Override isLauncherInitialized()221 protected boolean isLauncherInitialized() { 222 return super.isLauncherInitialized() && SystemUiProxy.INSTANCE.get(mContext).isActive(); 223 } 224 enableBlockingTimeout( TouchInteractionService.TISBinder tisBinder, boolean enable)225 private void enableBlockingTimeout( 226 TouchInteractionService.TISBinder tisBinder, boolean enable) { 227 TaskbarActivityContext context = tisBinder.getTaskbarManager().getCurrentActivityContext(); 228 if (context == null) { 229 return; 230 } 231 context.enableBlockingTimeoutDuringTests(enable); 232 } 233 enableTransientTaskbar(boolean enable)234 private void enableTransientTaskbar(boolean enable) { 235 DisplayController.INSTANCE.get(mContext).enableTransientTaskbarForTests(enable); 236 } 237 238 /** 239 * Runs the given command on the UI thread, after ensuring we are connected to 240 * TouchInteractionService. 241 */ runOnTISBinder(Consumer<TouchInteractionService.TISBinder> connectionCallback)242 protected void runOnTISBinder(Consumer<TouchInteractionService.TISBinder> connectionCallback) { 243 try { 244 CountDownLatch countDownLatch = new CountDownLatch(1); 245 TISBindHelper helper = MAIN_EXECUTOR.submit(() -> 246 new TISBindHelper(mContext, tisBinder -> { 247 connectionCallback.accept(tisBinder); 248 countDownLatch.countDown(); 249 })).get(); 250 countDownLatch.await(); 251 MAIN_EXECUTOR.execute(helper::onDestroy); 252 } catch (ExecutionException | InterruptedException e) { 253 throw new RuntimeException(e); 254 } 255 } 256 getTISBinderUIProperty( BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider)257 private <T> Bundle getTISBinderUIProperty( 258 BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider) { 259 Bundle response = new Bundle(); 260 261 runOnTISBinder(tisBinder -> bundleSetter.set( 262 response, 263 TestProtocol.TEST_INFO_RESPONSE_FIELD, 264 provider.apply(tisBinder))); 265 266 return response; 267 } 268 } 269