1 /* 2 * Copyright (C) 2019 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.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 19 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 22 23 import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll; 24 import static com.android.launcher3.util.DisplayController.CHANGE_ALL; 25 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; 26 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; 27 import static com.android.launcher3.util.NavigationMode.NO_BUTTON; 28 import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS; 29 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED; 30 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED; 31 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; 32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; 33 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; 34 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED; 35 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED; 36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING; 37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING; 38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING; 39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION; 40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; 41 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE; 42 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; 43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; 44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; 45 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; 46 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 47 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 48 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; 49 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; 50 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; 51 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED; 52 53 import android.app.ActivityTaskManager; 54 import android.content.Context; 55 import android.graphics.Region; 56 import android.inputmethodservice.InputMethodService; 57 import android.net.Uri; 58 import android.os.RemoteException; 59 import android.os.SystemProperties; 60 import android.provider.Settings; 61 import android.view.MotionEvent; 62 import android.view.ViewConfiguration; 63 64 import androidx.annotation.NonNull; 65 import androidx.annotation.Nullable; 66 import androidx.annotation.VisibleForTesting; 67 68 import com.android.launcher3.dagger.ApplicationContext; 69 import com.android.launcher3.dagger.LauncherAppComponent; 70 import com.android.launcher3.dagger.LauncherAppSingleton; 71 import com.android.launcher3.util.DaggerSingletonObject; 72 import com.android.launcher3.util.DaggerSingletonTracker; 73 import com.android.launcher3.util.DisplayController; 74 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; 75 import com.android.launcher3.util.DisplayController.Info; 76 import com.android.launcher3.util.NavigationMode; 77 import com.android.launcher3.util.SettingsCache; 78 import com.android.quickstep.TopTaskTracker.CachedTaskInfo; 79 import com.android.quickstep.util.ActiveGestureLog; 80 import com.android.quickstep.util.ContextualSearchStateManager; 81 import com.android.quickstep.util.GestureExclusionManager; 82 import com.android.quickstep.util.GestureExclusionManager.ExclusionListener; 83 import com.android.quickstep.util.NavBarPosition; 84 import com.android.systemui.shared.Flags; 85 import com.android.systemui.shared.system.ActivityManagerWrapper; 86 import com.android.systemui.shared.system.QuickStepContract; 87 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; 88 import com.android.systemui.shared.system.TaskStackChangeListener; 89 import com.android.systemui.shared.system.TaskStackChangeListeners; 90 91 import java.io.PrintWriter; 92 import java.util.Map; 93 import java.util.Set; 94 import java.util.concurrent.ConcurrentHashMap; 95 96 import javax.inject.Inject; 97 98 /** 99 * Manages the state of the system during a swipe up gesture. 100 */ 101 @LauncherAppSingleton 102 public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener { 103 104 static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; 105 106 // TODO: Move to quickstep contract 107 private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f; 108 private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f; 109 110 public static DaggerSingletonObject<RecentsAnimationDeviceState> INSTANCE = 111 new DaggerSingletonObject<>(LauncherAppComponent::getRecentsAnimationDeviceState); 112 113 private final Context mContext; 114 private final DisplayController mDisplayController; 115 116 private final GestureExclusionManager mExclusionManager; 117 private final ContextualSearchStateManager mContextualSearchStateManager; 118 119 private final RotationTouchHelper mRotationTouchHelper; 120 private final TaskStackChangeListener mPipListener; 121 // Cache for better performance since it doesn't change at runtime. 122 private final boolean mCanImeRenderGesturalNavButtons = 123 InputMethodService.canImeRenderGesturalNavButtons(); 124 125 private @SystemUiStateFlags long mSystemUiStateFlags = QuickStepContract.SYSUI_STATE_AWAKE; 126 private final Map<Integer, Long> mSysUIStateFlagsPerDisplay = new ConcurrentHashMap<>(); 127 private NavigationMode mMode = THREE_BUTTONS; 128 private NavBarPosition mNavBarPosition; 129 130 private final Region mDeferredGestureRegion = new Region(); 131 private boolean mAssistantAvailable; 132 private float mAssistantVisibility; 133 private boolean mIsUserSetupComplete; 134 private boolean mIsOneHandedModeEnabled; 135 private boolean mIsSwipeToNotificationEnabled; 136 private final boolean mIsOneHandedModeSupported; 137 private boolean mPipIsActive; 138 private boolean mIsPredictiveBackToHomeInProgress; 139 140 private int mGestureBlockingTaskId = -1; 141 private @NonNull Region mExclusionRegion = GestureExclusionManager.EMPTY_REGION; 142 private boolean mExclusionListenerRegistered; 143 144 @VisibleForTesting 145 @Inject RecentsAnimationDeviceState( @pplicationContext Context context, GestureExclusionManager exclusionManager, DisplayController displayController, ContextualSearchStateManager contextualSearchStateManager, RotationTouchHelper rotationTouchHelper, SettingsCache settingsCache, DaggerSingletonTracker lifeCycle)146 RecentsAnimationDeviceState( 147 @ApplicationContext Context context, 148 GestureExclusionManager exclusionManager, 149 DisplayController displayController, 150 ContextualSearchStateManager contextualSearchStateManager, 151 RotationTouchHelper rotationTouchHelper, 152 SettingsCache settingsCache, 153 DaggerSingletonTracker lifeCycle) { 154 mContext = context; 155 mDisplayController = displayController; 156 mExclusionManager = exclusionManager; 157 mContextualSearchStateManager = contextualSearchStateManager; 158 mRotationTouchHelper = rotationTouchHelper; 159 mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false); 160 161 // Register for exclusion updates 162 lifeCycle.addCloseable(this::unregisterExclusionListener); 163 164 // Register for display changes changes 165 mDisplayController.addChangeListener(this); 166 onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL); 167 lifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(this)); 168 169 if (mIsOneHandedModeSupported) { 170 Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED); 171 SettingsCache.OnChangeListener onChangeListener = 172 enabled -> mIsOneHandedModeEnabled = enabled; 173 settingsCache.register(oneHandedUri, onChangeListener); 174 mIsOneHandedModeEnabled = settingsCache.getValue(oneHandedUri); 175 lifeCycle.addCloseable(() -> settingsCache.unregister(oneHandedUri, onChangeListener)); 176 } else { 177 mIsOneHandedModeEnabled = false; 178 } 179 180 Uri swipeBottomNotificationUri = 181 Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED); 182 SettingsCache.OnChangeListener onChangeListener = 183 enabled -> mIsSwipeToNotificationEnabled = enabled; 184 settingsCache.register(swipeBottomNotificationUri, onChangeListener); 185 mIsSwipeToNotificationEnabled = settingsCache.getValue(swipeBottomNotificationUri); 186 lifeCycle.addCloseable( 187 () -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener)); 188 189 Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE); 190 mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0); 191 if (!mIsUserSetupComplete) { 192 SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e; 193 settingsCache.register(setupCompleteUri, userSetupChangeListener); 194 lifeCycle.addCloseable( 195 () -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener)); 196 } 197 198 try { 199 mPipIsActive = ActivityTaskManager.getService().getRootTaskInfo( 200 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED) != null; 201 } catch (RemoteException e) { 202 // Do nothing 203 } 204 mPipListener = new TaskStackChangeListener() { 205 @Override 206 public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { 207 mPipIsActive = true; 208 } 209 210 @Override 211 public void onActivityUnpinned() { 212 mPipIsActive = false; 213 } 214 }; 215 TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener); 216 lifeCycle.addCloseable(() -> 217 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener)); 218 } 219 220 /** 221 * Adds a listener for the nav mode change, guaranteed to be called after the device state's 222 * mode has changed. 223 * 224 * @return Added {@link DisplayInfoChangeListener} so that caller is 225 * responsible for removing the listener from {@link DisplayController} to avoid memory leak. 226 */ addNavigationModeChangedCallback( Runnable callback)227 public DisplayController.DisplayInfoChangeListener addNavigationModeChangedCallback( 228 Runnable callback) { 229 DisplayController.DisplayInfoChangeListener listener = (context, info, flags) -> { 230 if ((flags & CHANGE_NAVIGATION_MODE) != 0) { 231 callback.run(); 232 } 233 }; 234 mDisplayController.addChangeListener(listener); 235 callback.run(); 236 return listener; 237 } 238 239 /** 240 * Remove the {DisplayController.DisplayInfoChangeListener} added from 241 * {@link #addNavigationModeChangedCallback} when {@link TouchInteractionService} is destroyed. 242 */ removeDisplayInfoChangeListener( DisplayController.DisplayInfoChangeListener listener)243 public void removeDisplayInfoChangeListener( 244 DisplayController.DisplayInfoChangeListener listener) { 245 mDisplayController.removeChangeListener(listener); 246 } 247 248 @Override onDisplayInfoChanged(Context context, Info info, int flags)249 public void onDisplayInfoChanged(Context context, Info info, int flags) { 250 if ((flags & (CHANGE_ROTATION | CHANGE_NAVIGATION_MODE)) != 0) { 251 mMode = info.getNavigationMode(); 252 ActiveGestureLog.INSTANCE.setIsFullyGesturalNavMode(isFullyGesturalNavMode()); 253 mNavBarPosition = new NavBarPosition(mMode, info); 254 255 if (mMode == NO_BUTTON) { 256 registerExclusionListener(); 257 } else { 258 unregisterExclusionListener(); 259 } 260 } 261 } 262 263 @Override onGestureExclusionChanged(@ullable Region exclusionRegion, @Nullable Region unrestrictedOrNull)264 public void onGestureExclusionChanged(@Nullable Region exclusionRegion, 265 @Nullable Region unrestrictedOrNull) { 266 mExclusionRegion = exclusionRegion != null 267 ? exclusionRegion : GestureExclusionManager.EMPTY_REGION; 268 } 269 270 /** 271 * Registers itself for getting exclusion rect changes. 272 */ registerExclusionListener()273 public void registerExclusionListener() { 274 if (mExclusionListenerRegistered) { 275 return; 276 } 277 mExclusionManager.addListener(this); 278 mExclusionListenerRegistered = true; 279 } 280 281 /** 282 * Unregisters itself as gesture exclusion listener if previously registered. 283 */ unregisterExclusionListener()284 public void unregisterExclusionListener() { 285 if (!mExclusionListenerRegistered) { 286 return; 287 } 288 mExclusionManager.removeListener(this); 289 mExclusionListenerRegistered = false; 290 } 291 onOneHandedModeChanged(int newGesturalHeight)292 public void onOneHandedModeChanged(int newGesturalHeight) { 293 mRotationTouchHelper.setGesturalHeight(newGesturalHeight); 294 } 295 296 /** 297 * @return the nav bar position for the current nav bar mode and display rotation. 298 */ getNavBarPosition()299 public NavBarPosition getNavBarPosition() { 300 return mNavBarPosition; 301 } 302 getMode()303 public NavigationMode getMode() { 304 return mMode; 305 } 306 307 /** 308 * @return whether the current nav mode is fully gestural. 309 */ isFullyGesturalNavMode()310 public boolean isFullyGesturalNavMode() { 311 return mMode == NO_BUTTON; 312 } 313 314 /** 315 * @return whether the current nav mode has some gestures (either 2 or 0 button mode). 316 */ isGesturalNavMode()317 public boolean isGesturalNavMode() { 318 return mMode.hasGestures; 319 } 320 321 /** 322 * @return whether the current nav mode is button-based. 323 */ isButtonNavMode()324 public boolean isButtonNavMode() { 325 return mMode == THREE_BUTTONS; 326 } 327 328 /** 329 * @return whether the user has completed setup wizard 330 */ isUserSetupComplete()331 public boolean isUserSetupComplete() { 332 return mIsUserSetupComplete; 333 } 334 335 /** 336 * Sets the task id where gestures should be blocked 337 */ setGestureBlockingTaskId(int taskId)338 public void setGestureBlockingTaskId(int taskId) { 339 mGestureBlockingTaskId = taskId; 340 } 341 342 /** 343 * @return whether the given running task info matches the gesture-blocked task. 344 */ isGestureBlockedTask(CachedTaskInfo taskInfo)345 public boolean isGestureBlockedTask(CachedTaskInfo taskInfo) { 346 if (mGestureBlockingTaskId == INVALID_TASK_ID) { 347 return false; 348 } 349 if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) { 350 return taskInfo != null && taskInfo.topGroupedTaskContainsTask(mGestureBlockingTaskId); 351 } else { 352 return taskInfo != null && taskInfo.getTaskId() == mGestureBlockingTaskId; 353 } 354 } 355 356 /** 357 * Updates the system ui state flags from SystemUI for a specific display. 358 * 359 * @param stateFlags the current {@link SystemUiStateFlags} for the display. 360 * @param displayId the display's ID. 361 */ setSysUIStateFlagsForDisplay(@ystemUiStateFlags long stateFlags, int displayId)362 public void setSysUIStateFlagsForDisplay(@SystemUiStateFlags long stateFlags, 363 int displayId) { 364 mSysUIStateFlagsPerDisplay.put(displayId, stateFlags); 365 } 366 367 /** 368 * Clears the system ui state flags for a specific display. This is called when the display is 369 * destroyed. 370 * 371 * @param displayId the display's ID. 372 */ clearSysUIStateFlagsForDisplay(int displayId)373 public void clearSysUIStateFlagsForDisplay(int displayId) { 374 mSysUIStateFlagsPerDisplay.remove(displayId); 375 } 376 377 /** 378 * @return the system ui state flags for the default display. 379 */ 380 // TODO(141886704): See if we can remove this 381 @SystemUiStateFlags getSysuiStateFlag()382 public long getSysuiStateFlag() { 383 return getSystemUiStateFlags(DEFAULT_DISPLAY); 384 } 385 386 /** 387 * @return the system ui state flags for a given display ID. 388 */ 389 @SystemUiStateFlags getSystemUiStateFlags(int displayId)390 public long getSystemUiStateFlags(int displayId) { 391 return mSysUIStateFlagsPerDisplay.getOrDefault(displayId, 392 QuickStepContract.SYSUI_STATE_AWAKE); 393 } 394 395 /** 396 * @return the display ids that have sysui state. 397 */ getDisplaysWithSysUIState()398 public Set<Integer> getDisplaysWithSysUIState() { 399 return mSysUIStateFlagsPerDisplay.keySet(); 400 } 401 /** 402 * Sets the flag that indicates whether a predictive back-to-home animation is in progress 403 */ setPredictiveBackToHomeInProgress(boolean isInProgress)404 public void setPredictiveBackToHomeInProgress(boolean isInProgress) { 405 mIsPredictiveBackToHomeInProgress = isInProgress; 406 } 407 408 /** 409 * @return whether a predictive back-to-home animation is currently in progress 410 */ isPredictiveBackToHomeInProgress()411 public boolean isPredictiveBackToHomeInProgress() { 412 return mIsPredictiveBackToHomeInProgress; 413 } 414 415 /** 416 * @return whether SystemUI is in a state where we can start a system gesture. 417 */ canStartSystemGesture()418 public boolean canStartSystemGesture() { 419 boolean canStartWithNavHidden = (getSysuiStateFlag() & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 420 || (getSysuiStateFlag() & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0 421 || mRotationTouchHelper.isTaskListFrozen(); 422 return canStartWithNavHidden && canStartAnyGesture(); 423 } 424 425 /** 426 * @return whether SystemUI is in a state where we can start a system gesture from the trackpad. 427 * Trackpad gestures can start even when the nav bar / task bar is hidden in sticky immersive 428 * mode. 429 */ canStartTrackpadGesture()430 public boolean canStartTrackpadGesture() { 431 boolean trackpadGesturesEnabled = 432 (getSysuiStateFlag() & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0; 433 return trackpadGesturesEnabled && canStartAnyGesture(); 434 } 435 436 /** 437 * Common logic to determine if either trackpad or finger gesture can be started 438 */ canStartAnyGesture()439 private boolean canStartAnyGesture() { 440 boolean homeOrOverviewEnabled = (getSysuiStateFlag() & SYSUI_STATE_HOME_DISABLED) == 0 441 || (getSysuiStateFlag() & SYSUI_STATE_OVERVIEW_DISABLED) == 0; 442 long gestureDisablingStates = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED 443 | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING 444 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED 445 | SYSUI_STATE_MAGNIFICATION_OVERLAP 446 | SYSUI_STATE_DEVICE_DREAMING 447 | SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION 448 | SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING; 449 return (gestureDisablingStates & getSysuiStateFlag()) == 0 && homeOrOverviewEnabled; 450 } 451 452 /** 453 * @return whether the keyguard is showing and is occluded by an app showing above the keyguard 454 * (like camera or maps) 455 */ isKeyguardShowingOccluded()456 public boolean isKeyguardShowingOccluded() { 457 return (getSysuiStateFlag() & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0; 458 } 459 460 /** 461 * @return whether screen pinning is enabled and active 462 */ isScreenPinningActive()463 public boolean isScreenPinningActive() { 464 return (getSysuiStateFlag() & SYSUI_STATE_SCREEN_PINNING) != 0; 465 } 466 467 /** 468 * @return whether assistant gesture is constraint 469 */ isAssistantGestureIsConstrained()470 public boolean isAssistantGestureIsConstrained() { 471 return (getSysuiStateFlag() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0; 472 } 473 474 /** 475 * @return whether the bubble stack is expanded 476 */ isBubblesExpanded()477 public boolean isBubblesExpanded() { 478 return (getSysuiStateFlag() & SYSUI_STATE_BUBBLES_EXPANDED) != 0; 479 } 480 481 /** 482 * @return whether the global actions dialog is showing 483 */ isSystemUiDialogShowing()484 public boolean isSystemUiDialogShowing() { 485 return (getSysuiStateFlag() & SYSUI_STATE_DIALOG_SHOWING) != 0; 486 } 487 488 /** 489 * @return whether lock-task mode is active 490 */ isLockToAppActive()491 public boolean isLockToAppActive() { 492 return ActivityManagerWrapper.getInstance().isLockToAppActive(); 493 } 494 495 /** 496 * @return whether the accessibility menu is available. 497 */ isAccessibilityMenuAvailable()498 public boolean isAccessibilityMenuAvailable() { 499 return (getSysuiStateFlag() & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 500 } 501 502 /** 503 * @return whether the accessibility menu shortcut is available. 504 */ isAccessibilityMenuShortcutAvailable()505 public boolean isAccessibilityMenuShortcutAvailable() { 506 return (getSysuiStateFlag() & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 507 } 508 509 /** 510 * @return whether home is disabled (either by SUW/SysUI/device policy) 511 */ isHomeDisabled()512 public boolean isHomeDisabled() { 513 return (getSysuiStateFlag() & SYSUI_STATE_HOME_DISABLED) != 0; 514 } 515 516 /** 517 * @return whether overview is disabled (either by SUW/SysUI/device policy) 518 */ isOverviewDisabled()519 public boolean isOverviewDisabled() { 520 return (getSysuiStateFlag() & SYSUI_STATE_OVERVIEW_DISABLED) != 0; 521 } 522 523 /** 524 * @return whether one-handed mode is enabled and active 525 */ isOneHandedModeActive()526 public boolean isOneHandedModeActive() { 527 return (getSysuiStateFlag() & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0; 528 } 529 530 /** 531 * Sets the region in screen space where the gestures should be deferred (ie. due to specific 532 * nav bar ui). 533 */ setDeferredGestureRegion(Region deferredGestureRegion)534 public void setDeferredGestureRegion(Region deferredGestureRegion) { 535 mDeferredGestureRegion.set(deferredGestureRegion); 536 } 537 538 /** 539 * @return whether the given {@param event} is in the deferred gesture region indicating that 540 * the Launcher should not immediately start the recents animation until the gesture 541 * passes a certain threshold. 542 */ isInDeferredGestureRegion(MotionEvent event)543 public boolean isInDeferredGestureRegion(MotionEvent event) { 544 return mDeferredGestureRegion.contains((int) event.getX(), (int) event.getY()); 545 } 546 547 /** 548 * @return whether the given {@param event} is in the app-requested gesture-exclusion region. 549 * This is only used for quickswitch, and not swipe up. 550 */ isInExclusionRegion(MotionEvent event)551 public boolean isInExclusionRegion(MotionEvent event) { 552 return mMode == NO_BUTTON 553 && mExclusionRegion.contains((int) event.getX(), (int) event.getY()); 554 } 555 556 /** 557 * Sets whether the assistant is available. 558 */ setAssistantAvailable(boolean assistantAvailable)559 public void setAssistantAvailable(boolean assistantAvailable) { 560 mAssistantAvailable = assistantAvailable; 561 } 562 563 /** 564 * Sets the visibility fraction of the assistant. 565 */ setAssistantVisibility(float visibility)566 public void setAssistantVisibility(float visibility) { 567 mAssistantVisibility = visibility; 568 } 569 570 /** 571 * @return the visibility fraction of the assistant. 572 */ getAssistantVisibility()573 public float getAssistantVisibility() { 574 return mAssistantVisibility; 575 } 576 577 /** 578 * @return whether the Assistant gesture can be used in 3 button navigation mode. 579 */ supportsAssistantGestureInButtonNav()580 public boolean supportsAssistantGestureInButtonNav() { 581 return Flags.threeButtonCornerSwipe(); 582 } 583 584 /** 585 * @param ev An ACTION_DOWN motion event 586 * @return whether the given motion event can trigger the assistant over the current task. 587 */ canTriggerAssistantAction(MotionEvent ev)588 public boolean canTriggerAssistantAction(MotionEvent ev) { 589 return mAssistantAvailable 590 && !QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlag()) 591 && mRotationTouchHelper.touchInAssistantRegion(ev) 592 && !isTrackpadScroll(ev) 593 && !isLockToAppActive(); 594 } 595 596 /** 597 * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode 598 * 599 * @param ev The touch screen motion event. 600 * @return whether the given motion event can trigger the one handed mode. 601 */ canTriggerOneHandedAction(MotionEvent ev)602 public boolean canTriggerOneHandedAction(MotionEvent ev) { 603 if (!mIsOneHandedModeSupported) { 604 return false; 605 } 606 607 if (mIsOneHandedModeEnabled) { 608 final Info displayInfo = mDisplayController.getInfo(); 609 return (mRotationTouchHelper.touchInOneHandedModeRegion(ev) 610 && (displayInfo.currentSize.x < displayInfo.currentSize.y)); 611 } 612 return false; 613 } 614 isOneHandedModeEnabled()615 public boolean isOneHandedModeEnabled() { 616 return mIsOneHandedModeEnabled; 617 } 618 isSwipeToNotificationEnabled()619 public boolean isSwipeToNotificationEnabled() { 620 return mIsSwipeToNotificationEnabled; 621 } 622 isPipActive()623 public boolean isPipActive() { 624 return mPipIsActive; 625 } 626 627 /** Returns whether IME is rendering nav buttons, and IME is currently showing. */ isImeRenderingNavButtons()628 public boolean isImeRenderingNavButtons() { 629 return mCanImeRenderGesturalNavButtons && mMode == NO_BUTTON 630 && ((getSysuiStateFlag() & SYSUI_STATE_IME_VISIBLE) != 0); 631 } 632 633 /** 634 * Returns the touch slop for {@link InputConsumer}s to compare against before pilfering 635 * pointers. 636 */ getTouchSlop()637 public float getTouchSlop() { 638 float slopMultiplier = isFullyGesturalNavMode() 639 ? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL 640 : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON; 641 float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); 642 643 if (mContextualSearchStateManager.getLPNHCustomSlopMultiplier().isPresent()) { 644 float customSlopMultiplier = 645 mContextualSearchStateManager.getLPNHCustomSlopMultiplier().get(); 646 return customSlopMultiplier * slopMultiplier * touchSlop; 647 } else { 648 return slopMultiplier * touchSlop; 649 } 650 } 651 652 /** 653 * Returns the squared touch slop for {@link InputConsumer}s to compare against before pilfering 654 * pointers. Note that this is squared because it expects to be compared against 655 * {@link com.android.launcher3.Utilities#squaredHypot} (to avoid square root on each event). 656 */ getSquaredTouchSlop()657 public float getSquaredTouchSlop() { 658 float touchSlop = getTouchSlop(); 659 return touchSlop * touchSlop; 660 } 661 662 /** Returns a string representation of the system ui state flags for the default display. */ getSystemUiStateString()663 public String getSystemUiStateString() { 664 return getSystemUiStateString(getSysuiStateFlag()); 665 } 666 667 /** Returns a string representation of the system ui state flags. */ getSystemUiStateString(long flags)668 public String getSystemUiStateString(long flags) { 669 return QuickStepContract.getSystemUiStateString(flags); 670 } 671 dump(PrintWriter pw)672 public void dump(PrintWriter pw) { 673 pw.println("DeviceState:"); 674 pw.println(" canStartSystemGesture=" + canStartSystemGesture()); 675 pw.println(" systemUiFlagsForDefaultDisplay=" + getSysuiStateFlag()); 676 pw.println(" systemUiFlagsDesc=" + getSystemUiStateString()); 677 pw.println(" assistantAvailable=" + mAssistantAvailable); 678 pw.println(" assistantDisabled=" 679 + QuickStepContract.isAssistantGestureDisabled(getSysuiStateFlag())); 680 pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled); 681 pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled); 682 pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds()); 683 pw.println(" exclusionRegion=" + mExclusionRegion.getBounds()); 684 pw.println(" pipIsActive=" + mPipIsActive); 685 pw.println(" predictiveBackToHomeInProgress=" + mIsPredictiveBackToHomeInProgress); 686 for (int displayId : mSysUIStateFlagsPerDisplay.keySet()) { 687 pw.println(" systemUiFlagsForDisplay" + displayId + "=" + getSystemUiStateFlags( 688 displayId)); 689 pw.println(" systemUiFlagsForDisplay" + displayId + "Desc=" + getSystemUiStateString( 690 getSystemUiStateFlags(displayId))); 691 } 692 pw.println(" RotationTouchHelper:"); 693 mRotationTouchHelper.dump(pw); 694 } 695 } 696