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