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.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS; 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.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE; 27 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; 28 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe; 29 import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE; 30 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 31 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH; 32 import static com.android.quickstep.GestureState.DEFAULT_STATE; 33 import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType; 34 import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER; 35 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER; 36 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN; 37 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE; 38 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP; 39 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; 40 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; 41 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; 42 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; 43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; 44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 45 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; 46 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES; 47 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE; 48 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP; 49 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED; 50 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP; 51 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; 52 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; 53 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN; 54 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; 55 56 import android.annotation.TargetApi; 57 import android.app.PendingIntent; 58 import android.app.RemoteAction; 59 import android.app.Service; 60 import android.content.IIntentReceiver; 61 import android.content.IIntentSender; 62 import android.content.Intent; 63 import android.content.SharedPreferences; 64 import android.content.res.Configuration; 65 import android.graphics.Region; 66 import android.graphics.drawable.Icon; 67 import android.os.Build; 68 import android.os.Bundle; 69 import android.os.IBinder; 70 import android.os.Looper; 71 import android.os.SystemClock; 72 import android.os.Trace; 73 import android.util.Log; 74 import android.view.Choreographer; 75 import android.view.InputDevice; 76 import android.view.InputEvent; 77 import android.view.MotionEvent; 78 import android.view.SurfaceControl; 79 import android.view.accessibility.AccessibilityManager; 80 81 import androidx.annotation.BinderThread; 82 import androidx.annotation.NonNull; 83 import androidx.annotation.Nullable; 84 import androidx.annotation.UiThread; 85 86 import com.android.launcher3.BaseDraggingActivity; 87 import com.android.launcher3.DeviceProfile; 88 import com.android.launcher3.LauncherPrefs; 89 import com.android.launcher3.R; 90 import com.android.launcher3.anim.AnimatedFloat; 91 import com.android.launcher3.config.FeatureFlags; 92 import com.android.launcher3.provider.RestoreDbTask; 93 import com.android.launcher3.statemanager.StatefulActivity; 94 import com.android.launcher3.taskbar.TaskbarActivityContext; 95 import com.android.launcher3.taskbar.TaskbarManager; 96 import com.android.launcher3.testing.TestLogging; 97 import com.android.launcher3.testing.shared.ResourceUtils; 98 import com.android.launcher3.testing.shared.TestProtocol; 99 import com.android.launcher3.uioverrides.flags.FlagsFactory; 100 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; 101 import com.android.launcher3.util.DisplayController; 102 import com.android.launcher3.util.LockedUserState; 103 import com.android.launcher3.util.OnboardingPrefs; 104 import com.android.launcher3.util.SafeCloseable; 105 import com.android.launcher3.util.TraceHelper; 106 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; 107 import com.android.quickstep.inputconsumers.AssistantInputConsumer; 108 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; 109 import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer; 110 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer; 111 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; 112 import com.android.quickstep.inputconsumers.OverviewInputConsumer; 113 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer; 114 import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer; 115 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; 116 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; 117 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; 118 import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer; 119 import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer; 120 import com.android.quickstep.util.ActiveGestureLog; 121 import com.android.quickstep.util.ActiveGestureLog.CompoundString; 122 import com.android.quickstep.util.AssistStateManager; 123 import com.android.quickstep.util.AssistUtils; 124 import com.android.systemui.shared.recents.IOverviewProxy; 125 import com.android.systemui.shared.recents.ISystemUiProxy; 126 import com.android.systemui.shared.system.ActivityManagerWrapper; 127 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; 128 import com.android.systemui.shared.system.InputConsumerController; 129 import com.android.systemui.shared.system.InputMonitorCompat; 130 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; 131 import com.android.systemui.unfold.progress.IUnfoldAnimation; 132 import com.android.wm.shell.back.IBackAnimation; 133 import com.android.wm.shell.bubbles.IBubbles; 134 import com.android.wm.shell.desktopmode.IDesktopMode; 135 import com.android.wm.shell.draganddrop.IDragAndDrop; 136 import com.android.wm.shell.onehanded.IOneHanded; 137 import com.android.wm.shell.pip.IPip; 138 import com.android.wm.shell.recents.IRecentTasks; 139 import com.android.wm.shell.splitscreen.ISplitScreen; 140 import com.android.wm.shell.startingsurface.IStartingWindow; 141 import com.android.wm.shell.transition.IShellTransitions; 142 143 import java.io.FileDescriptor; 144 import java.io.PrintWriter; 145 import java.lang.ref.WeakReference; 146 import java.util.function.Consumer; 147 import java.util.function.Function; 148 149 /** 150 * Service connected by system-UI for handling touch interaction. 151 */ 152 @TargetApi(Build.VERSION_CODES.R) 153 public class TouchInteractionService extends Service { 154 155 private static final String SUBSTRING_PREFIX = "; "; 156 private static final String NEWLINE_PREFIX = "\n\t\t\t-> "; 157 158 private static final String TAG = "TouchInteractionService"; 159 160 private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once"; 161 162 private final TISBinder mTISBinder = new TISBinder(this); 163 164 /** 165 * Local IOverviewProxy implementation with some methods for local components 166 */ 167 public static class TISBinder extends IOverviewProxy.Stub { 168 169 private final WeakReference<TouchInteractionService> mTis; 170 171 @Nullable private Runnable mOnOverviewTargetChangeListener = null; 172 TISBinder(TouchInteractionService tis)173 private TISBinder(TouchInteractionService tis) { 174 mTis = new WeakReference<>(tis); 175 } 176 177 @BinderThread onInitialize(Bundle bundle)178 public void onInitialize(Bundle bundle) { 179 ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface( 180 bundle.getBinder(KEY_EXTRA_SYSUI_PROXY)); 181 IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP)); 182 IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_BUBBLES)); 183 ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder( 184 KEY_EXTRA_SHELL_SPLIT_SCREEN)); 185 IOneHanded onehanded = IOneHanded.Stub.asInterface( 186 bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED)); 187 IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface( 188 bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS)); 189 IStartingWindow startingWindow = IStartingWindow.Stub.asInterface( 190 bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW)); 191 ISysuiUnlockAnimationController launcherUnlockAnimationController = 192 ISysuiUnlockAnimationController.Stub.asInterface( 193 bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER)); 194 IRecentTasks recentTasks = IRecentTasks.Stub.asInterface( 195 bundle.getBinder(KEY_EXTRA_SHELL_RECENT_TASKS)); 196 IBackAnimation backAnimation = IBackAnimation.Stub.asInterface( 197 bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION)); 198 IDesktopMode desktopMode = IDesktopMode.Stub.asInterface( 199 bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE)); 200 IUnfoldAnimation unfoldTransition = IUnfoldAnimation.Stub.asInterface( 201 bundle.getBinder(KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER)); 202 IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface( 203 bundle.getBinder(KEY_EXTRA_SHELL_DRAG_AND_DROP)); 204 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 205 SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip, 206 bubbles, splitscreen, onehanded, shellTransitions, startingWindow, 207 recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode, 208 unfoldTransition, dragAndDrop); 209 tis.initInputMonitor("TISBinder#onInitialize()"); 210 tis.preloadOverview(true /* fromInit */); 211 })); 212 sIsInitialized = true; 213 } 214 215 @BinderThread 216 @Override onTaskbarToggled()217 public void onTaskbarToggled() { 218 if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) return; 219 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 220 TaskbarActivityContext activityContext = 221 tis.mTaskbarManager.getCurrentActivityContext(); 222 223 if (activityContext != null) { 224 activityContext.toggleTaskbarStash(); 225 } 226 })); 227 } 228 229 @BinderThread onOverviewToggle()230 public void onOverviewToggle() { 231 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle"); 232 executeForTouchInteractionService(tis -> { 233 // If currently screen pinning, do not enter overview 234 if (tis.mDeviceState.isScreenPinningActive()) { 235 return; 236 } 237 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); 238 tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE); 239 }); 240 } 241 242 @BinderThread 243 @Override onOverviewShown(boolean triggeredFromAltTab)244 public void onOverviewShown(boolean triggeredFromAltTab) { 245 executeForTouchInteractionService(tis -> { 246 if (triggeredFromAltTab) { 247 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); 248 tis.mOverviewCommandHelper.addCommand( 249 OverviewCommandHelper.TYPE_KEYBOARD_INPUT); 250 } else { 251 tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW); 252 } 253 }); 254 } 255 256 @BinderThread 257 @Override onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)258 public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 259 executeForTouchInteractionService(tis -> { 260 if (triggeredFromAltTab && !triggeredFromHomeKey) { 261 // onOverviewShownFromAltTab hides the overview and ends at the target app 262 tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE); 263 } 264 }); 265 } 266 267 @BinderThread 268 @Override onAssistantAvailable(boolean available, boolean longPressHomeEnabled)269 public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) { 270 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 271 tis.mDeviceState.setAssistantAvailable(available); 272 tis.onAssistantVisibilityChanged(); 273 executeForTaskbarManager(taskbarManager -> taskbarManager 274 .onLongPressHomeEnabled(longPressHomeEnabled)); 275 })); 276 } 277 278 @BinderThread 279 @Override onAssistantVisibilityChanged(float visibility)280 public void onAssistantVisibilityChanged(float visibility) { 281 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 282 tis.mDeviceState.setAssistantVisibility(visibility); 283 tis.onAssistantVisibilityChanged(); 284 })); 285 } 286 287 /** 288 * Sent when the assistant has been invoked with the given type (defined in AssistManager) 289 * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested 290 * was previously called including this invocation type. 291 */ 292 @Override onAssistantOverrideInvoked(int invocationType)293 public void onAssistantOverrideInvoked(int invocationType) { 294 executeForTouchInteractionService(tis -> { 295 if (!AssistUtils.newInstance(tis).tryStartAssistOverride(invocationType)) { 296 Log.w(TAG, "Failed to invoke Assist override"); 297 } 298 }); 299 } 300 301 @Override onNavigationBarSurface(SurfaceControl surface)302 public void onNavigationBarSurface(SurfaceControl surface) { 303 // TODO: implement 304 } 305 306 @BinderThread onSystemUiStateChanged(int stateFlags)307 public void onSystemUiStateChanged(int stateFlags) { 308 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 309 int lastFlags = tis.mDeviceState.getSystemUiStateFlags(); 310 tis.mDeviceState.setSystemUiFlags(stateFlags); 311 tis.onSystemUiFlagsChanged(lastFlags); 312 })); 313 } 314 315 @BinderThread onActiveNavBarRegionChanges(Region region)316 public void onActiveNavBarRegionChanges(Region region) { 317 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService( 318 tis -> tis.mDeviceState.setDeferredGestureRegion(region))); 319 } 320 321 @BinderThread 322 @Override enterStageSplitFromRunningApp(boolean leftOrTop)323 public void enterStageSplitFromRunningApp(boolean leftOrTop) { 324 executeForTouchInteractionService(tis -> { 325 StatefulActivity activity = 326 tis.mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); 327 if (activity != null) { 328 activity.enterStageSplitFromRunningApp(leftOrTop); 329 } 330 }); 331 } 332 333 /** 334 * Preloads the Overview activity. 335 * <p> 336 * This method should only be used when the All Set page of the SUW is reached to safely 337 * preload the Launcher for the SUW first reveal. 338 */ preloadOverviewForSUWAllSet()339 public void preloadOverviewForSUWAllSet() { 340 executeForTouchInteractionService(tis -> tis.preloadOverview(false, true)); 341 } 342 343 @Override onRotationProposal(int rotation, boolean isValid)344 public void onRotationProposal(int rotation, boolean isValid) { 345 executeForTaskbarManager(taskbarManager -> 346 taskbarManager.onRotationProposal(rotation, isValid)); 347 } 348 349 @Override disable(int displayId, int state1, int state2, boolean animate)350 public void disable(int displayId, int state1, int state2, boolean animate) { 351 executeForTaskbarManager(taskbarManager -> 352 taskbarManager.disableNavBarElements(displayId, state1, state2, animate)); 353 } 354 355 @Override onSystemBarAttributesChanged(int displayId, int behavior)356 public void onSystemBarAttributesChanged(int displayId, int behavior) { 357 executeForTaskbarManager(taskbarManager -> 358 taskbarManager.onSystemBarAttributesChanged(displayId, behavior)); 359 } 360 361 @Override onNavButtonsDarkIntensityChanged(float darkIntensity)362 public void onNavButtonsDarkIntensityChanged(float darkIntensity) { 363 executeForTaskbarManager(taskbarManager -> 364 taskbarManager.onNavButtonsDarkIntensityChanged(darkIntensity)); 365 } 366 executeForTouchInteractionService( @onNull Consumer<TouchInteractionService> tisConsumer)367 private void executeForTouchInteractionService( 368 @NonNull Consumer<TouchInteractionService> tisConsumer) { 369 TouchInteractionService tis = mTis.get(); 370 if (tis == null) return; 371 tisConsumer.accept(tis); 372 } 373 executeForTaskbarManager( @onNull Consumer<TaskbarManager> taskbarManagerConsumer)374 private void executeForTaskbarManager( 375 @NonNull Consumer<TaskbarManager> taskbarManagerConsumer) { 376 MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { 377 TaskbarManager taskbarManager = tis.mTaskbarManager; 378 if (taskbarManager == null) return; 379 taskbarManagerConsumer.accept(taskbarManager); 380 })); 381 } 382 383 /** 384 * Returns the {@link TaskbarManager}. 385 * <p> 386 * Returns {@code null} if TouchInteractionService is not connected 387 */ 388 @Nullable getTaskbarManager()389 public TaskbarManager getTaskbarManager() { 390 TouchInteractionService tis = mTis.get(); 391 if (tis == null) return null; 392 return tis.mTaskbarManager; 393 } 394 395 /** 396 * Returns the {@link OverviewCommandHelper}. 397 * <p> 398 * Returns {@code null} if TouchInteractionService is not connected 399 */ 400 @Nullable getOverviewCommandHelper()401 public OverviewCommandHelper getOverviewCommandHelper() { 402 TouchInteractionService tis = mTis.get(); 403 if (tis == null) return null; 404 return tis.mOverviewCommandHelper; 405 } 406 407 /** 408 * Sets a proxy to bypass swipe up behavior 409 */ setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy)410 public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) { 411 TouchInteractionService tis = mTis.get(); 412 if (tis == null) return; 413 tis.mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null); 414 } 415 416 /** 417 * Sets the task id where gestures should be blocked 418 */ setGestureBlockedTaskId(int taskId)419 public void setGestureBlockedTaskId(int taskId) { 420 TouchInteractionService tis = mTis.get(); 421 if (tis == null) return; 422 tis.mDeviceState.setGestureBlockingTaskId(taskId); 423 } 424 425 /** Sets a listener to be run on Overview Target updates. */ setOverviewTargetChangeListener(@ullable Runnable listener)426 public void setOverviewTargetChangeListener(@Nullable Runnable listener) { 427 mOnOverviewTargetChangeListener = listener; 428 } 429 onOverviewTargetChange()430 protected void onOverviewTargetChange() { 431 if (mOnOverviewTargetChangeListener != null) { 432 mOnOverviewTargetChangeListener.run(); 433 mOnOverviewTargetChangeListener = null; 434 } 435 } 436 } 437 438 private static boolean sConnected = false; 439 private static boolean sIsInitialized = false; 440 private RotationTouchHelper mRotationTouchHelper; 441 isConnected()442 public static boolean isConnected() { 443 return sConnected; 444 } 445 isInitialized()446 public static boolean isInitialized() { 447 return sIsInitialized; 448 } 449 450 private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory = 451 this::createLauncherSwipeHandler; 452 private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory = 453 this::createFallbackSwipeHandler; 454 455 private ActivityManagerWrapper mAM; 456 private OverviewCommandHelper mOverviewCommandHelper; 457 private OverviewComponentObserver mOverviewComponentObserver; 458 private InputConsumerController mInputConsumer; 459 private RecentsAnimationDeviceState mDeviceState; 460 private TaskAnimationManager mTaskAnimationManager; 461 462 private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP; 463 private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP; 464 private Choreographer mMainChoreographer; 465 private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer; 466 private GestureState mGestureState = DEFAULT_STATE; 467 468 private InputMonitorCompat mInputMonitorCompat; 469 private InputEventReceiver mInputEventReceiver; 470 471 private TaskbarManager mTaskbarManager; 472 private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null; 473 474 @Override onCreate()475 public void onCreate() { 476 super.onCreate(); 477 // Initialize anything here that is needed in direct boot mode. 478 // Everything else should be initialized in onUserUnlocked() below. 479 mMainChoreographer = Choreographer.getInstance(); 480 mAM = ActivityManagerWrapper.getInstance(); 481 mDeviceState = new RecentsAnimationDeviceState(this, true); 482 mTaskbarManager = new TaskbarManager(this); 483 mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); 484 BootAwarePreloader.start(this); 485 486 // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized. 487 LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked); 488 LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked); 489 mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); 490 sConnected = true; 491 } 492 disposeEventHandlers(String reason)493 private void disposeEventHandlers(String reason) { 494 Log.d(TAG, "disposeEventHandlers: Reason: " + reason); 495 if (mInputEventReceiver != null) { 496 mInputEventReceiver.dispose(); 497 mInputEventReceiver = null; 498 } 499 if (mInputMonitorCompat != null) { 500 mInputMonitorCompat.dispose(); 501 mInputMonitorCompat = null; 502 } 503 } 504 initInputMonitor(String reason)505 private void initInputMonitor(String reason) { 506 disposeEventHandlers("Initializing input monitor due to: " + reason); 507 508 if (mDeviceState.isButtonNavMode() && !ENABLE_TRACKPAD_GESTURE.get()) { 509 return; 510 } 511 512 mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId()); 513 mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), 514 mMainChoreographer, this::onInputEvent); 515 516 mRotationTouchHelper.updateGestureTouchRegions(); 517 } 518 519 /** 520 * Called when the navigation mode changes, guaranteed to be after the device state has updated. 521 */ onNavigationModeChanged()522 private void onNavigationModeChanged() { 523 initInputMonitor("onNavigationModeChanged()"); 524 resetHomeBounceSeenOnQuickstepEnabledFirstTime(); 525 } 526 527 @UiThread onUserUnlocked()528 public void onUserUnlocked() { 529 mTaskAnimationManager = new TaskAnimationManager(this); 530 mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState); 531 mOverviewCommandHelper = new OverviewCommandHelper(this, 532 mOverviewComponentObserver, mTaskAnimationManager); 533 mResetGestureInputConsumer = new ResetGestureInputConsumer( 534 mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext); 535 mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); 536 mInputConsumer.registerInputConsumer(); 537 onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags()); 538 onAssistantVisibilityChanged(); 539 540 // Initialize the task tracker 541 TopTaskTracker.INSTANCE.get(this); 542 543 // Temporarily disable model preload 544 // new ModelPreload().start(this); 545 resetHomeBounceSeenOnQuickstepEnabledFirstTime(); 546 547 mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange); 548 onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame()); 549 } 550 getOverviewCommandHelper()551 public OverviewCommandHelper getOverviewCommandHelper() { 552 return mOverviewCommandHelper; 553 } 554 resetHomeBounceSeenOnQuickstepEnabledFirstTime()555 private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { 556 if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) { 557 // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation 558 // mode doesn't have gestures 559 return; 560 } 561 562 // Reset home bounce seen on quick step enabled for first time 563 SharedPreferences sharedPrefs = LauncherPrefs.getPrefs(this); 564 if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) { 565 sharedPrefs.edit() 566 .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true) 567 .putBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN, false) 568 .apply(); 569 } 570 } 571 onOverviewTargetChange(boolean isHomeAndOverviewSame)572 private void onOverviewTargetChange(boolean isHomeAndOverviewSame) { 573 AccessibilityManager am = getSystemService(AccessibilityManager.class); 574 575 if (isHomeAndOverviewSame) { 576 am.registerSystemAction(createAllAppsAction(), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); 577 } else { 578 am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); 579 } 580 581 StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface() 582 .getCreatedActivity(); 583 if (newOverviewActivity != null) { 584 mTaskbarManager.setActivity(newOverviewActivity); 585 } 586 mTISBinder.onOverviewTargetChange(); 587 } 588 createAllAppsAction()589 private RemoteAction createAllAppsAction() { 590 final Intent homeIntent = new Intent(mOverviewComponentObserver.getHomeIntent()) 591 .setAction(INTENT_ACTION_ALL_APPS_TOGGLE); 592 final PendingIntent actionPendingIntent; 593 594 if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { 595 actionPendingIntent = new PendingIntent(new IIntentSender.Stub() { 596 @Override 597 public void send(int code, Intent intent, String resolvedType, 598 IBinder allowlistToken, IIntentReceiver finishedReceiver, 599 String requiredPermission, Bundle options) { 600 MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps(homeIntent)); 601 } 602 }); 603 } else { 604 actionPendingIntent = PendingIntent.getActivity( 605 this, 606 GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, 607 homeIntent, 608 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 609 } 610 611 return new RemoteAction( 612 Icon.createWithResource(this, R.drawable.ic_apps), 613 getString(R.string.all_apps_label), 614 getString(R.string.all_apps_label), 615 actionPendingIntent); 616 } 617 618 @UiThread onSystemUiFlagsChanged(int lastSysUIFlags)619 private void onSystemUiFlagsChanged(int lastSysUIFlags) { 620 if (LockedUserState.get(this).isUserUnlocked()) { 621 int systemUiStateFlags = mDeviceState.getSystemUiStateFlags(); 622 SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); 623 mOverviewComponentObserver.onSystemUiStateChanged(); 624 mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags); 625 626 int isShadeExpandedFlag = 627 SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 628 boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0; 629 boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0; 630 if (wasExpanded != isExpanded && isExpanded) { 631 // End live tile when expanding the notification panel for the first time from 632 // overview. 633 mTaskAnimationManager.endLiveTile(); 634 } 635 } 636 } 637 638 @UiThread onAssistantVisibilityChanged()639 private void onAssistantVisibilityChanged() { 640 if (LockedUserState.get(this).isUserUnlocked()) { 641 mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged( 642 mDeviceState.getAssistantVisibility()); 643 } 644 } 645 646 @Override onDestroy()647 public void onDestroy() { 648 Log.d(TAG, "Touch service destroyed: user=" + getUserId()); 649 sIsInitialized = false; 650 if (LockedUserState.get(this).isUserUnlocked()) { 651 mInputConsumer.unregisterInputConsumer(); 652 mOverviewComponentObserver.onDestroy(); 653 } 654 disposeEventHandlers("TouchInteractionService onDestroy()"); 655 mDeviceState.destroy(); 656 SystemUiProxy.INSTANCE.get(this).clearProxy(); 657 658 getSystemService(AccessibilityManager.class) 659 .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); 660 661 mTaskbarManager.destroy(); 662 sConnected = false; 663 super.onDestroy(); 664 } 665 666 @Override onBind(Intent intent)667 public IBinder onBind(Intent intent) { 668 Log.d(TAG, "Touch service connected: user=" + getUserId()); 669 return mTISBinder; 670 } 671 onInputEvent(InputEvent ev)672 private void onInputEvent(InputEvent ev) { 673 if (!(ev instanceof MotionEvent)) { 674 Log.e(TAG, "Unknown event " + ev); 675 return; 676 } 677 MotionEvent event = (MotionEvent) ev; 678 679 TestLogging.recordMotionEvent( 680 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event); 681 682 if (!LockedUserState.get(this).isUserUnlocked() || (mDeviceState.isButtonNavMode() 683 && !isTrackpadMotionEvent(event))) { 684 return; 685 } 686 687 SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent"); 688 689 final int action = event.getActionMasked(); 690 // Note this will create a new consumer every mouse click, as after ACTION_UP from the click 691 // an ACTION_HOVER_ENTER will fire as well. 692 boolean isHoverActionWithoutConsumer = 693 event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0; 694 if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { 695 mRotationTouchHelper.setOrientationTransformIfNeeded(event); 696 697 if ((!mDeviceState.isOneHandedModeActive() 698 && mRotationTouchHelper.isInSwipeUpTouchRegion(event)) 699 || isHoverActionWithoutConsumer) { 700 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger 701 // onConsumerInactive and wipe the previous gesture state 702 GestureState prevGestureState = new GestureState(mGestureState); 703 GestureState newGestureState = createGestureState(mGestureState, 704 getTrackpadGestureType(event)); 705 newGestureState.setSwipeUpStartTimeMs(SystemClock.uptimeMillis()); 706 mConsumer.onConsumerAboutToBeSwitched(); 707 mGestureState = newGestureState; 708 mConsumer = newConsumer(prevGestureState, mGestureState, event); 709 mUncheckedConsumer = mConsumer; 710 } else if (LockedUserState.get(this).isUserUnlocked() 711 && (mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event)) 712 && mDeviceState.canTriggerAssistantAction(event)) { 713 mGestureState = createGestureState(mGestureState, 714 getTrackpadGestureType(event)); 715 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we 716 // should not interrupt it. QuickSwitch assumes that interruption can only 717 // happen if the next gesture is also quick switch. 718 mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event); 719 } else if (mDeviceState.canTriggerOneHandedAction(event)) { 720 // Consume gesture event for triggering one handed feature. 721 mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState, 722 InputConsumer.NO_OP, mInputMonitorCompat); 723 } else { 724 mUncheckedConsumer = InputConsumer.NO_OP; 725 } 726 } else { 727 // Other events 728 if (mUncheckedConsumer != InputConsumer.NO_OP) { 729 // Only transform the event if we are handling it in a proper consumer 730 mRotationTouchHelper.setOrientationTransformIfNeeded(event); 731 } 732 } 733 734 if (mUncheckedConsumer != InputConsumer.NO_OP) { 735 switch (event.getActionMasked()) { 736 case ACTION_DOWN: 737 // fall through 738 case ACTION_UP: 739 ActiveGestureLog.INSTANCE.addLog( 740 /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", " 741 + (int) event.getRawY() + "): " 742 + MotionEvent.actionToString(event.getActionMasked()) + ", " 743 + MotionEvent.classificationToString(event.getClassification()), 744 /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN 745 ? MOTION_DOWN 746 : MOTION_UP); 747 break; 748 case ACTION_MOVE: 749 ActiveGestureLog.INSTANCE.addLog("onMotionEvent: " 750 + MotionEvent.actionToString(event.getActionMasked()) + "," 751 + MotionEvent.classificationToString(event.getClassification()) 752 + ", pointerCount: " + event.getPointerCount(), MOTION_MOVE); 753 break; 754 default: { 755 ActiveGestureLog.INSTANCE.addLog("onMotionEvent: " 756 + MotionEvent.actionToString(event.getActionMasked()) + "," 757 + MotionEvent.classificationToString(event.getClassification())); 758 } 759 } 760 } 761 762 boolean cancelGesture = mGestureState.getActivityInterface() != null 763 && mGestureState.getActivityInterface().shouldCancelCurrentGesture(); 764 boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture) 765 && mConsumer != null 766 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture(); 767 if (cancelGesture) { 768 event.setAction(ACTION_CANCEL); 769 } 770 771 if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN 772 || action == ACTION_POINTER_UP)) { 773 // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad. 774 } else if (isCursorHoverEvent(event)) { 775 mUncheckedConsumer.onHoverEvent(event); 776 } else { 777 mUncheckedConsumer.onMotionEvent(event); 778 } 779 780 if (cleanUpConsumer) { 781 reset(); 782 } 783 traceToken.close(); 784 } 785 786 // Talkback generates hover events on touch, which we do not want to consume. isCursorHoverEvent(MotionEvent event)787 private boolean isCursorHoverEvent(MotionEvent event) { 788 return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE; 789 } 790 tryCreateAssistantInputConsumer( GestureState gestureState, MotionEvent motionEvent)791 private InputConsumer tryCreateAssistantInputConsumer( 792 GestureState gestureState, MotionEvent motionEvent) { 793 return tryCreateAssistantInputConsumer( 794 InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP); 795 } 796 tryCreateAssistantInputConsumer( InputConsumer base, GestureState gestureState, MotionEvent motionEvent, CompoundString reasonString)797 private InputConsumer tryCreateAssistantInputConsumer( 798 InputConsumer base, 799 GestureState gestureState, 800 MotionEvent motionEvent, 801 CompoundString reasonString) { 802 if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) { 803 reasonString.append(SUBSTRING_PREFIX) 804 .append("is gesture-blocked task, using base input consumer"); 805 return base; 806 } else { 807 reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer"); 808 return new AssistantInputConsumer( 809 this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent); 810 } 811 } 812 createGestureState(GestureState previousGestureState, GestureState.TrackpadGestureType trackpadGestureType)813 public GestureState createGestureState(GestureState previousGestureState, 814 GestureState.TrackpadGestureType trackpadGestureType) { 815 final GestureState gestureState; 816 TopTaskTracker.CachedTaskInfo taskInfo; 817 if (mTaskAnimationManager.isRecentsAnimationRunning()) { 818 gestureState = new GestureState(mOverviewComponentObserver, 819 ActiveGestureLog.INSTANCE.getLogId()); 820 taskInfo = previousGestureState.getRunningTask(); 821 gestureState.updateRunningTask(taskInfo); 822 gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds()); 823 gestureState.updatePreviouslyAppearedTaskIds( 824 previousGestureState.getPreviouslyAppearedTaskIds()); 825 } else { 826 gestureState = new GestureState(mOverviewComponentObserver, 827 ActiveGestureLog.INSTANCE.incrementLogId()); 828 taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false); 829 gestureState.updateRunningTask(taskInfo); 830 } 831 gestureState.setTrackpadGestureType(trackpadGestureType); 832 833 // Log initial state for the gesture. 834 ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=") 835 .append(taskInfo == null ? "no running task" : taskInfo.getPackageName())); 836 ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=") 837 .append(mDeviceState.getSystemUiStateString())); 838 return gestureState; 839 } 840 newConsumer( GestureState previousGestureState, GestureState newGestureState, MotionEvent event)841 private InputConsumer newConsumer( 842 GestureState previousGestureState, GestureState newGestureState, MotionEvent event) { 843 AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState); 844 if (progressProxy != null) { 845 InputConsumer consumer = new ProgressDelegateInputConsumer( 846 this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy); 847 848 logInputConsumerSelectionReason(consumer, newCompoundString( 849 "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer")); 850 851 return consumer; 852 } 853 854 boolean canStartSystemGesture = 855 mGestureState.isTrackpadGesture() ? mDeviceState.canStartTrackpadGesture() 856 : mDeviceState.canStartSystemGesture(); 857 858 if (!LockedUserState.get(this).isUserUnlocked()) { 859 CompoundString reasonString = newCompoundString("device locked"); 860 InputConsumer consumer; 861 if (canStartSystemGesture) { 862 // This handles apps launched in direct boot mode (e.g. dialer) as well as apps 863 // launched while device is locked even after exiting direct boot mode (e.g. camera). 864 consumer = createDeviceLockedInputConsumer( 865 newGestureState, reasonString.append(SUBSTRING_PREFIX) 866 .append("can start system gesture")); 867 } else { 868 consumer = getDefaultInputConsumer( 869 reasonString.append(SUBSTRING_PREFIX) 870 .append("cannot start system gesture")); 871 } 872 logInputConsumerSelectionReason(consumer, reasonString); 873 return consumer; 874 } 875 876 CompoundString reasonString; 877 InputConsumer base; 878 // When there is an existing recents animation running, bypass systemState check as this is 879 // a followup gesture and the first gesture started in a valid system state. 880 if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) { 881 reasonString = newCompoundString(canStartSystemGesture 882 ? "can start system gesture" : "recents animation was running") 883 .append(", trying to use base consumer"); 884 base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString); 885 } else { 886 reasonString = newCompoundString( 887 "cannot start system gesture and recents animation was not running") 888 .append(", trying to use default input consumer"); 889 base = getDefaultInputConsumer(reasonString); 890 } 891 if (mDeviceState.isGesturalNavMode() || newGestureState.isTrackpadGesture()) { 892 handleOrientationSetup(base); 893 } 894 if (mDeviceState.isFullyGesturalNavMode() || newGestureState.isTrackpadGesture()) { 895 String reasonPrefix = "device is in gesture navigation mode or 3-button mode with a" 896 + " trackpad gesture"; 897 if (mDeviceState.canTriggerAssistantAction(event)) { 898 reasonString.append(NEWLINE_PREFIX) 899 .append(reasonPrefix) 900 .append(SUBSTRING_PREFIX) 901 .append("gesture can trigger the assistant") 902 .append(", trying to use assistant input consumer"); 903 base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString); 904 } 905 906 // If Taskbar is present, we listen for swipe or cursor hover events to unstash it. 907 TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); 908 if (tac != null && !(base instanceof AssistantInputConsumer)) { 909 // Present always on large screen or on small screen w/ flag 910 DeviceProfile dp = tac.getDeviceProfile(); 911 boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp) 912 && !tac.isInStashedLauncherState(); 913 if (canStartSystemGesture && useTaskbarConsumer) { 914 reasonString.append(NEWLINE_PREFIX) 915 .append(reasonPrefix) 916 .append(SUBSTRING_PREFIX) 917 .append("TaskbarActivityContext != null, " 918 + "using TaskbarUnstashInputConsumer"); 919 base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac, 920 mOverviewCommandHelper); 921 } 922 } else if (canStartSystemGesture && FeatureFlags.ENABLE_LONG_PRESS_NAV_HANDLE.get() 923 && !previousGestureState.isRecentsAnimationRunning()) { 924 reasonString.append(NEWLINE_PREFIX) 925 .append(reasonPrefix) 926 .append(SUBSTRING_PREFIX) 927 .append("Long press nav handle enabled, " 928 + "using NavHandleLongPressInputConsumer"); 929 base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat); 930 } 931 932 if (mDeviceState.isBubblesExpanded()) { 933 reasonString = newCompoundString(reasonPrefix) 934 .append(SUBSTRING_PREFIX) 935 .append("bubbles expanded, trying to use default input consumer"); 936 // Bubbles can handle home gesture itself. 937 base = getDefaultInputConsumer(reasonString); 938 } 939 940 if (mDeviceState.isSystemUiDialogShowing()) { 941 reasonString = newCompoundString(reasonPrefix) 942 .append(SUBSTRING_PREFIX) 943 .append("system dialog is showing, using SysUiOverlayInputConsumer"); 944 base = new SysUiOverlayInputConsumer( 945 getBaseContext(), mDeviceState, mInputMonitorCompat); 946 } 947 948 if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture() 949 && canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) { 950 reasonString = newCompoundString(reasonPrefix) 951 .append(SUBSTRING_PREFIX) 952 .append("Trackpad 3-finger gesture, using TrackpadStatusBarInputConsumer"); 953 base = new TrackpadStatusBarInputConsumer(getBaseContext(), base, 954 mInputMonitorCompat); 955 } 956 957 if (mDeviceState.isScreenPinningActive()) { 958 reasonString = newCompoundString(reasonPrefix) 959 .append(SUBSTRING_PREFIX) 960 .append("screen pinning is active, using ScreenPinnedInputConsumer"); 961 // Note: we only allow accessibility to wrap this, and it replaces the previous 962 // base input consumer (which should be NO_OP anyway since topTaskLocked == true). 963 base = new ScreenPinnedInputConsumer(this, newGestureState); 964 } 965 966 if (mDeviceState.canTriggerOneHandedAction(event)) { 967 reasonString.append(NEWLINE_PREFIX) 968 .append(reasonPrefix) 969 .append(SUBSTRING_PREFIX) 970 .append("gesture can trigger one handed mode") 971 .append(", using OneHandedModeInputConsumer"); 972 base = new OneHandedModeInputConsumer( 973 this, mDeviceState, base, mInputMonitorCompat); 974 } 975 976 if (mDeviceState.isAccessibilityMenuAvailable()) { 977 reasonString.append(NEWLINE_PREFIX) 978 .append(reasonPrefix) 979 .append(SUBSTRING_PREFIX) 980 .append("accessibility menu is available") 981 .append(", using AccessibilityInputConsumer"); 982 base = new AccessibilityInputConsumer( 983 this, mDeviceState, mGestureState, base, mInputMonitorCompat); 984 } 985 } else { 986 String reasonPrefix = "device is not in gesture navigation mode"; 987 if (mDeviceState.isScreenPinningActive()) { 988 reasonString = newCompoundString(reasonPrefix) 989 .append(SUBSTRING_PREFIX) 990 .append("screen pinning is active, trying to use default input consumer"); 991 base = getDefaultInputConsumer(reasonString); 992 } 993 994 if (mDeviceState.canTriggerOneHandedAction(event)) { 995 reasonString.append(NEWLINE_PREFIX) 996 .append(reasonPrefix) 997 .append(SUBSTRING_PREFIX) 998 .append("gesture can trigger one handed mode") 999 .append(", using OneHandedModeInputConsumer"); 1000 base = new OneHandedModeInputConsumer( 1001 this, mDeviceState, base, mInputMonitorCompat); 1002 } 1003 } 1004 logInputConsumerSelectionReason(base, reasonString); 1005 return base; 1006 } 1007 newCompoundString(String substring)1008 private CompoundString newCompoundString(String substring) { 1009 return new CompoundString(NEWLINE_PREFIX).append(substring); 1010 } 1011 logInputConsumerSelectionReason( InputConsumer consumer, CompoundString reasonString)1012 private void logInputConsumerSelectionReason( 1013 InputConsumer consumer, CompoundString reasonString) { 1014 ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ") 1015 .append(consumer.getName()) 1016 .append(". reason(s):") 1017 .append(reasonString)); 1018 if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) { 1019 ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER); 1020 } 1021 } 1022 handleOrientationSetup(InputConsumer baseInputConsumer)1023 private void handleOrientationSetup(InputConsumer baseInputConsumer) { 1024 baseInputConsumer.notifyOrientationSetup(); 1025 } 1026 newBaseConsumer( GestureState previousGestureState, GestureState gestureState, MotionEvent event, CompoundString reasonString)1027 private InputConsumer newBaseConsumer( 1028 GestureState previousGestureState, 1029 GestureState gestureState, 1030 MotionEvent event, 1031 CompoundString reasonString) { 1032 if (mDeviceState.isKeyguardShowingOccluded()) { 1033 // This handles apps showing over the lockscreen (e.g. camera) 1034 return createDeviceLockedInputConsumer( 1035 gestureState, 1036 reasonString.append(SUBSTRING_PREFIX) 1037 .append("keyguard is showing occluded") 1038 .append(", trying to use device locked input consumer")); 1039 } 1040 1041 reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded"); 1042 1043 // Use overview input consumer for sharesheets on top of home. 1044 boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted() 1045 && gestureState.getRunningTask() != null 1046 && gestureState.getRunningTask().isRootChooseActivity(); 1047 1048 // In the case where we are in an excluded, translucent overlay, ignore it and treat the 1049 // running activity as the task behind the overlay. 1050 TopTaskTracker.CachedTaskInfo otherVisibleTask = gestureState.getRunningTask() == null 1051 ? null 1052 : gestureState.getRunningTask().otherVisibleTaskThisIsExcludedOver(); 1053 if (otherVisibleTask != null) { 1054 ActiveGestureLog.INSTANCE.addLog(new CompoundString("Changing active task to ") 1055 .append(otherVisibleTask.getPackageName()) 1056 .append(" because the previous task running on top of this one (") 1057 .append(gestureState.getRunningTask().getPackageName()) 1058 .append(") was excluded from recents")); 1059 gestureState.updateRunningTask(otherVisibleTask); 1060 } 1061 1062 boolean previousGestureAnimatedToLauncher = 1063 previousGestureState.isRunningAnimationToLauncher(); 1064 // with shell-transitions, home is resumed during recents animation, so 1065 // explicitly check against recents animation too. 1066 boolean launcherResumedThroughShellTransition = 1067 gestureState.getActivityInterface().isResumed() 1068 && !previousGestureState.isRecentsAnimationRunning(); 1069 if (gestureState.getActivityInterface().isInLiveTileMode()) { 1070 return createOverviewInputConsumer( 1071 previousGestureState, 1072 gestureState, 1073 event, 1074 forceOverviewInputConsumer, 1075 reasonString.append(SUBSTRING_PREFIX) 1076 .append("is in live tile mode, trying to use overview input consumer")); 1077 } else if (gestureState.getRunningTask() == null) { 1078 return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX) 1079 .append("running task == null")); 1080 } else if (previousGestureAnimatedToLauncher 1081 || launcherResumedThroughShellTransition 1082 || forceOverviewInputConsumer) { 1083 return createOverviewInputConsumer( 1084 previousGestureState, 1085 gestureState, 1086 event, 1087 forceOverviewInputConsumer, 1088 reasonString.append(SUBSTRING_PREFIX) 1089 .append(previousGestureAnimatedToLauncher 1090 ? "previous gesture animated to launcher" 1091 : (launcherResumedThroughShellTransition 1092 ? "launcher resumed through a shell transition" 1093 : "forceOverviewInputConsumer == true")) 1094 .append(", trying to use overview input consumer")); 1095 } else if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) { 1096 return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX) 1097 .append("is gesture-blocked task, trying to use default input consumer")); 1098 } else { 1099 reasonString.append(SUBSTRING_PREFIX) 1100 .append("using OtherActivityInputConsumer"); 1101 return createOtherActivityInputConsumer(gestureState, event); 1102 } 1103 } 1104 getSwipeUpHandlerFactory()1105 public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() { 1106 return !mOverviewComponentObserver.isHomeAndOverviewSame() 1107 ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory; 1108 } 1109 createOtherActivityInputConsumer(GestureState gestureState, MotionEvent event)1110 private InputConsumer createOtherActivityInputConsumer(GestureState gestureState, 1111 MotionEvent event) { 1112 1113 final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory(); 1114 final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame() 1115 || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event); 1116 final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event); 1117 return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager, 1118 gestureState, shouldDefer, this::onConsumerInactive, 1119 mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory); 1120 } 1121 createDeviceLockedInputConsumer( GestureState gestureState, CompoundString reasonString)1122 private InputConsumer createDeviceLockedInputConsumer( 1123 GestureState gestureState, CompoundString reasonString) { 1124 if ((mDeviceState.isFullyGesturalNavMode() || gestureState.isTrackpadGesture()) 1125 && gestureState.getRunningTask() != null) { 1126 reasonString.append(SUBSTRING_PREFIX) 1127 .append("device is in gesture nav mode or 3-button mode with a trackpad gesture" 1128 + "and running task != null") 1129 .append(", using DeviceLockedInputConsumer"); 1130 return new DeviceLockedInputConsumer( 1131 this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat); 1132 } else { 1133 return getDefaultInputConsumer(reasonString 1134 .append(SUBSTRING_PREFIX) 1135 .append((mDeviceState.isFullyGesturalNavMode() 1136 || gestureState.isTrackpadGesture()) 1137 ? "running task == null" 1138 : "device is not in gesture nav mode and it's not a trackpad gesture") 1139 .append(", trying to use default input consumer")); 1140 } 1141 } 1142 createOverviewInputConsumer( GestureState previousGestureState, GestureState gestureState, MotionEvent event, boolean forceOverviewInputConsumer, CompoundString reasonString)1143 public InputConsumer createOverviewInputConsumer( 1144 GestureState previousGestureState, 1145 GestureState gestureState, 1146 MotionEvent event, 1147 boolean forceOverviewInputConsumer, 1148 CompoundString reasonString) { 1149 StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity(); 1150 if (activity == null) { 1151 return getDefaultInputConsumer( 1152 reasonString.append(SUBSTRING_PREFIX) 1153 .append("activity == null, trying to use default input consumer")); 1154 } 1155 1156 boolean hasWindowFocus = activity.getRootView().hasWindowFocus(); 1157 boolean isPreviousGestureAnimatingToLauncher = 1158 previousGestureState.isRunningAnimationToLauncher(); 1159 boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode(); 1160 reasonString.append(SUBSTRING_PREFIX) 1161 .append(hasWindowFocus 1162 ? "activity has window focus" 1163 : (isPreviousGestureAnimatingToLauncher 1164 ? "previous gesture is still animating to launcher" 1165 : isInLiveTileMode 1166 ? "device is in live mode" 1167 : "all overview focus conditions failed")); 1168 if (hasWindowFocus 1169 || isPreviousGestureAnimatingToLauncher 1170 || isInLiveTileMode) { 1171 reasonString.append(SUBSTRING_PREFIX) 1172 .append("overview should have focus, using OverviewInputConsumer"); 1173 return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat, 1174 false /* startingInActivityBounds */); 1175 } else { 1176 reasonString.append(SUBSTRING_PREFIX).append( 1177 "overview shouldn't have focus, using OverviewWithoutFocusInputConsumer"); 1178 final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event); 1179 return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState, 1180 mInputMonitorCompat, disableHorizontalSwipe); 1181 } 1182 } 1183 1184 /** 1185 * To be called by the consumer when it's no longer active. This can be called by any consumer 1186 * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts 1187 * intercepting touches, the base consumer can try to call this). 1188 */ onConsumerInactive(InputConsumer caller)1189 private void onConsumerInactive(InputConsumer caller) { 1190 if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) { 1191 reset(); 1192 } 1193 } 1194 reset()1195 private void reset() { 1196 mConsumer = mUncheckedConsumer = getDefaultInputConsumer(); 1197 mGestureState = DEFAULT_STATE; 1198 // By default, use batching of the input events, but check receiver before using in the rare 1199 // case that the monitor was disposed before the swipe settled 1200 if (mInputEventReceiver != null) { 1201 mInputEventReceiver.setBatchingEnabled(true); 1202 } 1203 } 1204 getDefaultInputConsumer()1205 private @NonNull InputConsumer getDefaultInputConsumer() { 1206 return getDefaultInputConsumer(CompoundString.NO_OP); 1207 } 1208 1209 /** 1210 * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP. 1211 */ getDefaultInputConsumer(@onNull CompoundString reasonString)1212 private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) { 1213 if (mResetGestureInputConsumer != null) { 1214 reasonString.append(SUBSTRING_PREFIX).append( 1215 "mResetGestureInputConsumer initialized, using ResetGestureInputConsumer"); 1216 return mResetGestureInputConsumer; 1217 } else { 1218 reasonString.append(SUBSTRING_PREFIX).append( 1219 "mResetGestureInputConsumer not initialized, using no-op input consumer"); 1220 // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to 1221 // NO_OP until then (we never want these to be null). 1222 return InputConsumer.NO_OP; 1223 } 1224 } 1225 preloadOverview(boolean fromInit)1226 private void preloadOverview(boolean fromInit) { 1227 Trace.beginSection("preloadOverview(fromInit=" + fromInit + ")"); 1228 preloadOverview(fromInit, false); 1229 Trace.endSection(); 1230 } 1231 preloadOverview(boolean fromInit, boolean forSUWAllSet)1232 private void preloadOverview(boolean fromInit, boolean forSUWAllSet) { 1233 if (!LockedUserState.get(this).isUserUnlocked()) { 1234 return; 1235 } 1236 1237 if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) { 1238 // Prevent the overview from being started before the real home on first boot. 1239 return; 1240 } 1241 1242 if ((RestoreDbTask.isPending(this) && !forSUWAllSet) 1243 || !mDeviceState.isUserSetupComplete()) { 1244 // Preloading while a restore is pending may cause launcher to start the restore 1245 // too early. 1246 return; 1247 } 1248 1249 final BaseActivityInterface activityInterface = 1250 mOverviewComponentObserver.getActivityInterface(); 1251 final Intent overviewIntent = new Intent( 1252 mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState()); 1253 if (activityInterface.getCreatedActivity() != null && fromInit) { 1254 // The activity has been created before the initialization of overview service. It is 1255 // usually happens when booting or launcher is the top activity, so we should already 1256 // have the latest state. 1257 return; 1258 } 1259 1260 // TODO(b/258022658): Remove temporary logging. 1261 Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet 1262 + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame()); 1263 1264 mTaskAnimationManager.preloadRecentsAnimation(overviewIntent); 1265 } 1266 1267 @Override onConfigurationChanged(Configuration newConfig)1268 public void onConfigurationChanged(Configuration newConfig) { 1269 if (!LockedUserState.get(this).isUserUnlocked()) { 1270 return; 1271 } 1272 final BaseActivityInterface activityInterface = 1273 mOverviewComponentObserver.getActivityInterface(); 1274 final BaseDraggingActivity activity = activityInterface.getCreatedActivity(); 1275 if (activity == null || activity.isStarted()) { 1276 // We only care about the existing background activity. 1277 return; 1278 } 1279 Configuration oldConfig = activity.getResources().getConfiguration(); 1280 boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig); 1281 if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges( 1282 activity.getComponentName(), 1283 activity.getResources().getConfiguration().diff(newConfig))) { 1284 // Since navBar gestural height are different between portrait and landscape, 1285 // can handle orientation changes and refresh navigation gestural region through 1286 // onOneHandedModeChanged() 1287 int newGesturalHeight = ResourceUtils.getNavbarSize( 1288 ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, 1289 getApplicationContext().getResources()); 1290 mDeviceState.onOneHandedModeChanged(newGesturalHeight); 1291 return; 1292 } 1293 1294 preloadOverview(false /* fromInit */); 1295 } 1296 isTablet(Configuration config)1297 private static boolean isTablet(Configuration config) { 1298 return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH; 1299 } 1300 1301 @Override dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs)1302 protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { 1303 // Dump everything 1304 FlagsFactory.dump(pw); 1305 if (LockedUserState.get(this).isUserUnlocked()) { 1306 PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); 1307 } 1308 mDeviceState.dump(pw); 1309 if (mOverviewComponentObserver != null) { 1310 mOverviewComponentObserver.dump(pw); 1311 } 1312 if (mOverviewCommandHelper != null) { 1313 mOverviewCommandHelper.dump(pw); 1314 } 1315 if (mGestureState != null) { 1316 mGestureState.dump(pw); 1317 } 1318 pw.println("Input state:"); 1319 pw.println(" mInputMonitorCompat=" + mInputMonitorCompat); 1320 pw.println(" mInputEventReceiver=" + mInputEventReceiver); 1321 DisplayController.INSTANCE.get(this).dump(pw); 1322 pw.println("TouchState:"); 1323 BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null 1324 : mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); 1325 boolean resumed = mOverviewComponentObserver != null 1326 && mOverviewComponentObserver.getActivityInterface().isResumed(); 1327 pw.println(" createdOverviewActivity=" + createdOverviewActivity); 1328 pw.println(" resumed=" + resumed); 1329 pw.println(" mConsumer=" + mConsumer.getName()); 1330 ActiveGestureLog.INSTANCE.dump("", pw); 1331 RecentsModel.INSTANCE.get(this).dump("", pw); 1332 if (createdOverviewActivity != null) { 1333 createdOverviewActivity.getDeviceProfile().dump(this, "", pw); 1334 } 1335 mTaskbarManager.dumpLogs("", pw); 1336 pw.println("AssistStateManager:"); 1337 AssistStateManager.INSTANCE.get(this).dump(" ", pw); 1338 } 1339 createLauncherSwipeHandler( GestureState gestureState, long touchTimeMs)1340 private AbsSwipeUpHandler createLauncherSwipeHandler( 1341 GestureState gestureState, long touchTimeMs) { 1342 return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager, 1343 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(), 1344 mInputConsumer); 1345 } 1346 createFallbackSwipeHandler( GestureState gestureState, long touchTimeMs)1347 private AbsSwipeUpHandler createFallbackSwipeHandler( 1348 GestureState gestureState, long touchTimeMs) { 1349 return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager, 1350 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(), 1351 mInputConsumer); 1352 } 1353 } 1354