1 /* 2 * Copyright (C) 2017 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.content.Intent.ACTION_CHOOSER; 19 import static android.view.MotionEvent.ACTION_CANCEL; 20 import static android.view.MotionEvent.ACTION_DOWN; 21 import static android.view.MotionEvent.ACTION_UP; 22 23 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; 24 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 25 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 26 import static com.android.quickstep.GestureState.DEFAULT_STATE; 27 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; 28 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; 29 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; 30 31 import android.annotation.TargetApi; 32 import android.app.ActivityManager; 33 import android.app.PendingIntent; 34 import android.app.RemoteAction; 35 import android.app.Service; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.SharedPreferences; 40 import android.content.res.Configuration; 41 import android.graphics.Rect; 42 import android.graphics.Region; 43 import android.graphics.drawable.Icon; 44 import android.os.Build; 45 import android.os.Bundle; 46 import android.os.IBinder; 47 import android.os.Looper; 48 import android.util.Log; 49 import android.view.Choreographer; 50 import android.view.InputEvent; 51 import android.view.MotionEvent; 52 import android.view.accessibility.AccessibilityManager; 53 54 import androidx.annotation.BinderThread; 55 import androidx.annotation.Nullable; 56 import androidx.annotation.UiThread; 57 import androidx.annotation.WorkerThread; 58 59 import com.android.launcher3.BaseDraggingActivity; 60 import com.android.launcher3.R; 61 import com.android.launcher3.Utilities; 62 import com.android.launcher3.config.FeatureFlags; 63 import com.android.launcher3.logging.UserEventDispatcher; 64 import com.android.launcher3.model.AppLaunchTracker; 65 import com.android.launcher3.provider.RestoreDbTask; 66 import com.android.launcher3.statemanager.StatefulActivity; 67 import com.android.launcher3.testing.TestLogging; 68 import com.android.launcher3.testing.TestProtocol; 69 import com.android.launcher3.tracing.nano.LauncherTraceProto; 70 import com.android.launcher3.tracing.nano.TouchInteractionServiceProto; 71 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; 72 import com.android.launcher3.util.OnboardingPrefs; 73 import com.android.launcher3.util.TraceHelper; 74 import com.android.launcher3.util.WindowBounds; 75 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; 76 import com.android.quickstep.inputconsumers.AssistantInputConsumer; 77 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; 78 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; 79 import com.android.quickstep.inputconsumers.OverscrollInputConsumer; 80 import com.android.quickstep.inputconsumers.OverviewInputConsumer; 81 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer; 82 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; 83 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; 84 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; 85 import com.android.quickstep.util.ActiveGestureLog; 86 import com.android.quickstep.util.AssistantUtilities; 87 import com.android.quickstep.util.ProtoTracer; 88 import com.android.quickstep.util.SplitScreenBounds; 89 import com.android.systemui.plugins.OverscrollPlugin; 90 import com.android.systemui.plugins.PluginListener; 91 import com.android.systemui.shared.recents.IOverviewProxy; 92 import com.android.systemui.shared.recents.ISystemUiProxy; 93 import com.android.systemui.shared.system.ActivityManagerWrapper; 94 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; 95 import com.android.systemui.shared.system.InputConsumerController; 96 import com.android.systemui.shared.system.InputMonitorCompat; 97 import com.android.systemui.shared.tracing.ProtoTraceable; 98 99 import java.io.FileDescriptor; 100 import java.io.PrintWriter; 101 import java.util.Arrays; 102 import java.util.LinkedList; 103 import java.util.List; 104 105 /** 106 * Wrapper around a list for processing arguments. 107 */ 108 class ArgList extends LinkedList<String> { ArgList(List<String> l)109 public ArgList(List<String> l) { 110 super(l); 111 } 112 peekArg()113 public String peekArg() { 114 return peekFirst(); 115 } 116 nextArg()117 public String nextArg() { 118 return pollFirst().toLowerCase(); 119 } 120 } 121 122 /** 123 * Service connected by system-UI for handling touch interaction. 124 */ 125 @TargetApi(Build.VERSION_CODES.R) 126 public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>, 127 ProtoTraceable<LauncherTraceProto> { 128 129 private static final String TAG = "TouchInteractionService"; 130 131 private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount"; 132 private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE"; 133 private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once"; 134 private static final int MAX_BACK_NOTIFICATION_COUNT = 3; 135 136 /** 137 * System Action ID to show all apps. 138 * TODO: Use AccessibilityService's corresponding global action constant in S 139 */ 140 private static final int SYSTEM_ACTION_ID_ALL_APPS = 14; 141 142 private int mBackGestureNotificationCounter = -1; 143 @Nullable 144 private OverscrollPlugin mOverscrollPlugin; 145 146 private final IBinder mMyBinder = new IOverviewProxy.Stub() { 147 148 @BinderThread 149 public void onInitialize(Bundle bundle) { 150 ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface( 151 bundle.getBinder(KEY_EXTRA_SYSUI_PROXY)); 152 MAIN_EXECUTOR.execute(() -> { 153 SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy); 154 TouchInteractionService.this.initInputMonitor(); 155 preloadOverview(true /* fromInit */); 156 }); 157 sIsInitialized = true; 158 } 159 160 @BinderThread 161 @Override 162 public void onOverviewToggle() { 163 TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle"); 164 mOverviewCommandHelper.onOverviewToggle(); 165 } 166 167 @BinderThread 168 @Override 169 public void onOverviewShown(boolean triggeredFromAltTab) { 170 mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab); 171 } 172 173 @BinderThread 174 @Override 175 public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 176 if (triggeredFromAltTab && !triggeredFromHomeKey) { 177 // onOverviewShownFromAltTab hides the overview and ends at the target app 178 mOverviewCommandHelper.onOverviewHidden(); 179 } 180 } 181 182 @BinderThread 183 @Override 184 public void onTip(int actionType, int viewType) { 185 mOverviewCommandHelper.onTip(actionType, viewType); 186 } 187 188 @BinderThread 189 @Override 190 public void onAssistantAvailable(boolean available) { 191 MAIN_EXECUTOR.execute(() -> { 192 mDeviceState.setAssistantAvailable(available); 193 TouchInteractionService.this.onAssistantVisibilityChanged(); 194 }); 195 } 196 197 @BinderThread 198 @Override 199 public void onAssistantVisibilityChanged(float visibility) { 200 MAIN_EXECUTOR.execute(() -> { 201 mDeviceState.setAssistantVisibility(visibility); 202 TouchInteractionService.this.onAssistantVisibilityChanged(); 203 }); 204 } 205 206 @BinderThread 207 public void onBackAction(boolean completed, int downX, int downY, boolean isButton, 208 boolean gestureSwipeLeft) { 209 if (mOverviewComponentObserver == null) { 210 return; 211 } 212 213 final BaseActivityInterface activityInterface = 214 mOverviewComponentObserver.getActivityInterface(); 215 UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY, 216 isButton, gestureSwipeLeft, activityInterface.getContainerType()); 217 218 if (completed && !isButton && shouldNotifyBackGesture()) { 219 UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture); 220 } 221 } 222 223 @BinderThread 224 public void onSystemUiStateChanged(int stateFlags) { 225 MAIN_EXECUTOR.execute(() -> { 226 mDeviceState.setSystemUiFlags(stateFlags); 227 TouchInteractionService.this.onSystemUiFlagsChanged(); 228 }); 229 } 230 231 @BinderThread 232 public void onActiveNavBarRegionChanges(Region region) { 233 MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region)); 234 } 235 236 public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) { 237 WindowBounds wb = new WindowBounds(bounds, insets); 238 MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb)); 239 } 240 241 /** Deprecated methods **/ 242 public void onQuickStep(MotionEvent motionEvent) { } 243 244 public void onQuickScrubEnd() { } 245 246 public void onQuickScrubProgress(float progress) { } 247 248 public void onQuickScrubStart() { } 249 250 public void onPreMotionEvent(int downHitTarget) { } 251 252 public void onMotionEvent(MotionEvent ev) { 253 ev.recycle(); 254 } 255 256 public void onBind(ISystemUiProxy iSystemUiProxy) { } 257 }; 258 259 private static boolean sConnected = false; 260 private static boolean sIsInitialized = false; 261 isConnected()262 public static boolean isConnected() { 263 return sConnected; 264 } 265 isInitialized()266 public static boolean isInitialized() { 267 return sIsInitialized; 268 } 269 270 private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory = 271 this::createLauncherSwipeHandler; 272 private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory = 273 this::createFallbackSwipeHandler; 274 275 private ActivityManagerWrapper mAM; 276 private OverviewCommandHelper mOverviewCommandHelper; 277 private OverviewComponentObserver mOverviewComponentObserver; 278 private InputConsumerController mInputConsumer; 279 private RecentsAnimationDeviceState mDeviceState; 280 private TaskAnimationManager mTaskAnimationManager; 281 282 private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP; 283 private InputConsumer mConsumer = InputConsumer.NO_OP; 284 private Choreographer mMainChoreographer; 285 private InputConsumer mResetGestureInputConsumer; 286 private GestureState mGestureState = DEFAULT_STATE; 287 288 private InputMonitorCompat mInputMonitorCompat; 289 private InputEventReceiver mInputEventReceiver; 290 291 @Override onCreate()292 public void onCreate() { 293 super.onCreate(); 294 // Initialize anything here that is needed in direct boot mode. 295 // Everything else should be initialized in onUserUnlocked() below. 296 mMainChoreographer = Choreographer.getInstance(); 297 mAM = ActivityManagerWrapper.getInstance(); 298 mDeviceState = new RecentsAnimationDeviceState(this); 299 mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); 300 mDeviceState.runOnUserUnlocked(this::onUserUnlocked); 301 ProtoTracer.INSTANCE.get(this).add(this); 302 303 sConnected = true; 304 } 305 disposeEventHandlers()306 private void disposeEventHandlers() { 307 if (mInputEventReceiver != null) { 308 mInputEventReceiver.dispose(); 309 mInputEventReceiver = null; 310 } 311 if (mInputMonitorCompat != null) { 312 mInputMonitorCompat.dispose(); 313 mInputMonitorCompat = null; 314 } 315 } 316 initInputMonitor()317 private void initInputMonitor() { 318 disposeEventHandlers(); 319 if (mDeviceState.isButtonNavMode() || !SystemUiProxy.INSTANCE.get(this).isActive()) { 320 return; 321 } 322 323 Bundle bundle = SystemUiProxy.INSTANCE.get(this).monitorGestureInput("swipe-up", 324 mDeviceState.getDisplayId()); 325 mInputMonitorCompat = InputMonitorCompat.fromBundle(bundle, KEY_EXTRA_INPUT_MONITOR); 326 mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), 327 mMainChoreographer, this::onInputEvent); 328 329 mDeviceState.updateGestureTouchRegions(); 330 } 331 332 /** 333 * Called when the navigation mode changes, guaranteed to be after the device state has updated. 334 */ onNavigationModeChanged(SysUINavigationMode.Mode mode)335 private void onNavigationModeChanged(SysUINavigationMode.Mode mode) { 336 initInputMonitor(); 337 resetHomeBounceSeenOnQuickstepEnabledFirstTime(); 338 } 339 340 @UiThread onUserUnlocked()341 public void onUserUnlocked() { 342 mTaskAnimationManager = new TaskAnimationManager(); 343 mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState); 344 mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState, 345 mOverviewComponentObserver); 346 mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager); 347 mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); 348 mInputConsumer.registerInputConsumer(); 349 onSystemUiFlagsChanged(); 350 onAssistantVisibilityChanged(); 351 352 // Temporarily disable model preload 353 // new ModelPreload().start(this); 354 mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this) 355 .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT)); 356 resetHomeBounceSeenOnQuickstepEnabledFirstTime(); 357 358 PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this, 359 OverscrollPlugin.class, false /* allowMultiple */); 360 361 mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange); 362 onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame()); 363 } 364 resetHomeBounceSeenOnQuickstepEnabledFirstTime()365 private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { 366 if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) { 367 // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation 368 // mode doesn't have gestures 369 return; 370 } 371 372 // Reset home bounce seen on quick step enabled for first time 373 SharedPreferences sharedPrefs = Utilities.getPrefs(this); 374 if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) { 375 sharedPrefs.edit() 376 .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true) 377 .putBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN, false) 378 .apply(); 379 } 380 } 381 onOverviewTargetChange(boolean isHomeAndOverviewSame)382 private void onOverviewTargetChange(boolean isHomeAndOverviewSame) { 383 AccessibilityManager am = getSystemService(AccessibilityManager.class); 384 385 if (isHomeAndOverviewSame) { 386 Intent intent = new Intent(mOverviewComponentObserver.getHomeIntent()) 387 .setAction(Intent.ACTION_ALL_APPS); 388 RemoteAction allAppsAction = new RemoteAction( 389 Icon.createWithResource(this, R.drawable.ic_apps), 390 getString(R.string.all_apps_label), 391 getString(R.string.all_apps_label), 392 PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent, 393 PendingIntent.FLAG_UPDATE_CURRENT)); 394 am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS); 395 } else { 396 am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); 397 } 398 } 399 400 @UiThread onSystemUiFlagsChanged()401 private void onSystemUiFlagsChanged() { 402 if (mDeviceState.isUserUnlocked()) { 403 SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags( 404 mDeviceState.getSystemUiStateFlags()); 405 mOverviewComponentObserver.onSystemUiStateChanged(); 406 407 // Update the tracing state 408 if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) { 409 ProtoTracer.INSTANCE.get(TouchInteractionService.this).start(); 410 } else { 411 ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop(); 412 } 413 } 414 } 415 416 @UiThread onAssistantVisibilityChanged()417 private void onAssistantVisibilityChanged() { 418 if (mDeviceState.isUserUnlocked()) { 419 mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged( 420 mDeviceState.getAssistantVisibility()); 421 } 422 } 423 424 @Override onDestroy()425 public void onDestroy() { 426 sIsInitialized = false; 427 if (mDeviceState.isUserUnlocked()) { 428 mInputConsumer.unregisterInputConsumer(); 429 mOverviewComponentObserver.onDestroy(); 430 PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this); 431 } 432 disposeEventHandlers(); 433 mDeviceState.destroy(); 434 SystemUiProxy.INSTANCE.get(this).setProxy(null); 435 ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop(); 436 ProtoTracer.INSTANCE.get(this).remove(this); 437 438 getSystemService(AccessibilityManager.class) 439 .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); 440 441 sConnected = false; 442 super.onDestroy(); 443 } 444 445 @Override onBind(Intent intent)446 public IBinder onBind(Intent intent) { 447 Log.d(TAG, "Touch service connected"); 448 return mMyBinder; 449 } 450 onInputEvent(InputEvent ev)451 private void onInputEvent(InputEvent ev) { 452 if (!(ev instanceof MotionEvent)) { 453 Log.e(TAG, "Unknown event " + ev); 454 return; 455 } 456 MotionEvent event = (MotionEvent) ev; 457 458 TestLogging.recordMotionEvent( 459 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event); 460 461 if (!mDeviceState.isUserUnlocked()) { 462 return; 463 } 464 465 Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride( 466 TraceHelper.FLAG_ALLOW_BINDER_TRACKING); 467 468 final int action = event.getAction(); 469 if (action == ACTION_DOWN) { 470 if (TestProtocol.sDebugTracing) { 471 Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN"); 472 } 473 mDeviceState.setOrientationTransformIfNeeded(event); 474 475 if (mDeviceState.isInSwipeUpTouchRegion(event)) { 476 if (TestProtocol.sDebugTracing) { 477 Log.d(TestProtocol.NO_SWIPE_TO_HOME, 478 "TouchInteractionService.onInputEvent:isInSwipeUpTouchRegion"); 479 } 480 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger 481 // onConsumerInactive and wipe the previous gesture state 482 GestureState prevGestureState = new GestureState(mGestureState); 483 GestureState newGestureState = createGestureState(mGestureState); 484 mConsumer.onConsumerAboutToBeSwitched(); 485 mGestureState = newGestureState; 486 mConsumer = newConsumer(prevGestureState, mGestureState, event); 487 488 ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName()); 489 mUncheckedConsumer = mConsumer; 490 } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()) { 491 mGestureState = createGestureState(mGestureState); 492 ActivityManager.RunningTaskInfo runningTask = mGestureState.getRunningTask(); 493 if (mDeviceState.canTriggerAssistantAction(event, runningTask)) { 494 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we 495 // should not interrupt it. QuickSwitch assumes that interruption can only 496 // happen if the next gesture is also quick switch. 497 mUncheckedConsumer = new AssistantInputConsumer( 498 this, 499 mGestureState, 500 InputConsumer.NO_OP, mInputMonitorCompat, 501 mOverviewComponentObserver.assistantGestureIsConstrained()); 502 } else { 503 mUncheckedConsumer = InputConsumer.NO_OP; 504 } 505 } else { 506 mUncheckedConsumer = InputConsumer.NO_OP; 507 } 508 } else { 509 // Other events 510 if (mUncheckedConsumer != InputConsumer.NO_OP) { 511 // Only transform the event if we are handling it in a proper consumer 512 mDeviceState.setOrientationTransformIfNeeded(event); 513 } 514 } 515 516 if (mUncheckedConsumer != InputConsumer.NO_OP) { 517 switch (event.getActionMasked()) { 518 case ACTION_DOWN: 519 case ACTION_UP: 520 ActiveGestureLog.INSTANCE.addLog("onMotionEvent(" 521 + (int) event.getRawX() + ", " + (int) event.getRawY() + ")", 522 event.getActionMasked()); 523 break; 524 default: 525 ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked()); 526 break; 527 } 528 } 529 530 boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL) 531 && mConsumer != null 532 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture(); 533 mUncheckedConsumer.onMotionEvent(event); 534 535 if (cleanUpConsumer) { 536 reset(); 537 } 538 TraceHelper.INSTANCE.endFlagsOverride(traceToken); 539 } 540 createGestureState(GestureState previousGestureState)541 private GestureState createGestureState(GestureState previousGestureState) { 542 GestureState gestureState = new GestureState(mOverviewComponentObserver, 543 ActiveGestureLog.INSTANCE.generateAndSetLogId()); 544 if (mTaskAnimationManager.isRecentsAnimationRunning()) { 545 gestureState.updateRunningTask(previousGestureState.getRunningTask()); 546 gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId()); 547 gestureState.updatePreviouslyAppearedTaskIds( 548 previousGestureState.getPreviouslyAppearedTaskIds()); 549 } else { 550 gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0", 551 () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */))); 552 } 553 return gestureState; 554 } 555 newConsumer(GestureState previousGestureState, GestureState newGestureState, MotionEvent event)556 private InputConsumer newConsumer(GestureState previousGestureState, 557 GestureState newGestureState, MotionEvent event) { 558 if (TestProtocol.sDebugTracing) { 559 Log.d(TestProtocol.NO_SWIPE_TO_HOME, "newConsumer"); 560 } 561 boolean canStartSystemGesture = mDeviceState.canStartSystemGesture(); 562 563 if (!mDeviceState.isUserUnlocked()) { 564 if (canStartSystemGesture) { 565 // This handles apps launched in direct boot mode (e.g. dialer) as well as apps 566 // launched while device is locked even after exiting direct boot mode (e.g. camera). 567 return createDeviceLockedInputConsumer(newGestureState); 568 } else { 569 return mResetGestureInputConsumer; 570 } 571 } 572 if (TestProtocol.sDebugTracing) { 573 Log.d(TestProtocol.NO_SWIPE_TO_HOME, "newConsumer:user is unlocked"); 574 } 575 576 // When there is an existing recents animation running, bypass systemState check as this is 577 // a followup gesture and the first gesture started in a valid system state. 578 InputConsumer base = canStartSystemGesture 579 || previousGestureState.isRecentsAnimationRunning() 580 ? newBaseConsumer(previousGestureState, newGestureState, event) 581 : mResetGestureInputConsumer; 582 if (mDeviceState.isGesturalNavMode()) { 583 handleOrientationSetup(base); 584 } 585 if (mDeviceState.isFullyGesturalNavMode()) { 586 if (mDeviceState.canTriggerAssistantAction(event, newGestureState.getRunningTask())) { 587 base = new AssistantInputConsumer( 588 this, 589 newGestureState, 590 base, 591 mInputMonitorCompat, 592 mOverviewComponentObserver.assistantGestureIsConstrained()); 593 } 594 595 if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) { 596 OverscrollPlugin plugin = null; 597 if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) { 598 TaskOverlayFactory factory = 599 TaskOverlayFactory.INSTANCE.get(getApplicationContext()); 600 plugin = factory.getLocalOverscrollPlugin(); // may be null 601 } 602 603 // If not local plugin was forced, use the actual overscroll plugin if available. 604 if (plugin == null && mOverscrollPlugin != null && mOverscrollPlugin.isActive()) { 605 plugin = mOverscrollPlugin; 606 } 607 608 if (plugin != null) { 609 // Put the overscroll gesture as higher priority than the Assistant or base 610 // gestures 611 base = new OverscrollInputConsumer(this, newGestureState, base, 612 mInputMonitorCompat, plugin); 613 } 614 } 615 616 // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles 617 // instead of going all the way home when a swipe up is detected. 618 if (mDeviceState.isBubblesExpanded() || mDeviceState.isGlobalActionsShowing()) { 619 base = new SysUiOverlayInputConsumer( 620 getBaseContext(), mDeviceState, mInputMonitorCompat); 621 } 622 623 if (mDeviceState.isScreenPinningActive()) { 624 // Note: we only allow accessibility to wrap this, and it replaces the previous 625 // base input consumer (which should be NO_OP anyway since topTaskLocked == true). 626 base = new ScreenPinnedInputConsumer(this, newGestureState); 627 } 628 629 if (mDeviceState.isAccessibilityMenuAvailable()) { 630 base = new AccessibilityInputConsumer(this, mDeviceState, base, 631 mInputMonitorCompat); 632 } 633 } else { 634 if (mDeviceState.isScreenPinningActive()) { 635 base = mResetGestureInputConsumer; 636 } 637 } 638 return base; 639 } 640 handleOrientationSetup(InputConsumer baseInputConsumer)641 private void handleOrientationSetup(InputConsumer baseInputConsumer) { 642 if (TestProtocol.sDebugTracing) { 643 Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.1"); 644 } 645 646 baseInputConsumer.notifyOrientationSetup(); 647 } 648 newBaseConsumer(GestureState previousGestureState, GestureState gestureState, MotionEvent event)649 private InputConsumer newBaseConsumer(GestureState previousGestureState, 650 GestureState gestureState, MotionEvent event) { 651 if (mDeviceState.isKeyguardShowingOccluded()) { 652 // This handles apps showing over the lockscreen (e.g. camera) 653 return createDeviceLockedInputConsumer(gestureState); 654 } 655 656 // Use overview input consumer for sharesheets on top of home. 657 boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted() 658 && gestureState.getRunningTask() != null 659 && ACTION_CHOOSER.equals(gestureState.getRunningTask().baseIntent.getAction()); 660 if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) { 661 // In the case where we are in the excluded assistant state, ignore it and treat the 662 // running activity as the task behind the assistant 663 gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant", 664 () -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */))); 665 ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); 666 ComponentName runningComponent = 667 gestureState.getRunningTask().baseIntent.getComponent(); 668 forceOverviewInputConsumer = 669 runningComponent != null && runningComponent.equals(homeComponent); 670 } 671 672 if (gestureState.getRunningTask() == null) { 673 return mResetGestureInputConsumer; 674 } else if (previousGestureState.isRunningAnimationToLauncher() 675 || gestureState.getActivityInterface().isResumed() 676 || forceOverviewInputConsumer) { 677 return createOverviewInputConsumer( 678 previousGestureState, gestureState, event, forceOverviewInputConsumer); 679 } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() 680 && gestureState.getActivityInterface().isInLiveTileMode()) { 681 return createOverviewInputConsumer( 682 previousGestureState, gestureState, event, forceOverviewInputConsumer); 683 } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) { 684 return mResetGestureInputConsumer; 685 } else { 686 return createOtherActivityInputConsumer(gestureState, event); 687 } 688 } 689 createOtherActivityInputConsumer(GestureState gestureState, MotionEvent event)690 private InputConsumer createOtherActivityInputConsumer(GestureState gestureState, 691 MotionEvent event) { 692 693 final BaseSwipeUpHandler.Factory factory; 694 if (!mOverviewComponentObserver.isHomeAndOverviewSame()) { 695 factory = mFallbackSwipeHandlerFactory; 696 } else { 697 factory = mLauncherSwipeHandlerFactory; 698 } 699 700 final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame() 701 || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event); 702 final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event); 703 return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager, 704 gestureState, shouldDefer, this::onConsumerInactive, 705 mInputMonitorCompat, disableHorizontalSwipe, factory); 706 } 707 createDeviceLockedInputConsumer(GestureState gestureState)708 private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) { 709 if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) { 710 return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager, 711 gestureState, mInputMonitorCompat); 712 } else { 713 return mResetGestureInputConsumer; 714 } 715 } 716 createOverviewInputConsumer(GestureState previousGestureState, GestureState gestureState, MotionEvent event, boolean forceOverviewInputConsumer)717 public InputConsumer createOverviewInputConsumer(GestureState previousGestureState, 718 GestureState gestureState, MotionEvent event, 719 boolean forceOverviewInputConsumer) { 720 StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity(); 721 if (activity == null) { 722 return mResetGestureInputConsumer; 723 } 724 725 if (activity.getRootView().hasWindowFocus() 726 || previousGestureState.isRunningAnimationToLauncher() 727 || (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get() 728 && forceOverviewInputConsumer)) { 729 return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat, 730 false /* startingInActivityBounds */); 731 } else { 732 final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event); 733 return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState, 734 mInputMonitorCompat, disableHorizontalSwipe); 735 } 736 } 737 738 /** 739 * To be called by the consumer when it's no longer active. This can be called by any consumer 740 * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts 741 * intercepting touches, the base consumer can try to call this). 742 */ onConsumerInactive(InputConsumer caller)743 private void onConsumerInactive(InputConsumer caller) { 744 if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) { 745 reset(); 746 } 747 } 748 reset()749 private void reset() { 750 mConsumer = mUncheckedConsumer = mResetGestureInputConsumer; 751 mGestureState = DEFAULT_STATE; 752 } 753 preloadOverview(boolean fromInit)754 private void preloadOverview(boolean fromInit) { 755 if (!mDeviceState.isUserUnlocked()) { 756 return; 757 } 758 759 if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) { 760 // Prevent the overview from being started before the real home on first boot. 761 return; 762 } 763 764 if (RestoreDbTask.isPending(this) || !mDeviceState.isUserSetupComplete()) { 765 // Preloading while a restore is pending may cause launcher to start the restore 766 // too early. 767 return; 768 } 769 770 final BaseActivityInterface activityInterface = 771 mOverviewComponentObserver.getActivityInterface(); 772 final Intent overviewIntent = new Intent( 773 mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState()); 774 if (activityInterface.getCreatedActivity() == null) { 775 // Make sure that UI states will be initialized. 776 activityInterface.createActivityInitListener((wasVisible) -> { 777 AppLaunchTracker.INSTANCE.get(TouchInteractionService.this); 778 return false; 779 }).register(overviewIntent); 780 } else if (fromInit) { 781 // The activity has been created before the initialization of overview service. It is 782 // usually happens when booting or launcher is the top activity, so we should already 783 // have the latest state. 784 return; 785 } 786 787 mTaskAnimationManager.preloadRecentsAnimation(overviewIntent); 788 } 789 790 @Override onConfigurationChanged(Configuration newConfig)791 public void onConfigurationChanged(Configuration newConfig) { 792 if (!mDeviceState.isUserUnlocked()) { 793 return; 794 } 795 final BaseActivityInterface activityInterface = 796 mOverviewComponentObserver.getActivityInterface(); 797 final BaseDraggingActivity activity = activityInterface.getCreatedActivity(); 798 if (activity == null || activity.isStarted()) { 799 // We only care about the existing background activity. 800 return; 801 } 802 if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(), 803 activity.getResources().getConfiguration().diff(newConfig))) { 804 return; 805 } 806 807 preloadOverview(false /* fromInit */); 808 } 809 810 @Override dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs)811 protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { 812 if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) { 813 ArgList args = new ArgList(Arrays.asList(rawArgs)); 814 switch (args.nextArg()) { 815 case "cmd": 816 if (args.peekArg() == null) { 817 printAvailableCommands(pw); 818 } else { 819 onCommand(pw, args); 820 } 821 break; 822 } 823 } else { 824 // Dump everything 825 FeatureFlags.dump(pw); 826 if (mDeviceState.isUserUnlocked()) { 827 PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); 828 } 829 mDeviceState.dump(pw); 830 if (mOverviewComponentObserver != null) { 831 mOverviewComponentObserver.dump(pw); 832 } 833 if (mGestureState != null) { 834 mGestureState.dump(pw); 835 } 836 SysUINavigationMode.INSTANCE.get(this).dump(pw); 837 pw.println("TouchState:"); 838 BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null 839 : mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); 840 boolean resumed = mOverviewComponentObserver != null 841 && mOverviewComponentObserver.getActivityInterface().isResumed(); 842 pw.println(" createdOverviewActivity=" + createdOverviewActivity); 843 pw.println(" resumed=" + resumed); 844 pw.println(" mConsumer=" + mConsumer.getName()); 845 ActiveGestureLog.INSTANCE.dump("", pw); 846 pw.println("ProtoTrace:"); 847 pw.println(" file=" 848 + ProtoTracer.INSTANCE.get(TouchInteractionService.this).getTraceFile()); 849 } 850 } 851 printAvailableCommands(PrintWriter pw)852 private void printAvailableCommands(PrintWriter pw) { 853 pw.println("Available commands:"); 854 pw.println(" clear-touch-log: Clears the touch interaction log"); 855 } 856 onCommand(PrintWriter pw, ArgList args)857 private void onCommand(PrintWriter pw, ArgList args) { 858 switch (args.nextArg()) { 859 case "clear-touch-log": 860 ActiveGestureLog.INSTANCE.clear(); 861 break; 862 } 863 } 864 createLauncherSwipeHandler( GestureState gestureState, long touchTimeMs, boolean continuingLastGesture)865 private BaseSwipeUpHandler createLauncherSwipeHandler( 866 GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) { 867 return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager, 868 gestureState, touchTimeMs, continuingLastGesture, mInputConsumer); 869 } 870 createFallbackSwipeHandler( GestureState gestureState, long touchTimeMs, boolean continuingLastGesture)871 private BaseSwipeUpHandler createFallbackSwipeHandler( 872 GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) { 873 return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager, 874 gestureState, touchTimeMs, continuingLastGesture, mInputConsumer); 875 } 876 shouldNotifyBackGesture()877 protected boolean shouldNotifyBackGesture() { 878 return mBackGestureNotificationCounter > 0 && 879 !mDeviceState.getGestureBlockedActivityPackages().isEmpty(); 880 } 881 882 @WorkerThread tryNotifyBackGesture()883 protected void tryNotifyBackGesture() { 884 if (shouldNotifyBackGesture()) { 885 mBackGestureNotificationCounter--; 886 Utilities.getDevicePrefs(this).edit() 887 .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply(); 888 mDeviceState.getGestureBlockedActivityPackages().forEach(blockedPackage -> 889 sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(blockedPackage))); 890 } 891 } 892 893 @Override onPluginConnected(OverscrollPlugin overscrollPlugin, Context context)894 public void onPluginConnected(OverscrollPlugin overscrollPlugin, Context context) { 895 mOverscrollPlugin = overscrollPlugin; 896 } 897 898 @Override onPluginDisconnected(OverscrollPlugin overscrollPlugin)899 public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) { 900 mOverscrollPlugin = null; 901 } 902 903 @Override writeToProto(LauncherTraceProto proto)904 public void writeToProto(LauncherTraceProto proto) { 905 if (proto.touchInteractionService == null) { 906 proto.touchInteractionService = new TouchInteractionServiceProto(); 907 } 908 proto.touchInteractionService.serviceConnected = true; 909 proto.touchInteractionService.serviceConnected = true; 910 } 911 } 912