1 /* 2 * Copyright (C) 2022 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.quickstep; 17 18 import static android.view.Display.DEFAULT_DISPLAY; 19 import static android.view.MotionEvent.ACTION_CANCEL; 20 import static android.view.MotionEvent.ACTION_DOWN; 21 import static android.view.MotionEvent.ACTION_MOVE; 22 import static android.view.MotionEvent.ACTION_POINTER_DOWN; 23 import static android.view.MotionEvent.ACTION_POINTER_UP; 24 import static android.view.MotionEvent.ACTION_UP; 25 26 import static com.android.launcher3.Flags.enableCursorHoverStates; 27 import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks; 28 import static com.android.launcher3.LauncherPrefs.backedUpItem; 29 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; 30 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; 31 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 32 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 33 import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN; 34 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH; 35 import static com.android.quickstep.GestureState.DEFAULT_STATE; 36 import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType; 37 import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER; 38 import static com.android.quickstep.InputConsumer.createNoOpInputConsumer; 39 import static com.android.quickstep.InputConsumerUtils.newConsumer; 40 import static com.android.quickstep.InputConsumerUtils.tryCreateAssistantInputConsumer; 41 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; 42 43 import android.app.PendingIntent; 44 import android.app.Service; 45 import android.content.Context; 46 import android.content.IIntentReceiver; 47 import android.content.IIntentSender; 48 import android.content.Intent; 49 import android.content.res.Configuration; 50 import android.graphics.Region; 51 import android.os.Bundle; 52 import android.os.IBinder; 53 import android.os.IRemoteCallback; 54 import android.os.Looper; 55 import android.os.RemoteException; 56 import android.os.SystemClock; 57 import android.util.Log; 58 import android.view.Choreographer; 59 import android.view.Display; 60 import android.view.InputDevice; 61 import android.view.InputEvent; 62 import android.view.MotionEvent; 63 import android.window.DesktopModeFlags; 64 65 import androidx.annotation.BinderThread; 66 import androidx.annotation.NonNull; 67 import androidx.annotation.Nullable; 68 import androidx.annotation.UiThread; 69 import androidx.annotation.VisibleForTesting; 70 71 import com.android.launcher3.ConstantItem; 72 import com.android.launcher3.EncryptionType; 73 import com.android.launcher3.Flags; 74 import com.android.launcher3.LauncherPrefs; 75 import com.android.launcher3.anim.AnimatedFloat; 76 import com.android.launcher3.desktop.DesktopAppLaunchTransitionManager; 77 import com.android.launcher3.statehandlers.DesktopVisibilityController; 78 import com.android.launcher3.statemanager.StatefulActivity; 79 import com.android.launcher3.taskbar.TaskbarActivityContext; 80 import com.android.launcher3.taskbar.TaskbarManager; 81 import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks; 82 import com.android.launcher3.taskbar.bubbles.BubbleControllers; 83 import com.android.launcher3.testing.TestLogging; 84 import com.android.launcher3.testing.shared.ResourceUtils; 85 import com.android.launcher3.testing.shared.TestProtocol; 86 import com.android.launcher3.util.DisplayController; 87 import com.android.launcher3.util.LockedUserState; 88 import com.android.launcher3.util.MSDLPlayerWrapper; 89 import com.android.launcher3.util.NavigationMode; 90 import com.android.launcher3.util.PluginManagerWrapper; 91 import com.android.launcher3.util.SafeCloseable; 92 import com.android.launcher3.util.ScreenOnTracker; 93 import com.android.launcher3.util.TraceHelper; 94 import com.android.quickstep.OverviewCommandHelper.CommandType; 95 import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener; 96 import com.android.quickstep.fallback.window.RecentsDisplayModel; 97 import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource; 98 import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler; 99 import com.android.quickstep.inputconsumers.BubbleBarInputConsumer; 100 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer; 101 import com.android.quickstep.util.ActiveGestureLog; 102 import com.android.quickstep.util.ActiveGestureLog.CompoundString; 103 import com.android.quickstep.util.ActiveGestureProtoLogProxy; 104 import com.android.quickstep.util.ActiveTrackpadList; 105 import com.android.quickstep.util.ActivityPreloadUtil; 106 import com.android.quickstep.util.ContextualSearchInvoker; 107 import com.android.quickstep.util.ContextualSearchStateManager; 108 import com.android.quickstep.views.RecentsViewContainer; 109 import com.android.systemui.shared.recents.ILauncherProxy; 110 import com.android.systemui.shared.recents.ISystemUiProxy; 111 import com.android.systemui.shared.statusbar.phone.BarTransitions; 112 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; 113 import com.android.systemui.shared.system.InputConsumerController; 114 import com.android.systemui.shared.system.InputMonitorCompat; 115 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; 116 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; 117 import com.android.systemui.unfold.progress.IUnfoldAnimation; 118 import com.android.wm.shell.back.IBackAnimation; 119 import com.android.wm.shell.bubbles.IBubbles; 120 import com.android.wm.shell.common.pip.IPip; 121 import com.android.wm.shell.desktopmode.IDesktopMode; 122 import com.android.wm.shell.draganddrop.IDragAndDrop; 123 import com.android.wm.shell.onehanded.IOneHanded; 124 import com.android.wm.shell.recents.IRecentTasks; 125 import com.android.wm.shell.shared.IShellTransitions; 126 import com.android.wm.shell.splitscreen.ISplitScreen; 127 import com.android.wm.shell.startingsurface.IStartingWindow; 128 129 import java.io.FileDescriptor; 130 import java.io.PrintWriter; 131 import java.lang.ref.WeakReference; 132 import java.util.Locale; 133 import java.util.function.Consumer; 134 import java.util.function.Function; 135 136 /** 137 * Service connected by system-UI for handling touch interaction. 138 */ 139 public class TouchInteractionService extends Service { 140 141 private static final String SUBSTRING_PREFIX = "; "; 142 143 private static final String TAG = "TouchInteractionService"; 144 145 private static final ConstantItem<Boolean> HAS_ENABLED_QUICKSTEP_ONCE = backedUpItem( 146 "launcher.has_enabled_quickstep_once", false, EncryptionType.ENCRYPTED); 147 148 private static final DesktopModeFlags.DesktopModeFlag ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS = 149 new DesktopModeFlags.DesktopModeFlag(Flags::enableGestureNavOnConnectedDisplays, false); 150 151 private final TISBinder mTISBinder = new TISBinder(this); 152 153 /** 154 * Local ILauncherProxy implementation with some methods for local components 155 */ 156 public static class TISBinder extends ILauncherProxy.Stub { 157 158 private final WeakReference<TouchInteractionService> mTis; 159 TISBinder(TouchInteractionService tis)160 private TISBinder(TouchInteractionService tis) { 161 mTis = new WeakReference<>(tis); 162 } 163 164 @BinderThread onInitialize(Bundle bundle)165 public void onInitialize(Bundle bundle) { 166 ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface( 167 bundle.getBinder(ISystemUiProxy.DESCRIPTOR)); 168 IPip pip = IPip.Stub.asInterface(bundle.getBinder(IPip.DESCRIPTOR)); 169 IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(IBubbles.DESCRIPTOR)); 170 ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder( 171 ISplitScreen.DESCRIPTOR)); 172 IOneHanded onehanded = IOneHanded.Stub.asInterface( 173 bundle.getBinder(IOneHanded.DESCRIPTOR)); 174 IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface( 175 bundle.getBinder(IShellTransitions.DESCRIPTOR)); 176 IStartingWindow startingWindow = IStartingWindow.Stub.asInterface( 177 bundle.getBinder(IStartingWindow.DESCRIPTOR)); 178 ISysuiUnlockAnimationController launcherUnlockAnimationController = 179 ISysuiUnlockAnimationController.Stub.asInterface( 180 bundle.getBinder(ISysuiUnlockAnimationController.DESCRIPTOR)); 181 IRecentTasks recentTasks = IRecentTasks.Stub.asInterface( 182 bundle.getBinder(IRecentTasks.DESCRIPTOR)); 183 IBackAnimation backAnimation = IBackAnimation.Stub.asInterface( 184 bundle.getBinder(IBackAnimation.DESCRIPTOR)); 185 IDesktopMode desktopMode = IDesktopMode.Stub.asInterface( 186 bundle.getBinder(IDesktopMode.DESCRIPTOR)); 187 IUnfoldAnimation unfoldTransition = IUnfoldAnimation.Stub.asInterface( 188 bundle.getBinder(IUnfoldAnimation.DESCRIPTOR)); 189 IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface( 190 bundle.getBinder(IDragAndDrop.DESCRIPTOR)); 191 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 192 SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip, 193 bubbles, splitscreen, onehanded, shellTransitions, startingWindow, 194 recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode, 195 unfoldTransition, dragAndDrop); 196 tis.initInputMonitor("TISBinder#onInitialize()"); 197 ActivityPreloadUtil.preloadOverviewForTIS(tis, true /* fromInit */); 198 })); 199 } 200 201 @BinderThread 202 @Override onTaskbarToggled()203 public void onTaskbarToggled() { 204 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 205 TaskbarActivityContext activityContext = 206 tis.mTaskbarManager.getCurrentActivityContext(); 207 208 if (activityContext != null) { 209 activityContext.toggleTaskbarStash(); 210 } 211 })); 212 } 213 214 @BinderThread onOverviewToggle()215 public void onOverviewToggle() { 216 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle"); 217 executeForTouchInteractionService(tis -> { 218 // If currently screen pinning, do not enter overview 219 if (tis.mDeviceState.isScreenPinningActive()) { 220 return; 221 } 222 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); 223 tis.mOverviewCommandHelper.addCommand(CommandType.TOGGLE); 224 }); 225 } 226 227 @BinderThread 228 @Override onOverviewShown(boolean triggeredFromAltTab)229 public void onOverviewShown(boolean triggeredFromAltTab) { 230 executeForTouchInteractionService(tis -> { 231 if (triggeredFromAltTab) { 232 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); 233 tis.mOverviewCommandHelper.addCommand(CommandType.KEYBOARD_INPUT); 234 } else { 235 tis.mOverviewCommandHelper.addCommand(CommandType.SHOW); 236 } 237 }); 238 } 239 240 @BinderThread 241 @Override onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)242 public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 243 executeForTouchInteractionService(tis -> { 244 if (triggeredFromAltTab && !triggeredFromHomeKey) { 245 // onOverviewShownFromAltTab hides the overview and ends at the target app 246 tis.mOverviewCommandHelper.addCommand(CommandType.HIDE); 247 } 248 }); 249 } 250 251 @BinderThread 252 @Override onAssistantAvailable(boolean available, boolean longPressHomeEnabled)253 public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) { 254 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 255 tis.mDeviceState.setAssistantAvailable(available); 256 tis.onAssistantVisibilityChanged(); 257 executeForTaskbarManager(taskbarManager -> taskbarManager 258 .onLongPressHomeEnabled(longPressHomeEnabled)); 259 })); 260 } 261 262 @BinderThread 263 @Override onAssistantVisibilityChanged(float visibility)264 public void onAssistantVisibilityChanged(float visibility) { 265 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 266 tis.mDeviceState.setAssistantVisibility(visibility); 267 tis.onAssistantVisibilityChanged(); 268 })); 269 } 270 271 /** 272 * Sent when the assistant has been invoked with the given type (defined in AssistManager) 273 * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested 274 * was previously called including this invocation type. 275 */ 276 @Override onAssistantOverrideInvoked(int invocationType)277 public void onAssistantOverrideInvoked(int invocationType) { 278 executeForTouchInteractionService(tis -> { 279 if (!new ContextualSearchInvoker(tis).tryStartAssistOverride(invocationType)) { 280 Log.w(TAG, "Failed to invoke Assist override"); 281 } 282 }); 283 } 284 285 @BinderThread onSystemUiStateChanged(@ystemUiStateFlags long stateFlags, int displayId)286 public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags, int displayId) { 287 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 288 // Last flags is only used for the default display case. 289 long lastFlags = tis.mDeviceState.getSysuiStateFlag(); 290 tis.mDeviceState.setSysUIStateFlagsForDisplay(stateFlags, displayId); 291 tis.onSystemUiFlagsChanged(lastFlags, displayId); 292 })); 293 } 294 295 @BinderThread onActiveNavBarRegionChanges(Region region)296 public void onActiveNavBarRegionChanges(Region region) { 297 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService( 298 tis -> tis.mDeviceState.setDeferredGestureRegion(region))); 299 } 300 301 @BinderThread 302 @Override enterStageSplitFromRunningApp(boolean leftOrTop)303 public void enterStageSplitFromRunningApp(boolean leftOrTop) { 304 executeForTouchInteractionService(tis -> { 305 // TODO (b/397942185): support external displays 306 RecentsViewContainer container = tis.mOverviewComponentObserver 307 .getContainerInterface(DEFAULT_DISPLAY).getCreatedContainer(); 308 if (container != null) { 309 container.enterStageSplitFromRunningApp(leftOrTop); 310 } 311 }); 312 } 313 314 @BinderThread 315 @Override onDisplayAddSystemDecorations(int displayId)316 public void onDisplayAddSystemDecorations(int displayId) { 317 executeForTouchInteractionService(tis -> 318 tis.mSystemDecorationChangeObserver.notifyAddSystemDecorations(displayId)); 319 } 320 321 @BinderThread 322 @Override onDisplayRemoved(int displayId)323 public void onDisplayRemoved(int displayId) { 324 executeForTouchInteractionService(tis -> { 325 tis.mSystemDecorationChangeObserver.notifyOnDisplayRemoved(displayId); 326 tis.mDeviceState.clearSysUIStateFlagsForDisplay(displayId); 327 }); 328 } 329 330 @BinderThread 331 @Override onDisplayRemoveSystemDecorations(int displayId)332 public void onDisplayRemoveSystemDecorations(int displayId) { 333 executeForTouchInteractionService(tis -> { 334 tis.mSystemDecorationChangeObserver.notifyDisplayRemoveSystemDecorations(displayId); 335 }); 336 } 337 338 @BinderThread 339 @Override updateWallpaperVisibility(int displayId, boolean visible)340 public void updateWallpaperVisibility(int displayId, boolean visible) { 341 executeForTaskbarManager(taskbarManager -> 342 taskbarManager.setWallpaperVisible(displayId, visible)); 343 } 344 345 @BinderThread 346 @Override checkNavBarModes(int displayId)347 public void checkNavBarModes(int displayId) { 348 executeForTaskbarManager(taskbarManager -> 349 taskbarManager.checkNavBarModes(displayId)); 350 } 351 352 @BinderThread 353 @Override finishBarAnimations(int displayId)354 public void finishBarAnimations(int displayId) { 355 executeForTaskbarManager(taskbarManager -> 356 taskbarManager.finishBarAnimations(displayId)); 357 } 358 359 @BinderThread 360 @Override touchAutoDim(int displayId, boolean reset)361 public void touchAutoDim(int displayId, boolean reset) { 362 executeForTaskbarManager(taskbarManager -> 363 taskbarManager.touchAutoDim(displayId, reset)); 364 } 365 366 @BinderThread 367 @Override transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate)368 public void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, 369 boolean animate) { 370 executeForTaskbarManager(taskbarManager -> 371 taskbarManager.transitionTo(displayId, barMode, animate)); 372 } 373 374 @BinderThread 375 @Override appTransitionPending(boolean pending)376 public void appTransitionPending(boolean pending) { 377 executeForTaskbarManager(taskbarManager -> 378 taskbarManager.appTransitionPending(pending)); 379 } 380 381 @Override onRotationProposal(int rotation, boolean isValid)382 public void onRotationProposal(int rotation, boolean isValid) { 383 executeForTaskbarManager(taskbarManager -> 384 taskbarManager.onRotationProposal(rotation, isValid)); 385 } 386 387 @Override disable(int displayId, int state1, int state2, boolean animate)388 public void disable(int displayId, int state1, int state2, boolean animate) { 389 executeForTaskbarManager(taskbarManager -> 390 taskbarManager.disableNavBarElements(displayId, state1, state2, animate)); 391 } 392 393 @Override onSystemBarAttributesChanged(int displayId, int behavior)394 public void onSystemBarAttributesChanged(int displayId, int behavior) { 395 executeForTaskbarManager(taskbarManager -> 396 taskbarManager.onSystemBarAttributesChanged(displayId, behavior)); 397 } 398 399 @Override onTransitionModeUpdated(int barMode, boolean checkBarModes)400 public void onTransitionModeUpdated(int barMode, boolean checkBarModes) { 401 executeForTaskbarManager(taskbarManager -> 402 taskbarManager.onTransitionModeUpdated(barMode, checkBarModes)); 403 } 404 405 @Override onNavButtonsDarkIntensityChanged(float darkIntensity)406 public void onNavButtonsDarkIntensityChanged(float darkIntensity) { 407 executeForTaskbarManager(taskbarManager -> 408 taskbarManager.onNavButtonsDarkIntensityChanged(darkIntensity)); 409 } 410 411 @Override onNavigationBarLumaSamplingEnabled(int displayId, boolean enable)412 public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) { 413 executeForTaskbarManager(taskbarManager -> 414 taskbarManager.onNavigationBarLumaSamplingEnabled(displayId, enable)); 415 } 416 417 @Override onUnbind(IRemoteCallback reply)418 public void onUnbind(IRemoteCallback reply) { 419 // Run everything in the same main thread block to ensure the cleanup happens before 420 // sending the reply. 421 MAIN_EXECUTOR.execute(() -> { 422 executeForTaskbarManager(TaskbarManager::destroy); 423 try { 424 reply.sendResult(null); 425 } catch (RemoteException e) { 426 Log.w(TAG, "onUnbind: Failed to reply to LauncherProxyService", e); 427 } 428 }); 429 } 430 executeForTouchInteractionService( @onNull Consumer<TouchInteractionService> tisConsumer)431 private void executeForTouchInteractionService( 432 @NonNull Consumer<TouchInteractionService> tisConsumer) { 433 TouchInteractionService tis = mTis.get(); 434 if (tis == null) return; 435 tisConsumer.accept(tis); 436 } 437 executeForTaskbarManager( @onNull Consumer<TaskbarManager> taskbarManagerConsumer)438 private void executeForTaskbarManager( 439 @NonNull Consumer<TaskbarManager> taskbarManagerConsumer) { 440 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 441 TaskbarManager taskbarManager = tis.mTaskbarManager; 442 if (taskbarManager == null) return; 443 taskbarManagerConsumer.accept(taskbarManager); 444 })); 445 } 446 447 /** 448 * Returns the {@link TaskbarManager}. 449 * <p> 450 * Returns {@code null} if TouchInteractionService is not connected 451 */ 452 @Nullable getTaskbarManager()453 public TaskbarManager getTaskbarManager() { 454 TouchInteractionService tis = mTis.get(); 455 if (tis == null) return null; 456 return tis.mTaskbarManager; 457 } 458 459 @VisibleForTesting injectFakeTrackpadForTesting()460 public void injectFakeTrackpadForTesting() { 461 TouchInteractionService tis = mTis.get(); 462 if (tis == null) return; 463 tis.mTrackpadsConnected.add(1000); 464 tis.initInputMonitor("tapl testing"); 465 } 466 467 @VisibleForTesting ejectFakeTrackpadForTesting()468 public void ejectFakeTrackpadForTesting() { 469 TouchInteractionService tis = mTis.get(); 470 if (tis == null) return; 471 tis.mTrackpadsConnected.clear(); 472 // This method destroys the current input monitor if set up, and only init a new one 473 // in 3-button mode if {@code mTrackpadsConnected} is not empty. So in other words, 474 // it will destroy the input monitor. 475 tis.initInputMonitor("tapl testing"); 476 } 477 478 /** 479 * Sets whether a predictive back-to-home animation is in progress in the device state 480 */ setPredictiveBackToHomeInProgress(boolean isInProgress)481 public void setPredictiveBackToHomeInProgress(boolean isInProgress) { 482 executeForTouchInteractionService(tis -> 483 tis.mDeviceState.setPredictiveBackToHomeInProgress(isInProgress)); 484 } 485 486 /** 487 * Returns the {@link OverviewCommandHelper}. 488 * <p> 489 * Returns {@code null} if TouchInteractionService is not connected 490 */ 491 @Nullable getOverviewCommandHelper()492 public OverviewCommandHelper getOverviewCommandHelper() { 493 TouchInteractionService tis = mTis.get(); 494 if (tis == null) return null; 495 return tis.mOverviewCommandHelper; 496 } 497 498 /** 499 * Sets a proxy to bypass swipe up behavior 500 */ setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy)501 public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) { 502 executeForTouchInteractionService( 503 tis -> tis.mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null)); 504 } 505 506 /** 507 * Sets the task id where gestures should be blocked 508 */ setGestureBlockedTaskId(int taskId)509 public void setGestureBlockedTaskId(int taskId) { 510 executeForTouchInteractionService( 511 tis -> tis.mDeviceState.setGestureBlockingTaskId(taskId)); 512 } 513 514 /** Refreshes the current overview target. */ refreshOverviewTarget()515 public void refreshOverviewTarget() { 516 executeForTouchInteractionService(tis -> { 517 tis.mAllAppsActionManager.onDestroy(); 518 tis.onOverviewTargetChanged(tis.mOverviewComponentObserver.isHomeAndOverviewSame()); 519 }); 520 } 521 } 522 523 private RotationTouchHelper mRotationTouchHelper; 524 525 private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory = 526 this::createLauncherSwipeHandler; 527 private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory = 528 this::createFallbackSwipeHandler; 529 private final AbsSwipeUpHandler.Factory mRecentsWindowSwipeHandlerFactory = 530 this::createRecentsWindowSwipeHandler; 531 // This needs to be a member to be queued and potentially removed later if the service is 532 // destroyed before the user is unlocked 533 private final Runnable mUserUnlockedRunnable = this::onUserUnlocked; 534 535 private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged; 536 private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged; 537 538 private final TaskbarNavButtonCallbacks mNavCallbacks = new TaskbarNavButtonCallbacks() { 539 @Override 540 public void onNavigateHome() { 541 mOverviewCommandHelper.addCommand(CommandType.HOME); 542 } 543 544 @Override 545 public void onToggleOverview() { 546 mOverviewCommandHelper.addCommand(CommandType.TOGGLE); 547 } 548 549 @Override 550 public void onHideOverview() { 551 mOverviewCommandHelper.addCommand(CommandType.HIDE); 552 } 553 }; 554 555 private OverviewCommandHelper mOverviewCommandHelper; 556 private OverviewComponentObserver mOverviewComponentObserver; 557 private InputConsumerController mInputConsumer; 558 private RecentsAnimationDeviceState mDeviceState; 559 560 private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.DEFAULT_NO_OP; 561 private @NonNull InputConsumer mConsumer = InputConsumer.DEFAULT_NO_OP; 562 private Choreographer mMainChoreographer; 563 private boolean mUserUnlocked = false; 564 private GestureState mGestureState = DEFAULT_STATE; 565 566 private InputMonitorDisplayModel mInputMonitorDisplayModel; 567 private InputMonitorCompat mInputMonitorCompat; 568 private InputEventReceiver mInputEventReceiver; 569 570 private TaskbarManager mTaskbarManager; 571 private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null; 572 private AllAppsActionManager mAllAppsActionManager; 573 private ActiveTrackpadList mTrackpadsConnected; 574 575 private NavigationMode mGestureStartNavMode = null; 576 577 private DesktopAppLaunchTransitionManager mDesktopAppLaunchTransitionManager; 578 579 private DisplayController.DisplayInfoChangeListener mDisplayInfoChangeListener; 580 581 private RecentsDisplayModel mRecentsDisplayModel; 582 583 private SystemDecorationChangeObserver mSystemDecorationChangeObserver; 584 585 @Override onCreate()586 public void onCreate() { 587 super.onCreate(); 588 Log.d(TAG, "onCreate: user=" + getUserId() 589 + " instance=" + System.identityHashCode(this)); 590 // Initialize anything here that is needed in direct boot mode. 591 // Everything else should be initialized in onUserUnlocked() below. 592 mMainChoreographer = Choreographer.getInstance(); 593 mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this); 594 mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this); 595 mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this); 596 mSystemDecorationChangeObserver = SystemDecorationChangeObserver.getINSTANCE().get(this); 597 mAllAppsActionManager = new AllAppsActionManager( 598 this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent); 599 mTrackpadsConnected = new ActiveTrackpadList(this, () -> { 600 if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) { 601 // Don't destroy and reinitialize input monitor due to trackpad 602 // connecting when it's already set up. 603 return; 604 } 605 initInputMonitor("onTrackpadConnected()"); 606 }); 607 608 mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks, 609 mRecentsDisplayModel); 610 mDesktopAppLaunchTransitionManager = 611 new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this)); 612 mDesktopAppLaunchTransitionManager.registerTransitions(); 613 mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); 614 615 // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized. 616 LockedUserState.get(this).runOnUserUnlocked(mUserUnlockedRunnable); 617 mDisplayInfoChangeListener = 618 mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); 619 ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener); 620 } 621 622 @Nullable getInputEventReceiver(int displayId)623 private InputEventReceiver getInputEventReceiver(int displayId) { 624 if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) { 625 InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null 626 ? null : mInputMonitorDisplayModel.getDisplayResource(displayId); 627 return inputMonitorResource == null ? null : inputMonitorResource.inputEventReceiver; 628 } 629 return mInputEventReceiver; 630 } 631 632 @Nullable getInputMonitorCompat(int displayId)633 private InputMonitorCompat getInputMonitorCompat(int displayId) { 634 if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) { 635 InputMonitorResource inputMonitorResource = mInputMonitorDisplayModel == null 636 ? null : mInputMonitorDisplayModel.getDisplayResource(displayId); 637 return inputMonitorResource == null ? null : inputMonitorResource.inputMonitorCompat; 638 } 639 return mInputMonitorCompat; 640 } 641 disposeEventHandlers(String reason)642 private void disposeEventHandlers(String reason) { 643 Log.d(TAG, "disposeEventHandlers: Reason: " + reason 644 + " instance=" + System.identityHashCode(this)); 645 if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) { 646 if (mInputMonitorDisplayModel == null) return; 647 mInputMonitorDisplayModel.destroy(); 648 return; 649 } 650 if (mInputEventReceiver != null) { 651 mInputEventReceiver.dispose(); 652 mInputEventReceiver = null; 653 } 654 if (mInputMonitorCompat != null) { 655 mInputMonitorCompat.dispose(); 656 mInputMonitorCompat = null; 657 } 658 } 659 initInputMonitor(String reason)660 private void initInputMonitor(String reason) { 661 disposeEventHandlers("Initializing input monitor due to: " + reason); 662 663 if (mDeviceState.isButtonNavMode() 664 && !mDeviceState.supportsAssistantGestureInButtonNav() 665 && (mTrackpadsConnected.isEmpty())) { 666 return; 667 } 668 if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) { 669 mInputMonitorDisplayModel = new InputMonitorDisplayModel(this); 670 } else { 671 mInputMonitorCompat = new InputMonitorCompat("swipe-up", DEFAULT_DISPLAY); 672 mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), 673 mMainChoreographer, this::onInputEvent); 674 } 675 676 mRotationTouchHelper.updateGestureTouchRegions(); 677 } 678 679 /** 680 * Called when the navigation mode changes, guaranteed to be after the device state has updated. 681 */ onNavigationModeChanged()682 private void onNavigationModeChanged() { 683 initInputMonitor("onNavigationModeChanged()"); 684 resetHomeBounceSeenOnQuickstepEnabledFirstTime(); 685 } 686 687 @UiThread onUserUnlocked()688 public void onUserUnlocked() { 689 Log.d(TAG, "onUserUnlocked: userId=" + getUserId() 690 + " instance=" + System.identityHashCode(this)); 691 mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this); 692 mOverviewCommandHelper = new OverviewCommandHelper(this, 693 mOverviewComponentObserver, mRecentsDisplayModel, 694 SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager); 695 mUserUnlocked = true; 696 mInputConsumer.registerInputConsumer(); 697 for (int displayId : mDeviceState.getDisplaysWithSysUIState()) { 698 onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags(displayId), displayId); 699 } 700 onAssistantVisibilityChanged(); 701 702 // Initialize the task tracker 703 TopTaskTracker.INSTANCE.get(this); 704 705 // Temporarily disable model preload 706 // new ModelPreload().start(this); 707 resetHomeBounceSeenOnQuickstepEnabledFirstTime(); 708 709 mOverviewComponentObserver.addOverviewChangeListener(mOverviewChangeListener); 710 onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame()); 711 712 mTaskbarManager.onUserUnlocked(); 713 } 714 getOverviewCommandHelper()715 public OverviewCommandHelper getOverviewCommandHelper() { 716 return mOverviewCommandHelper; 717 } 718 resetHomeBounceSeenOnQuickstepEnabledFirstTime()719 private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { 720 if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) { 721 // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation 722 // mode doesn't have gestures 723 return; 724 } 725 726 // Reset home bounce seen on quick step enabled for first time 727 LauncherPrefs prefs = LauncherPrefs.get(this); 728 if (!prefs.get(HAS_ENABLED_QUICKSTEP_ONCE)) { 729 prefs.put( 730 HAS_ENABLED_QUICKSTEP_ONCE.to(true), 731 HOME_BOUNCE_SEEN.to(false)); 732 } 733 } 734 onOverviewTargetChanged(boolean isHomeAndOverviewSame)735 private void onOverviewTargetChanged(boolean isHomeAndOverviewSame) { 736 mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame); 737 // TODO (b/399089118): how will this work with per-display Taskbars? Is using the 738 // default-display container ok? 739 RecentsViewContainer newOverviewContainer = 740 mOverviewComponentObserver.getContainerInterface( 741 DEFAULT_DISPLAY).getCreatedContainer(); 742 if (newOverviewContainer != null) { 743 if (newOverviewContainer instanceof StatefulActivity activity) { 744 // This will also call setRecentsViewContainer() internally. 745 mTaskbarManager.setActivity(activity); 746 } else { 747 mTaskbarManager.setRecentsViewContainer(newOverviewContainer); 748 } 749 } 750 } 751 createAllAppsPendingIntent()752 private PendingIntent createAllAppsPendingIntent() { 753 return new PendingIntent(new IIntentSender.Stub() { 754 @Override 755 public void send(int code, Intent intent, String resolvedType, 756 IBinder allowlistToken, IIntentReceiver finishedReceiver, 757 String requiredPermission, Bundle options) { 758 MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllAppsSearch()); 759 } 760 }); 761 } 762 763 @UiThread 764 private void onSystemUiFlagsChanged(@SystemUiStateFlags long lastSysUIFlags, int displayId) { 765 if (LockedUserState.get(this).isUserUnlocked()) { 766 long systemUiStateFlags = mDeviceState.getSystemUiStateFlags(displayId); 767 mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags, displayId); 768 if (displayId == DEFAULT_DISPLAY) { 769 // The following don't care about non-default displays, at least for now. If they 770 // ever will, they should be taken care of. 771 SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); 772 mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled()); 773 // TODO b/399371607 - Propagate to taskAnimationManager once overview is multi 774 // display. 775 TaskAnimationManager taskAnimationManager = 776 mRecentsDisplayModel.getTaskAnimationManager(displayId); 777 if (taskAnimationManager != null) { 778 taskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags); 779 } 780 } 781 } 782 } 783 784 @UiThread 785 private void onAssistantVisibilityChanged() { 786 if (LockedUserState.get(this).isUserUnlocked()) { 787 mOverviewComponentObserver.getContainerInterface( 788 DEFAULT_DISPLAY).onAssistantVisibilityChanged( 789 mDeviceState.getAssistantVisibility()); 790 } 791 } 792 793 @Override 794 public void onDestroy() { 795 Log.d(TAG, "onDestroy: user=" + getUserId() 796 + " instance=" + System.identityHashCode(this)); 797 if (LockedUserState.get(this).isUserUnlocked()) { 798 mInputConsumer.unregisterInputConsumer(); 799 mOverviewComponentObserver.setHomeDisabled(false); 800 mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener); 801 } 802 disposeEventHandlers("TouchInteractionService onDestroy()"); 803 SystemUiProxy.INSTANCE.get(this).clearProxy(); 804 805 mAllAppsActionManager.onDestroy(); 806 807 mTrackpadsConnected.destroy(); 808 mTaskbarManager.destroy(); 809 if (mDesktopAppLaunchTransitionManager != null) { 810 mDesktopAppLaunchTransitionManager.unregisterTransitions(); 811 } 812 mDesktopAppLaunchTransitionManager = null; 813 mDeviceState.removeDisplayInfoChangeListener(mDisplayInfoChangeListener); 814 LockedUserState.get(this).removeOnUserUnlockedRunnable(mUserUnlockedRunnable); 815 ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener); 816 super.onDestroy(); 817 } 818 819 @Override 820 public IBinder onBind(Intent intent) { 821 Log.d(TAG, "onBind: user=" + getUserId() 822 + " instance=" + System.identityHashCode(this)); 823 return mTISBinder; 824 } 825 826 protected void onScreenOnChanged(boolean isOn) { 827 if (isOn) { 828 return; 829 } 830 long currentTime = SystemClock.uptimeMillis(); 831 MotionEvent cancelEvent = MotionEvent.obtain( 832 currentTime, currentTime, ACTION_CANCEL, 0f, 0f, 0); 833 onInputEvent(cancelEvent); 834 cancelEvent.recycle(); 835 } 836 837 private void onInputEvent(InputEvent ev) { 838 int displayId = ev.getDisplayId(); 839 if (!(ev instanceof MotionEvent)) { 840 ActiveGestureProtoLogProxy.logUnknownInputEvent(displayId, ev.toString()); 841 return; 842 } 843 MotionEvent event = (MotionEvent) ev; 844 845 TestLogging.recordMotionEvent( 846 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event); 847 848 if (!LockedUserState.get(this).isUserUnlocked()) { 849 ActiveGestureProtoLogProxy.logOnInputEventUserLocked(displayId); 850 return; 851 } 852 853 NavigationMode currentNavMode = mDeviceState.getMode(); 854 if (mGestureStartNavMode != null && mGestureStartNavMode != currentNavMode) { 855 ActiveGestureProtoLogProxy.logOnInputEventNavModeSwitched( 856 displayId, mGestureStartNavMode.name(), currentNavMode.name()); 857 event.setAction(ACTION_CANCEL); 858 } else if (mDeviceState.isButtonNavMode() 859 && !mDeviceState.supportsAssistantGestureInButtonNav() 860 && !isTrackpadMotionEvent(event)) { 861 ActiveGestureProtoLogProxy.logOnInputEventThreeButtonNav(displayId); 862 return; 863 } 864 865 final int action = event.getActionMasked(); 866 // Note this will create a new consumer every mouse click, as after ACTION_UP from the click 867 // an ACTION_HOVER_ENTER will fire as well. 868 boolean isHoverActionWithoutConsumer = enableCursorHoverStates() 869 && isHoverActionWithoutConsumer(event); 870 871 TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager( 872 displayId); 873 if (taskAnimationManager == null) { 874 Log.e(TAG, "TaskAnimationManager not available for displayId " + displayId); 875 ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(displayId); 876 return; 877 } 878 if (enableHandleDelayedGestureCallbacks()) { 879 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { 880 taskAnimationManager.notifyNewGestureStart(); 881 } 882 if (taskAnimationManager.shouldIgnoreMotionEvents()) { 883 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { 884 ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents(displayId); 885 } 886 return; 887 } 888 } 889 890 InputMonitorCompat inputMonitorCompat = getInputMonitorCompat(displayId); 891 InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId); 892 893 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { 894 mGestureStartNavMode = currentNavMode; 895 } else if (action == ACTION_UP || action == ACTION_CANCEL) { 896 mGestureStartNavMode = null; 897 } 898 899 SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent"); 900 901 CompoundString reasonString = action == ACTION_DOWN 902 ? CompoundString.newEmptyString() : CompoundString.NO_OP; 903 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { 904 mRotationTouchHelper.setOrientationTransformIfNeeded(event); 905 906 boolean isOneHandedModeActive = mDeviceState.isOneHandedModeActive(); 907 boolean isInSwipeUpTouchRegion = mRotationTouchHelper.isInSwipeUpTouchRegion(event); 908 TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); 909 BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null; 910 boolean isOnBubbles = bubbleControllers != null 911 && BubbleBarInputConsumer.isEventOnBubbles(tac, event); 912 if (mDeviceState.isButtonNavMode() 913 && mDeviceState.supportsAssistantGestureInButtonNav()) { 914 reasonString.append("in three button mode which supports Assistant gesture"); 915 // Consume gesture event for Assistant (all other gestures should do nothing). 916 if (mDeviceState.canTriggerAssistantAction(event)) { 917 reasonString.append(" and event can trigger assistant action, " 918 + "consuming gesture for assistant action"); 919 mGestureState = createGestureState( 920 displayId, mGestureState, getTrackpadGestureType(event)); 921 mUncheckedConsumer = tryCreateAssistantInputConsumer( 922 this, 923 mDeviceState, 924 inputMonitorCompat, 925 mGestureState, 926 event); 927 } else { 928 reasonString.append(" but event cannot trigger Assistant, " 929 + "consuming gesture as no-op"); 930 mUncheckedConsumer = createNoOpInputConsumer(displayId); 931 } 932 } else if ((!isOneHandedModeActive && isInSwipeUpTouchRegion) 933 || isHoverActionWithoutConsumer || isOnBubbles) { 934 reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion 935 ? "one handed mode is not active and event is in swipe up region, " 936 + "creating new input consumer" 937 : "isHoverActionWithoutConsumer == true, creating new input consumer"); 938 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger 939 // onConsumerInactive and wipe the previous gesture state 940 GestureState prevGestureState = new GestureState(mGestureState); 941 GestureState newGestureState = createGestureState( 942 displayId, mGestureState, getTrackpadGestureType(event)); 943 mConsumer.onConsumerAboutToBeSwitched(); 944 mGestureState = newGestureState; 945 mConsumer = newConsumer( 946 this, 947 mUserUnlocked, 948 mOverviewComponentObserver, 949 mDeviceState, 950 prevGestureState, 951 mGestureState, 952 taskAnimationManager, 953 inputMonitorCompat, 954 getSwipeUpHandlerFactory(displayId), 955 this::onConsumerInactive, 956 inputEventReceiver, 957 mTaskbarManager, 958 mSwipeUpProxyProvider, 959 mOverviewCommandHelper, 960 event); 961 mUncheckedConsumer = mConsumer; 962 } else if ((mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event)) 963 && mDeviceState.canTriggerAssistantAction(event)) { 964 reasonString.append(mDeviceState.isFullyGesturalNavMode() 965 ? "using fully gestural nav and event can trigger assistant action, " 966 + "consuming gesture for assistant action" 967 : "event is a trackpad multi-finger swipe and event can trigger assistant " 968 + "action, consuming gesture for assistant action"); 969 mGestureState = createGestureState( 970 displayId, mGestureState, getTrackpadGestureType(event)); 971 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we 972 // should not interrupt it. QuickSwitch assumes that interruption can only 973 // happen if the next gesture is also quick switch. 974 mUncheckedConsumer = tryCreateAssistantInputConsumer( 975 this, mDeviceState, inputMonitorCompat, mGestureState, event); 976 } else if (mDeviceState.canTriggerOneHandedAction(event)) { 977 reasonString.append("event can trigger one-handed action, " 978 + "consuming gesture for one-handed action"); 979 // Consume gesture event for triggering one handed feature. 980 mUncheckedConsumer = new OneHandedModeInputConsumer( 981 this, 982 displayId, 983 mDeviceState, 984 InputConsumer.createNoOpInputConsumer(displayId), inputMonitorCompat); 985 } else { 986 mUncheckedConsumer = InputConsumer.createNoOpInputConsumer(displayId); 987 } 988 } else { 989 // Other events 990 if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) { 991 // Only transform the event if we are handling it in a proper consumer 992 mRotationTouchHelper.setOrientationTransformIfNeeded(event); 993 } 994 } 995 996 if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) { 997 switch (action) { 998 case ACTION_DOWN: 999 ActiveGestureProtoLogProxy.logOnInputEventActionDown(displayId, reasonString); 1000 // fall through 1001 case ACTION_UP: 1002 ActiveGestureProtoLogProxy.logOnInputEventActionUp( 1003 (int) event.getRawX(), 1004 (int) event.getRawY(), 1005 action, 1006 MotionEvent.classificationToString(event.getClassification()), 1007 displayId); 1008 break; 1009 case ACTION_MOVE: 1010 ActiveGestureProtoLogProxy.logOnInputEventActionMove( 1011 MotionEvent.actionToString(action), 1012 MotionEvent.classificationToString(event.getClassification()), 1013 event.getPointerCount(), 1014 displayId); 1015 break; 1016 default: { 1017 ActiveGestureProtoLogProxy.logOnInputEventGenericAction( 1018 MotionEvent.actionToString(action), 1019 MotionEvent.classificationToString(event.getClassification()), 1020 displayId); 1021 } 1022 } 1023 } 1024 1025 boolean cancelGesture = mGestureState.getContainerInterface() != null 1026 && mGestureState.getContainerInterface().shouldCancelCurrentGesture(); 1027 boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture) 1028 && mConsumer != null 1029 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture(); 1030 if (cancelGesture) { 1031 event.setAction(ACTION_CANCEL); 1032 } 1033 1034 if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN 1035 || action == ACTION_POINTER_UP)) { 1036 // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad. 1037 } else if (isCursorHoverEvent(event)) { 1038 mUncheckedConsumer.onHoverEvent(event); 1039 } else { 1040 mUncheckedConsumer.onMotionEvent(event); 1041 } 1042 1043 if (cleanUpConsumer) { 1044 reset(displayId); 1045 } 1046 traceToken.close(); 1047 } 1048 1049 private boolean isHoverActionWithoutConsumer(MotionEvent event) { 1050 // Only process these events when taskbar is present. 1051 int displayId = event.getDisplayId(); 1052 TaskbarActivityContext tac = mTaskbarManager.getTaskbarForDisplay(displayId); 1053 boolean isTaskbarPresent = tac != null && tac.getDeviceProfile().isTaskbarPresent 1054 && !tac.isPhoneMode(); 1055 return event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0 1056 && isTaskbarPresent; 1057 } 1058 1059 // Talkback generates hover events on touch, which we do not want to consume. 1060 private boolean isCursorHoverEvent(MotionEvent event) { 1061 return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE; 1062 } 1063 1064 public GestureState createGestureState( 1065 int displayId, 1066 GestureState previousGestureState, 1067 GestureState.TrackpadGestureType trackpadGestureType) { 1068 final GestureState gestureState; 1069 TopTaskTracker.CachedTaskInfo taskInfo; 1070 TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager( 1071 displayId); 1072 if (taskAnimationManager != null && taskAnimationManager.isRecentsAnimationRunning()) { 1073 gestureState = new GestureState( 1074 mOverviewComponentObserver, displayId, ActiveGestureLog.INSTANCE.getLogId()); 1075 TopTaskTracker.CachedTaskInfo previousTaskInfo = previousGestureState.getRunningTask(); 1076 // previousTaskInfo can be null iff previousGestureState == GestureState.DEFAULT_STATE 1077 taskInfo = previousTaskInfo != null 1078 ? previousTaskInfo 1079 : TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false, displayId); 1080 gestureState.updateRunningTask(taskInfo); 1081 gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds()); 1082 gestureState.updatePreviouslyAppearedTaskIds( 1083 previousGestureState.getPreviouslyAppearedTaskIds()); 1084 } else { 1085 gestureState = new GestureState( 1086 mOverviewComponentObserver, 1087 displayId, 1088 ActiveGestureLog.INSTANCE.incrementLogId()); 1089 taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false, displayId); 1090 gestureState.updateRunningTask(taskInfo); 1091 } 1092 gestureState.setTrackpadGestureType(trackpadGestureType); 1093 1094 // Log initial state for the gesture. 1095 ActiveGestureProtoLogProxy.logRunningTaskPackage(taskInfo.getPackageName()); 1096 ActiveGestureProtoLogProxy.logSysuiStateFlags(mDeviceState.getSystemUiStateString()); 1097 return gestureState; 1098 } 1099 1100 /** 1101 * Returns a AbsSwipeUpHandler.Factory, used to instantiate AbsSwipeUpHandler later. 1102 * @param displayId The displayId of the display this handler will be used on. 1103 */ 1104 public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory(int displayId) { 1105 BaseContainerInterface<?, ?> containerInterface = 1106 mOverviewComponentObserver.getContainerInterface(displayId); 1107 if (containerInterface instanceof FallbackWindowInterface) { 1108 return mRecentsWindowSwipeHandlerFactory; 1109 } else if (containerInterface instanceof LauncherActivityInterface) { 1110 return mLauncherSwipeHandlerFactory; 1111 } else { 1112 return mFallbackSwipeHandlerFactory; 1113 } 1114 } 1115 1116 /** 1117 * To be called by the consumer when it's no longer active. This can be called by any consumer 1118 * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts 1119 * intercepting touches, the base consumer can try to call this). 1120 */ 1121 private void onConsumerInactive(InputConsumer caller) { 1122 if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) { 1123 reset(caller.getDisplayId()); 1124 } 1125 } 1126 1127 private void reset(int displayId) { 1128 mConsumer = mUncheckedConsumer = InputConsumerUtils.getDefaultInputConsumer( 1129 displayId, 1130 mUserUnlocked, 1131 mRecentsDisplayModel.getTaskAnimationManager(displayId), 1132 mTaskbarManager, 1133 CompoundString.NO_OP); 1134 mGestureState = DEFAULT_STATE; 1135 // By default, use batching of the input events, but check receiver before using in the rare 1136 // case that the monitor was disposed before the swipe settled 1137 InputEventReceiver inputEventReceiver = getInputEventReceiver(displayId); 1138 if (inputEventReceiver != null) { 1139 inputEventReceiver.setBatchingEnabled(true); 1140 } 1141 } 1142 1143 @Override 1144 public void onConfigurationChanged(Configuration newConfig) { 1145 if (!LockedUserState.get(this).isUserUnlocked()) { 1146 return; 1147 } 1148 // TODO (b/399094853): handle config updates for all connected displays (relevant only for 1149 // gestures on external displays) 1150 final BaseContainerInterface containerInterface = 1151 mOverviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY); 1152 final RecentsViewContainer container = containerInterface.getCreatedContainer(); 1153 if (container == null || container.isStarted()) { 1154 // We only care about the existing background activity. 1155 return; 1156 } 1157 Configuration oldConfig = container.asContext().getResources().getConfiguration(); 1158 boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig); 1159 if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges( 1160 container.getComponentName(), 1161 container.asContext().getResources().getConfiguration().diff(newConfig))) { 1162 // Since navBar gestural height are different between portrait and landscape, 1163 // can handle orientation changes and refresh navigation gestural region through 1164 // onOneHandedModeChanged() 1165 int newGesturalHeight = ResourceUtils.getNavbarSize( 1166 ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, 1167 getApplicationContext().getResources()); 1168 mDeviceState.onOneHandedModeChanged(newGesturalHeight); 1169 return; 1170 } 1171 1172 ActivityPreloadUtil.preloadOverviewForTIS(this, false /* fromInit */); 1173 } 1174 1175 private static boolean isTablet(Configuration config) { 1176 return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH; 1177 } 1178 1179 @Override 1180 protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { 1181 // Dump everything 1182 if (LockedUserState.get(this).isUserUnlocked()) { 1183 PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); 1184 } 1185 mDeviceState.dump(pw); 1186 if (mOverviewComponentObserver != null) { 1187 mOverviewComponentObserver.dump(pw); 1188 } 1189 if (mOverviewCommandHelper != null) { 1190 mOverviewCommandHelper.dump(pw); 1191 } 1192 if (mGestureState != null) { 1193 mGestureState.dump("", pw); 1194 } 1195 pw.println("Input state:"); 1196 pw.println("\tmInputMonitorCompat=" + mInputMonitorCompat); 1197 pw.println("\tmInputEventReceiver=" + mInputEventReceiver); 1198 if (mInputMonitorDisplayModel == null) { 1199 pw.println("\tmInputMonitorDisplayModel=null"); 1200 } else { 1201 mInputMonitorDisplayModel.dump("\t", pw); 1202 } 1203 DisplayController.INSTANCE.get(this).dump(pw); 1204 for (RecentsDisplayResource resource : mRecentsDisplayModel.getActiveDisplayResources()) { 1205 int displayId = resource.getDisplayId(); 1206 pw.println(String.format(Locale.ENGLISH, "TouchState (displayId %d):", displayId)); 1207 RecentsViewContainer createdOverviewContainer = 1208 mOverviewComponentObserver == null ? null 1209 : mOverviewComponentObserver.getContainerInterface( 1210 displayId).getCreatedContainer(); 1211 boolean resumed = mOverviewComponentObserver != null 1212 && mOverviewComponentObserver.getContainerInterface(displayId).isResumed(); 1213 pw.println("\tcreatedOverviewActivity=" + createdOverviewContainer); 1214 pw.println("\tresumed=" + resumed); 1215 if (createdOverviewContainer != null) { 1216 createdOverviewContainer.getDeviceProfile().dump(this, "", pw); 1217 } 1218 resource.getTaskAnimationManager().dump("\t", pw); 1219 } 1220 pw.println("\tmConsumer=" + mConsumer.getName()); 1221 ActiveGestureLog.INSTANCE.dump("", pw); 1222 RecentsModel.INSTANCE.get(this).dump("", pw); 1223 mTaskbarManager.dumpLogs("", pw); 1224 DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw); 1225 pw.println("ContextualSearchStateManager:"); 1226 ContextualSearchStateManager.INSTANCE.get(this).dump("\t", pw); 1227 SystemUiProxy.INSTANCE.get(this).dump(pw); 1228 DeviceConfigWrapper.get().dump(" ", pw); 1229 TopTaskTracker.INSTANCE.get(this).dump(pw); 1230 } 1231 1232 private AbsSwipeUpHandler createLauncherSwipeHandler( 1233 GestureState gestureState, long touchTimeMs) { 1234 TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager( 1235 gestureState.getDisplayId()); 1236 return new LauncherSwipeHandlerV2(this, taskAnimationManager, 1237 gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(), 1238 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this)); 1239 } 1240 1241 private AbsSwipeUpHandler createFallbackSwipeHandler( 1242 GestureState gestureState, long touchTimeMs) { 1243 TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager( 1244 gestureState.getDisplayId()); 1245 return new FallbackSwipeHandler(this, taskAnimationManager, 1246 gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(), 1247 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this)); 1248 } 1249 1250 private AbsSwipeUpHandler createRecentsWindowSwipeHandler( 1251 GestureState gestureState, long touchTimeMs) { 1252 TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager( 1253 gestureState.getDisplayId()); 1254 return new RecentsWindowSwipeHandler(this, taskAnimationManager, 1255 gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(), 1256 mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this)); 1257 } 1258 1259 /** 1260 * Helper class that keeps track of external displays and prepares input monitors for each. 1261 */ 1262 private class InputMonitorDisplayModel extends DisplayModel<InputMonitorResource> { 1263 1264 private InputMonitorDisplayModel(Context context) { 1265 super(context); 1266 initializeDisplays(); 1267 } 1268 1269 @NonNull 1270 @Override 1271 public InputMonitorResource createDisplayResource(@NonNull Display display) { 1272 return new InputMonitorResource(display.getDisplayId()); 1273 } 1274 } 1275 1276 private class InputMonitorResource extends DisplayModel.DisplayResource { 1277 1278 private final int displayId; 1279 1280 private final InputMonitorCompat inputMonitorCompat; 1281 private final InputEventReceiver inputEventReceiver; 1282 1283 private InputMonitorResource(int displayId) { 1284 this.displayId = displayId; 1285 inputMonitorCompat = new InputMonitorCompat("swipe-up", displayId); 1286 inputEventReceiver = inputMonitorCompat.getInputReceiver( 1287 Looper.getMainLooper(), 1288 TouchInteractionService.this.mMainChoreographer, 1289 TouchInteractionService.this::onInputEvent); 1290 } 1291 1292 @Override 1293 public void cleanup() { 1294 inputEventReceiver.dispose(); 1295 inputMonitorCompat.dispose(); 1296 } 1297 1298 @Override 1299 public void dump(String prefix , PrintWriter writer) { 1300 writer.println(prefix + "InputMonitorResource:"); 1301 1302 writer.println(prefix + "\tdisplayId=" + displayId); 1303 writer.println(prefix + "\tinputMonitorCompat=" + inputMonitorCompat); 1304 writer.println(prefix + "\tinputEventReceiver=" + inputEventReceiver); 1305 } 1306 } 1307 } 1308