1 /* 2 * Copyright (C) 2020 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 17 package com.android.systemui.navigationbar; 18 19 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; 20 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 21 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; 22 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 23 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 24 import static android.app.StatusBarManager.WindowType; 25 import static android.app.StatusBarManager.WindowVisibleState; 26 import static android.app.StatusBarManager.windowStateToString; 27 import static android.app.WindowConfiguration.ROTATION_UNDEFINED; 28 import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR; 29 import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM; 30 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 31 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 32 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 33 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 34 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; 35 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; 36 37 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; 38 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; 39 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode; 40 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 41 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; 42 import static com.android.systemui.shared.rotation.RotationButtonController.DEBUG_ROTATION; 43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; 44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; 45 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; 46 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; 47 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; 48 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; 49 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; 50 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; 51 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 52 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; 53 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE; 54 import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions; 55 import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay; 56 57 import android.annotation.IdRes; 58 import android.annotation.NonNull; 59 import android.app.ActivityTaskManager; 60 import android.app.IActivityTaskManager; 61 import android.app.StatusBarManager; 62 import android.content.Context; 63 import android.content.Intent; 64 import android.content.res.Configuration; 65 import android.graphics.Insets; 66 import android.graphics.PixelFormat; 67 import android.graphics.Point; 68 import android.graphics.Rect; 69 import android.graphics.RectF; 70 import android.graphics.Region; 71 import android.os.Binder; 72 import android.os.Bundle; 73 import android.os.Handler; 74 import android.os.IBinder; 75 import android.os.RemoteException; 76 import android.os.Trace; 77 import android.provider.DeviceConfig; 78 import android.telecom.TelecomManager; 79 import android.text.TextUtils; 80 import android.util.Log; 81 import android.view.Display; 82 import android.view.Gravity; 83 import android.view.HapticFeedbackConstants; 84 import android.view.InsetsFrameProvider; 85 import android.view.KeyEvent; 86 import android.view.MotionEvent; 87 import android.view.Surface; 88 import android.view.View; 89 import android.view.ViewConfiguration; 90 import android.view.ViewTreeObserver; 91 import android.view.ViewTreeObserver.InternalInsetsInfo; 92 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; 93 import android.view.WindowInsets; 94 import android.view.WindowInsets.Type.InsetsType; 95 import android.view.WindowInsetsController.Appearance; 96 import android.view.WindowInsetsController.Behavior; 97 import android.view.WindowManager; 98 import android.view.accessibility.AccessibilityEvent; 99 import android.view.accessibility.AccessibilityManager; 100 import android.view.inputmethod.InputMethodManager; 101 102 import androidx.annotation.Nullable; 103 import androidx.annotation.VisibleForTesting; 104 105 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; 106 import com.android.internal.logging.MetricsLogger; 107 import com.android.internal.logging.UiEvent; 108 import com.android.internal.logging.UiEventLogger; 109 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 110 import com.android.internal.statusbar.LetterboxDetails; 111 import com.android.internal.util.LatencyTracker; 112 import com.android.internal.view.AppearanceRegion; 113 import com.android.systemui.Gefingerpoken; 114 import com.android.systemui.assist.AssistManager; 115 import com.android.systemui.dagger.qualifiers.Background; 116 import com.android.systemui.dagger.qualifiers.DisplayId; 117 import com.android.systemui.dagger.qualifiers.Main; 118 import com.android.systemui.keyguard.WakefulnessLifecycle; 119 import com.android.systemui.model.SysUiState; 120 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope; 121 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener; 122 import com.android.systemui.navigationbar.buttons.ButtonDispatcher; 123 import com.android.systemui.navigationbar.buttons.DeadZone; 124 import com.android.systemui.navigationbar.buttons.KeyButtonView; 125 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; 126 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle; 127 import com.android.systemui.plugins.statusbar.StatusBarStateController; 128 import com.android.systemui.recents.OverviewProxyService; 129 import com.android.systemui.recents.Recents; 130 import com.android.systemui.res.R; 131 import com.android.systemui.settings.DisplayTracker; 132 import com.android.systemui.settings.UserContextProvider; 133 import com.android.systemui.settings.UserTracker; 134 import com.android.systemui.shade.ShadeViewController; 135 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; 136 import com.android.systemui.shared.navigationbar.RegionSamplingHelper; 137 import com.android.systemui.shared.recents.utilities.Utilities; 138 import com.android.systemui.shared.rotation.RotationButtonController; 139 import com.android.systemui.shared.system.QuickStepContract; 140 import com.android.systemui.shared.system.SysUiStatsLog; 141 import com.android.systemui.shared.system.TaskStackChangeListener; 142 import com.android.systemui.shared.system.TaskStackChangeListeners; 143 import com.android.systemui.statusbar.AutoHideUiElement; 144 import com.android.systemui.statusbar.CommandQueue; 145 import com.android.systemui.statusbar.CommandQueue.Callbacks; 146 import com.android.systemui.statusbar.NotificationRemoteInputManager; 147 import com.android.systemui.statusbar.NotificationShadeDepthController; 148 import com.android.systemui.statusbar.StatusBarState; 149 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 150 import com.android.systemui.statusbar.phone.AutoHideController; 151 import com.android.systemui.statusbar.phone.BarTransitions; 152 import com.android.systemui.statusbar.phone.CentralSurfaces; 153 import com.android.systemui.statusbar.phone.LightBarController; 154 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 155 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 156 import com.android.systemui.statusbar.policy.KeyguardStateController; 157 import com.android.systemui.util.DeviceConfigProxy; 158 import com.android.systemui.util.ViewController; 159 import com.android.wm.shell.back.BackAnimation; 160 import com.android.wm.shell.pip.Pip; 161 162 import dagger.Lazy; 163 164 import java.io.PrintWriter; 165 import java.util.Locale; 166 import java.util.Map; 167 import java.util.Optional; 168 import java.util.concurrent.Executor; 169 170 import javax.inject.Inject; 171 172 /** 173 * Contains logic for a navigation bar view. 174 */ 175 @NavigationBarScope 176 public class NavigationBar extends ViewController<NavigationBarView> implements Callbacks { 177 178 public static final String TAG = "NavigationBar"; 179 private static final boolean DEBUG = false; 180 private static final String EXTRA_DISABLE_STATE = "disabled_state"; 181 private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; 182 private static final String EXTRA_APPEARANCE = "appearance"; 183 private static final String EXTRA_BEHAVIOR = "behavior"; 184 private static final String EXTRA_TRANSIENT_STATE = "transient_state"; 185 186 /** Allow some time inbetween the long press for back and recents. */ 187 private static final int LOCK_TO_APP_GESTURE_TOLERANCE = 200; 188 private static final long AUTODIM_TIMEOUT_MS = 2250; 189 private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f; 190 191 private final Context mContext; 192 private final Bundle mSavedState; 193 private final WindowManager mWindowManager; 194 private final AccessibilityManager mAccessibilityManager; 195 private final DeviceProvisionedController mDeviceProvisionedController; 196 private final StatusBarStateController mStatusBarStateController; 197 private final MetricsLogger mMetricsLogger; 198 private final Lazy<AssistManager> mAssistManagerLazy; 199 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 200 private final SysUiState mSysUiFlagsContainer; 201 private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; 202 private final KeyguardStateController mKeyguardStateController; 203 private final ShadeViewController mShadeViewController; 204 private final PanelExpansionInteractor mPanelExpansionInteractor; 205 private final NotificationRemoteInputManager mNotificationRemoteInputManager; 206 private final OverviewProxyService mOverviewProxyService; 207 private final NavigationModeController mNavigationModeController; 208 private final UserTracker mUserTracker; 209 private final CommandQueue mCommandQueue; 210 private final Optional<Pip> mPipOptional; 211 private final Optional<Recents> mRecentsOptional; 212 private final DeviceConfigProxy mDeviceConfigProxy; 213 private final NavigationBarTransitions mNavigationBarTransitions; 214 private final Optional<BackAnimation> mBackAnimation; 215 private final Handler mHandler; 216 private final UiEventLogger mUiEventLogger; 217 private final NavBarHelper mNavBarHelper; 218 private final NotificationShadeDepthController mNotificationShadeDepthController; 219 private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener; 220 private final UserContextProvider mUserContextProvider; 221 private final WakefulnessLifecycle mWakefulnessLifecycle; 222 private final DisplayTracker mDisplayTracker; 223 private final RegionSamplingHelper mRegionSamplingHelper; 224 private final int mNavColorSampleMargin; 225 private EdgeBackGestureHandler mEdgeBackGestureHandler; 226 private NavigationBarFrame mFrame; 227 private MotionEvent mCurrentDownEvent; 228 229 private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 230 231 private int mNavigationIconHints = 0; 232 private @TransitionMode int mTransitionMode; 233 private boolean mLongPressHomeEnabled; 234 235 private int mDisabledFlags1; 236 private int mDisabledFlags2; 237 private long mLastLockToAppLongPress; 238 239 private Locale mLocale; 240 private int mLayoutDirection; 241 242 private Optional<Long> mHomeButtonLongPressDurationMs; 243 private Optional<Long> mOverrideHomeButtonLongPressDurationMs = Optional.empty(); 244 private Optional<Float> mOverrideHomeButtonLongPressSlopMultiplier = Optional.empty(); 245 private boolean mHomeButtonLongPressHapticEnabled = true; 246 247 /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ 248 private @Appearance int mAppearance; 249 250 /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */ 251 private @Behavior int mBehavior; 252 253 private boolean mTransientShown; 254 private boolean mTransientShownFromGestureOnSystemBar; 255 private int mNavBarMode = NAV_BAR_MODE_3BUTTON; 256 private LightBarController mLightBarController; 257 private final LightBarController mMainLightBarController; 258 private final LightBarController.Factory mLightBarControllerFactory; 259 private AutoHideController mAutoHideController; 260 private final AutoHideController mMainAutoHideController; 261 private final AutoHideController.Factory mAutoHideControllerFactory; 262 private final Optional<TelecomManager> mTelecomManagerOptional; 263 private final InputMethodManager mInputMethodManager; 264 private final TaskStackChangeListeners mTaskStackChangeListeners; 265 266 @VisibleForTesting 267 public int mDisplayId; 268 private boolean mIsOnDefaultDisplay; 269 private boolean mHomeBlockedThisTouch; 270 271 /** 272 * When user is QuickSwitching between apps of different orientations, we'll draw a fake 273 * home handle on the orientation they originally touched down to start their swipe 274 * gesture to indicate to them that they can continue in that orientation without having to 275 * rotate the phone 276 * The secondary handle will show when we get 277 * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the 278 * original handle hidden and we'll flip the visibilities once the 279 * {@link #mTasksFrozenListener} fires 280 */ 281 private QuickswitchOrientedNavHandle mOrientationHandle; 282 private WindowManager.LayoutParams mOrientationParams; 283 private int mStartingQuickSwitchRotation = -1; 284 private int mCurrentRotation; 285 private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener; 286 private boolean mShowOrientedHandleForImmersiveMode; 287 private final DeadZone mDeadZone; 288 private boolean mImeVisible; 289 private final Rect mSamplingBounds = new Rect(); 290 private final Binder mInsetsSourceOwner = new Binder(); 291 private final NavBarButtonClickLogger mNavBarButtonClickLogger; 292 private final NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger; 293 294 /** 295 * When quickswitching between apps of different orientations, we draw a secondary home handle 296 * in the position of the first app's orientation. This rect represents the region of that 297 * home handle so we can apply the correct light/dark luma on that. 298 * @see {@link NavigationBar#mOrientationHandle} 299 */ 300 @android.annotation.Nullable 301 private Rect mOrientedHandleSamplingRegion; 302 303 @com.android.internal.annotations.VisibleForTesting 304 public enum NavBarActionEvent implements UiEventLogger.UiEventEnum { 305 306 @UiEvent(doc = "Assistant invoked via home button long press.") 307 NAVBAR_ASSIST_LONGPRESS(550); 308 309 private final int mId; 310 NavBarActionEvent(int id)311 NavBarActionEvent(int id) { 312 mId = id; 313 } 314 315 @Override getId()316 public int getId() { 317 return mId; 318 } 319 } 320 321 private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() { 322 @Override 323 public void synchronizeState() { 324 checkNavBarModes(); 325 } 326 327 @Override 328 public boolean shouldHideOnTouch() { 329 return !mNotificationRemoteInputManager.isRemoteInputActive(); 330 } 331 332 @Override 333 public boolean isVisible() { 334 return isTransientShown(); 335 } 336 337 @Override 338 public void hide() { 339 clearTransient(); 340 } 341 }; 342 343 private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater = 344 new NavBarHelper.NavbarTaskbarStateUpdater() { 345 @Override 346 public void updateAccessibilityServicesState() { 347 updateAccessibilityStateFlags(); 348 } 349 350 @Override 351 public void updateAssistantAvailable(boolean available, 352 boolean longPressHomeEnabled) { 353 // TODO(b/198002034): Content observers currently can still be called back after 354 // being unregistered, and in this case we can ignore the change if the nav bar 355 // has been destroyed already 356 if (mView == null) { 357 return; 358 } 359 mLongPressHomeEnabled = longPressHomeEnabled; 360 updateAssistantEntrypoints(available, longPressHomeEnabled); 361 } 362 363 @Override 364 public void updateWallpaperVisibility(boolean visible, int displayId) { 365 mNavigationBarTransitions.setWallpaperVisibility(visible); 366 } 367 368 @Override 369 public void updateRotationWatcherState(int rotation) { 370 if (mIsOnDefaultDisplay && mView != null) { 371 mView.getRotationButtonController().onRotationWatcherChanged(rotation); 372 if (mView.needsReorient(rotation)) { 373 repositionNavigationBar(rotation); 374 } 375 } 376 } 377 }; 378 379 private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { 380 @Override 381 public void onConnectionChanged(boolean isConnected) { 382 mView.onOverviewProxyConnectionChange( 383 mOverviewProxyService.isEnabled()); 384 mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); 385 updateScreenPinningGestures(); 386 } 387 388 @Override 389 public void onPrioritizedRotation(@Surface.Rotation int rotation) { 390 mStartingQuickSwitchRotation = rotation; 391 if (rotation == -1) { 392 mShowOrientedHandleForImmersiveMode = false; 393 } 394 orientSecondaryHomeHandle(); 395 } 396 397 @Override 398 public void startAssistant(Bundle bundle) { 399 mAssistManagerLazy.get().startAssist(bundle); 400 } 401 402 @Override 403 public void setAssistantOverridesRequested(int[] invocationTypes) { 404 mAssistManagerLazy.get().setAssistantOverridesRequested(invocationTypes); 405 } 406 407 @Override 408 public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) { 409 mView.getHomeHandle().animateLongPress(isTouchDown, shrink, durationMs); 410 } 411 412 @Override 413 public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier, 414 boolean haptic) { 415 Log.d(TAG, "setOverrideHomeButtonLongPress receives: " + duration + ";" 416 + slopMultiplier + ";" + haptic); 417 mOverrideHomeButtonLongPressDurationMs = Optional.of(duration) 418 .filter(value -> value > 0); 419 mOverrideHomeButtonLongPressSlopMultiplier = Optional.of(slopMultiplier) 420 .filter(value -> value > 0); 421 mHomeButtonLongPressHapticEnabled = haptic; 422 mOverrideHomeButtonLongPressDurationMs.ifPresent(aLong 423 -> Log.d(TAG, "Use duration override: " + aLong)); 424 mOverrideHomeButtonLongPressSlopMultiplier.ifPresent(aFloat 425 -> Log.d(TAG, "Use slop multiplier override: " + aFloat)); 426 if (mView != null) { 427 reconfigureHomeLongClick(); 428 } 429 } 430 431 @Override 432 public void onHomeRotationEnabled(boolean enabled) { 433 mView.getRotationButtonController().setHomeRotationEnabled(enabled); 434 } 435 436 @Override 437 public void onOverviewShown(boolean fromHome) { 438 // If the overview has fixed orientation that may change display to natural rotation, 439 // we don't want the user rotation to be reset. So after user returns to application, 440 // it can keep in the original rotation. 441 mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); 442 } 443 444 @Override 445 public void onTaskbarStatusUpdated(boolean visible, boolean stashed) { 446 mView.getFloatingRotationButton().onTaskbarStateChanged(visible, stashed); 447 } 448 449 @Override 450 public void onToggleRecentApps() { 451 // The same case as onOverviewShown but only for 3-button navigation. 452 mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); 453 } 454 }; 455 456 private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener = 457 new NavigationBarTransitions.DarkIntensityListener() { 458 @Override 459 public void onDarkIntensity(float darkIntensity) { 460 mOrientationHandle.setDarkIntensity(darkIntensity); 461 } 462 }; 463 464 private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); 465 private final Runnable mEnableLayoutTransitions = () -> mView.setLayoutTransitionsEnabled(true); 466 private final Runnable mOnVariableDurationHomeLongClick = () -> { 467 if (onHomeLongClick(mView.getHomeButton().getCurrentView())) { 468 if (mHomeButtonLongPressHapticEnabled) { 469 mView.getHomeButton().getCurrentView().performHapticFeedback( 470 HapticFeedbackConstants.LONG_PRESS, 471 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 472 } 473 } 474 }; 475 476 private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = 477 new DeviceConfig.OnPropertiesChangedListener() { 478 @Override 479 public void onPropertiesChanged(DeviceConfig.Properties properties) { 480 if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) { 481 mHomeButtonLongPressDurationMs = Optional.of( 482 properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)) 483 .filter(duration -> duration != 0); 484 if (mView != null) { 485 reconfigureHomeLongClick(); 486 } 487 } 488 } 489 }; 490 491 private final NotificationShadeDepthController.DepthListener mDepthListener = 492 new NotificationShadeDepthController.DepthListener() { 493 boolean mHasBlurs; 494 495 @Override 496 public void onWallpaperZoomOutChanged(float zoomOut) { 497 } 498 499 @Override 500 public void onBlurRadiusChanged(int radius) { 501 boolean hasBlurs = radius != 0; 502 if (hasBlurs == mHasBlurs) { 503 return; 504 } 505 mHasBlurs = hasBlurs; 506 mRegionSamplingHelper.setWindowHasBlurs(hasBlurs); 507 } 508 }; 509 510 private final WakefulnessLifecycle.Observer mWakefulnessObserver = 511 new WakefulnessLifecycle.Observer() { 512 private void notifyScreenStateChanged(boolean isScreenOn) { 513 notifyNavigationBarScreenOn(); 514 mView.onScreenStateChanged(isScreenOn); 515 } 516 517 @Override 518 public void onStartedWakingUp() { 519 notifyScreenStateChanged(true); 520 if (isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker, 521 mNavBarMode)) { 522 mRegionSamplingHelper.start(mSamplingBounds); 523 } 524 } 525 526 @Override 527 public void onFinishedGoingToSleep() { 528 notifyScreenStateChanged(false); 529 mRegionSamplingHelper.stop(); 530 } 531 }; 532 533 private boolean mScreenPinningActive = false; 534 private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { 535 @Override 536 public void onLockTaskModeChanged(int mode) { 537 mScreenPinningActive = (mode == LOCK_TASK_MODE_PINNED); 538 mSysUiFlagsContainer.setFlag(SYSUI_STATE_SCREEN_PINNING, mScreenPinningActive) 539 .commitUpdate(mDisplayId); 540 mView.setInScreenPinning(mScreenPinningActive); 541 updateScreenPinningGestures(); 542 } 543 }; 544 545 @Inject NavigationBar( NavigationBarView navigationBarView, NavigationBarFrame navigationBarFrame, @Nullable Bundle savedState, @DisplayId Context context, @DisplayId WindowManager windowManager, Lazy<AssistManager> assistManagerLazy, AccessibilityManager accessibilityManager, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, SysUiState sysUiFlagsContainer, UserTracker userTracker, CommandQueue commandQueue, Optional<Pip> pipOptional, Optional<Recents> recentsOptional, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, KeyguardStateController keyguardStateController, ShadeViewController shadeViewController, PanelExpansionInteractor panelExpansionInteractor, NotificationRemoteInputManager notificationRemoteInputManager, NotificationShadeDepthController notificationShadeDepthController, @Main Handler mainHandler, @Main Executor mainExecutor, @Background Executor bgExecutor, UiEventLogger uiEventLogger, NavBarHelper navBarHelper, LightBarController mainLightBarController, LightBarController.Factory lightBarControllerFactory, AutoHideController mainAutoHideController, AutoHideController.Factory autoHideControllerFactory, Optional<TelecomManager> telecomManagerOptional, InputMethodManager inputMethodManager, DeadZone deadZone, DeviceConfigProxy deviceConfigProxy, NavigationBarTransitions navigationBarTransitions, Optional<BackAnimation> backAnimation, UserContextProvider userContextProvider, WakefulnessLifecycle wakefulnessLifecycle, TaskStackChangeListeners taskStackChangeListeners, DisplayTracker displayTracker, NavBarButtonClickLogger navBarButtonClickLogger, NavbarOrientationTrackingLogger navbarOrientationTrackingLogger)546 NavigationBar( 547 NavigationBarView navigationBarView, 548 NavigationBarFrame navigationBarFrame, 549 @Nullable Bundle savedState, 550 @DisplayId Context context, 551 @DisplayId WindowManager windowManager, 552 Lazy<AssistManager> assistManagerLazy, 553 AccessibilityManager accessibilityManager, 554 DeviceProvisionedController deviceProvisionedController, 555 MetricsLogger metricsLogger, 556 OverviewProxyService overviewProxyService, 557 NavigationModeController navigationModeController, 558 StatusBarStateController statusBarStateController, 559 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 560 SysUiState sysUiFlagsContainer, 561 UserTracker userTracker, 562 CommandQueue commandQueue, 563 Optional<Pip> pipOptional, 564 Optional<Recents> recentsOptional, 565 Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, 566 KeyguardStateController keyguardStateController, 567 ShadeViewController shadeViewController, 568 PanelExpansionInteractor panelExpansionInteractor, 569 NotificationRemoteInputManager notificationRemoteInputManager, 570 NotificationShadeDepthController notificationShadeDepthController, 571 @Main Handler mainHandler, 572 @Main Executor mainExecutor, 573 @Background Executor bgExecutor, 574 UiEventLogger uiEventLogger, 575 NavBarHelper navBarHelper, 576 LightBarController mainLightBarController, 577 LightBarController.Factory lightBarControllerFactory, 578 AutoHideController mainAutoHideController, 579 AutoHideController.Factory autoHideControllerFactory, 580 Optional<TelecomManager> telecomManagerOptional, 581 InputMethodManager inputMethodManager, 582 DeadZone deadZone, 583 DeviceConfigProxy deviceConfigProxy, 584 NavigationBarTransitions navigationBarTransitions, 585 Optional<BackAnimation> backAnimation, 586 UserContextProvider userContextProvider, 587 WakefulnessLifecycle wakefulnessLifecycle, 588 TaskStackChangeListeners taskStackChangeListeners, 589 DisplayTracker displayTracker, 590 NavBarButtonClickLogger navBarButtonClickLogger, 591 NavbarOrientationTrackingLogger navbarOrientationTrackingLogger) { 592 super(navigationBarView); 593 mFrame = navigationBarFrame; 594 mContext = context; 595 mSavedState = savedState; 596 mWindowManager = windowManager; 597 mAccessibilityManager = accessibilityManager; 598 mDeviceProvisionedController = deviceProvisionedController; 599 mStatusBarStateController = statusBarStateController; 600 mMetricsLogger = metricsLogger; 601 mAssistManagerLazy = assistManagerLazy; 602 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 603 mSysUiFlagsContainer = sysUiFlagsContainer; 604 mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy; 605 mKeyguardStateController = keyguardStateController; 606 mShadeViewController = shadeViewController; 607 mPanelExpansionInteractor = panelExpansionInteractor; 608 mNotificationRemoteInputManager = notificationRemoteInputManager; 609 mOverviewProxyService = overviewProxyService; 610 mNavigationModeController = navigationModeController; 611 mUserTracker = userTracker; 612 mCommandQueue = commandQueue; 613 mPipOptional = pipOptional; 614 mRecentsOptional = recentsOptional; 615 mDeadZone = deadZone; 616 mDeviceConfigProxy = deviceConfigProxy; 617 mNavigationBarTransitions = navigationBarTransitions; 618 mBackAnimation = backAnimation; 619 mHandler = mainHandler; 620 mUiEventLogger = uiEventLogger; 621 mNavBarHelper = navBarHelper; 622 mNotificationShadeDepthController = notificationShadeDepthController; 623 mMainLightBarController = mainLightBarController; 624 mLightBarControllerFactory = lightBarControllerFactory; 625 mMainAutoHideController = mainAutoHideController; 626 mAutoHideControllerFactory = autoHideControllerFactory; 627 mTelecomManagerOptional = telecomManagerOptional; 628 mInputMethodManager = inputMethodManager; 629 mUserContextProvider = userContextProvider; 630 mWakefulnessLifecycle = wakefulnessLifecycle; 631 mTaskStackChangeListeners = taskStackChangeListeners; 632 mDisplayTracker = displayTracker; 633 mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler(); 634 mNavBarButtonClickLogger = navBarButtonClickLogger; 635 mNavbarOrientationTrackingLogger = navbarOrientationTrackingLogger; 636 637 mNavColorSampleMargin = getResources() 638 .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin); 639 640 mOnComputeInternalInsetsListener = info -> { 641 // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully 642 // gestural mode, the entire nav bar should be touchable. 643 if (!mEdgeBackGestureHandler.isHandlingGestures()) { 644 // We're in 2/3 button mode OR back button force-shown in SUW 645 if (!mImeVisible) { 646 // IME not showing, take all touches 647 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 648 return; 649 } 650 if (!mView.isImeRenderingNavButtons()) { 651 // IME showing but not drawing any buttons, take all touches 652 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 653 return; 654 } 655 } 656 657 // When in gestural and the IME is showing, don't use the nearest region since it will 658 // take gesture space away from the IME 659 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 660 info.touchableRegion.set( 661 getButtonLocations(false /* inScreen */, false /* useNearestRegion */)); 662 }; 663 664 mRegionSamplingHelper = new RegionSamplingHelper(mView, 665 new RegionSamplingHelper.SamplingCallback() { 666 @Override 667 public void onRegionDarknessChanged(boolean isRegionDark) { 668 getBarTransitions().getLightTransitionsController().setIconsDark( 669 !isRegionDark, true /* animate */); 670 } 671 672 @Override 673 public Rect getSampledRegion(View sampledView) { 674 if (mOrientedHandleSamplingRegion != null) { 675 return mOrientedHandleSamplingRegion; 676 } 677 678 return calculateSamplingRect(); 679 } 680 681 @Override 682 public boolean isSamplingEnabled() { 683 return isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker, 684 mNavBarMode); 685 } 686 }, mainExecutor, bgExecutor); 687 688 mView.setBackgroundExecutor(bgExecutor); 689 mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler); 690 mView.setDisplayTracker(mDisplayTracker); 691 mNavBarMode = mNavigationModeController.addListener(mModeChangedListener); 692 } 693 getView()694 public NavigationBarView getView() { 695 return mView; 696 } 697 698 @Override onInit()699 public void onInit() { 700 // TODO: A great deal of this code should probably live in onViewAttached. 701 // It should also has corresponding cleanup in onViewDetached. 702 mView.setBarTransitions(mNavigationBarTransitions); 703 mView.setTouchHandler(mTouchHandler); 704 setNavBarMode(mNavBarMode); 705 mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates); 706 mEdgeBackGestureHandler.setButtonForcedVisibleChangeCallback((forceVisible) -> { 707 repositionNavigationBar(mCurrentRotation); 708 }); 709 mNavigationBarTransitions.addListener(this::onBarTransition); 710 mView.updateRotationButton(); 711 712 mView.setVisibility( 713 mStatusBarKeyguardViewManager.isNavBarVisible() ? View.VISIBLE : View.INVISIBLE); 714 715 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView); 716 717 mWindowManager.addView(mFrame, 718 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration 719 .getRotation())); 720 mDisplayId = mContext.getDisplayId(); 721 mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId(); 722 723 // Ensure we try to get currentSysuiState from navBarHelper before command queue callbacks 724 // start firing, since the latter is source of truth 725 parseCurrentSysuiState(); 726 mCommandQueue.addCallback(this); 727 mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong( 728 DeviceConfig.NAMESPACE_SYSTEMUI, 729 HOME_BUTTON_LONG_PRESS_DURATION_MS, 730 /* defaultValue = */ 0 731 )).filter(duration -> duration != 0); 732 // This currently MUST be called after mHomeButtonLongPressDurationMs is initialized since 733 // the registration callbacks will trigger code that uses it 734 mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); 735 mDeviceConfigProxy.addOnPropertiesChangedListener( 736 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); 737 738 if (mSavedState != null) { 739 mDisabledFlags1 = mSavedState.getInt(EXTRA_DISABLE_STATE, 0); 740 mDisabledFlags2 = mSavedState.getInt(EXTRA_DISABLE2_STATE, 0); 741 mAppearance = mSavedState.getInt(EXTRA_APPEARANCE, 0); 742 mBehavior = mSavedState.getInt(EXTRA_BEHAVIOR, 0); 743 mTransientShown = mSavedState.getBoolean(EXTRA_TRANSIENT_STATE, false); 744 } 745 746 // Respect the latest disabled-flags. 747 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 748 749 mNotificationShadeDepthController.addListener(mDepthListener); 750 mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener); 751 } 752 destroyView()753 public void destroyView() { 754 Trace.beginSection("NavigationBar#destroyView"); 755 try { 756 setAutoHideController(/* autoHideController */ null); 757 mCommandQueue.removeCallback(this); 758 Trace.beginSection("NavigationBar#removeViewImmediate"); 759 try { 760 mWindowManager.removeViewImmediate(mView.getRootView()); 761 } finally { 762 Trace.endSection(); 763 } 764 mNavigationModeController.removeListener(mModeChangedListener); 765 mEdgeBackGestureHandler.setStateChangeCallback(null); 766 767 mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); 768 mNotificationShadeDepthController.removeListener(mDepthListener); 769 770 mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); 771 mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener); 772 } finally { 773 Trace.endSection(); 774 } 775 } 776 777 @Override onViewAttached()778 public void onViewAttached() { 779 final Display display = mView.getDisplay(); 780 mView.setComponents(mRecentsOptional); 781 if (mCentralSurfacesOptionalLazy.get().isPresent()) { 782 mView.setComponents(mShadeViewController, mPanelExpansionInteractor); 783 } 784 mView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer); 785 mView.setOnVerticalChangedListener(this::onVerticalChanged); 786 mView.setOnTouchListener(this::onNavigationTouch); 787 if (mSavedState != null) { 788 getBarTransitions().getLightTransitionsController().restoreState(mSavedState); 789 } 790 setNavigationIconHints(mNavigationIconHints); 791 setWindowVisible(isNavBarWindowVisible()); 792 mView.setBehavior(mBehavior); 793 setNavBarMode(mNavBarMode); 794 repositionNavigationBar(mCurrentRotation); 795 mView.setUpdateActiveTouchRegionsCallback( 796 () -> mOverviewProxyService.onActiveNavBarRegionChanges( 797 getButtonLocations(true /* inScreen */, true /* useNearestRegion */))); 798 799 mView.getViewTreeObserver().addOnComputeInternalInsetsListener( 800 mOnComputeInternalInsetsListener); 801 802 mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener); 803 mBackAnimation.ifPresent(mView::registerBackAnimation); 804 805 prepareNavigationBarView(); 806 checkNavBarModes(); 807 808 mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); 809 mWakefulnessLifecycle.addObserver(mWakefulnessObserver); 810 notifyNavigationBarScreenOn(); 811 812 mOverviewProxyService.addCallback(mOverviewProxyListener); 813 updateSystemUiStateFlags(); 814 815 // Currently there is no accelerometer sensor on non-default display. 816 if (mIsOnDefaultDisplay) { 817 final RotationButtonController rotationButtonController = 818 mView.getRotationButtonController(); 819 820 // Reset user rotation pref to match that of the WindowManager if starting in locked 821 // mode. This will automatically happen when switching from auto-rotate to locked mode. 822 if (display != null && rotationButtonController.isRotationLocked()) { 823 rotationButtonController.setRotationLockedAtAngle( 824 display.getRotation(), /* caller= */ "NavigationBar#onViewAttached"); 825 } 826 } else { 827 mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS; 828 } 829 setDisabled2Flags(mDisabledFlags2); 830 831 initSecondaryHomeHandleForRotation(); 832 833 // Unfortunately, we still need it because status bar needs LightBarController 834 // before notifications creation. We cannot directly use getLightBarController() 835 // from NavigationBarFragment directly. 836 LightBarController lightBarController = mIsOnDefaultDisplay 837 ? mMainLightBarController : mLightBarControllerFactory.create(mContext); 838 setLightBarController(lightBarController); 839 840 // TODO(b/118592525): to support multi-display, we start to add something which is 841 // per-display, while others may be global. I think it's time to 842 // add a new class maybe named DisplayDependency to solve 843 // per-display Dependency problem. 844 // Alternative: this is a good case for a Dagger subcomponent. Same with LightBarController. 845 AutoHideController autoHideController = mIsOnDefaultDisplay 846 ? mMainAutoHideController : mAutoHideControllerFactory.create(mContext); 847 setAutoHideController(autoHideController); 848 restoreAppearanceAndTransientState(); 849 } 850 851 @Override onViewDetached()852 public void onViewDetached() { 853 mView.setUpdateActiveTouchRegionsCallback(null); 854 getBarTransitions().destroy(); 855 mOverviewProxyService.removeCallback(mOverviewProxyListener); 856 mUserTracker.removeCallback(mUserChangedCallback); 857 mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); 858 if (mOrientationHandle != null) { 859 resetSecondaryHandle(); 860 getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); 861 mWindowManager.removeView(mOrientationHandle); 862 mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener( 863 mOrientationHandleGlobalLayoutListener); 864 } 865 mView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 866 mOnComputeInternalInsetsListener); 867 mHandler.removeCallbacks(mAutoDim); 868 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 869 mHandler.removeCallbacks(mEnableLayoutTransitions); 870 mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); 871 mPipOptional.ifPresent(mView::removePipExclusionBoundsChangeListener); 872 mFrame = null; 873 mOrientationHandle = null; 874 } 875 876 // TODO: Remove this when we update nav bar recreation onSaveInstanceState(Bundle outState)877 public void onSaveInstanceState(Bundle outState) { 878 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); 879 outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); 880 outState.putInt(EXTRA_APPEARANCE, mAppearance); 881 outState.putInt(EXTRA_BEHAVIOR, mBehavior); 882 outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); 883 getBarTransitions().getLightTransitionsController().saveState(outState); 884 } 885 886 /** 887 * Called when a non-reloading configuration change happens and we need to update. 888 */ onConfigurationChanged(Configuration newConfig)889 public void onConfigurationChanged(Configuration newConfig) { 890 final int rotation = newConfig.windowConfiguration.getRotation(); 891 final Locale locale = mContext.getResources().getConfiguration().locale; 892 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 893 if (!locale.equals(mLocale) || ld != mLayoutDirection) { 894 if (DEBUG) { 895 Log.v(TAG, String.format( 896 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 897 locale, ld)); 898 } 899 mLocale = locale; 900 mLayoutDirection = ld; 901 refreshLayout(ld); 902 } 903 repositionNavigationBar(rotation); 904 if (canShowSecondaryHandle()) { 905 if (rotation != mCurrentRotation) { 906 mCurrentRotation = rotation; 907 orientSecondaryHomeHandle(); 908 } 909 } 910 } 911 initSecondaryHomeHandleForRotation()912 private void initSecondaryHomeHandleForRotation() { 913 if (mNavBarMode != NAV_BAR_MODE_GESTURAL) { 914 return; 915 } 916 917 mOrientationHandle = new QuickswitchOrientedNavHandle(mContext); 918 mOrientationHandle.setId(R.id.secondary_home_handle); 919 920 getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener); 921 mOrientationParams = new WindowManager.LayoutParams(0, 0, 922 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 923 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 924 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 925 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 926 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 927 | WindowManager.LayoutParams.FLAG_SLIPPERY, 928 PixelFormat.TRANSLUCENT); 929 mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); 930 mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION 931 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; 932 mWindowManager.addView(mOrientationHandle, mOrientationParams); 933 mOrientationHandle.setVisibility(View.GONE); 934 935 logNavbarOrientation("initSecondaryHomeHandleForRotation"); 936 mOrientationParams.setFitInsetsTypes(0 /* types*/); 937 mOrientationHandleGlobalLayoutListener = 938 () -> { 939 if (mStartingQuickSwitchRotation == -1) { 940 return; 941 } 942 943 RectF boundsOnScreen = mOrientationHandle.computeHomeHandleBounds(); 944 mOrientationHandle.mapRectFromViewToScreenCoords(boundsOnScreen, true); 945 Rect boundsRounded = new Rect(); 946 boundsOnScreen.roundOut(boundsRounded); 947 setOrientedHandleSamplingRegion(boundsRounded); 948 }; 949 mOrientationHandle.getViewTreeObserver().addOnGlobalLayoutListener( 950 mOrientationHandleGlobalLayoutListener); 951 } 952 orientSecondaryHomeHandle()953 private void orientSecondaryHomeHandle() { 954 if (!canShowSecondaryHandle()) { 955 return; 956 } 957 958 if (mStartingQuickSwitchRotation == -1) { 959 resetSecondaryHandle(); 960 } else { 961 int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation); 962 if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) { 963 // Curious if starting quickswitch can change between the if check and our delta 964 Log.d(TAG, "secondary nav delta rotation: " + deltaRotation 965 + " current: " + mCurrentRotation 966 + " starting: " + mStartingQuickSwitchRotation); 967 } 968 int height = 0; 969 int width = 0; 970 Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds(); 971 mOrientationHandle.setDeltaRotation(deltaRotation); 972 switch (deltaRotation) { 973 case Surface.ROTATION_90: 974 case Surface.ROTATION_270: 975 height = dispSize.height(); 976 width = mView.getHeight(); 977 break; 978 case Surface.ROTATION_180: 979 case Surface.ROTATION_0: 980 // TODO(b/152683657): Need to determine best UX for this 981 if (!mShowOrientedHandleForImmersiveMode) { 982 resetSecondaryHandle(); 983 return; 984 } 985 width = dispSize.width(); 986 height = mView.getHeight(); 987 break; 988 } 989 990 mOrientationParams.gravity = 991 deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM : 992 (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT); 993 mOrientationParams.height = height; 994 mOrientationParams.width = width; 995 mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams); 996 mView.setVisibility(View.GONE); 997 mOrientationHandle.setVisibility(View.VISIBLE); 998 logNavbarOrientation("orientSecondaryHomeHandle"); 999 } 1000 } 1001 resetSecondaryHandle()1002 private void resetSecondaryHandle() { 1003 if (mOrientationHandle != null) { 1004 // Case where nav mode is changed w/o ever invoking a quickstep 1005 // mOrientedHandle is initialized lazily 1006 mOrientationHandle.setVisibility(View.GONE); 1007 } 1008 mView.setVisibility(View.VISIBLE); 1009 logNavbarOrientation("resetSecondaryHandle"); 1010 setOrientedHandleSamplingRegion(null); 1011 } 1012 1013 /** 1014 * Logging method for issues concerning Navbar/secondary handle visibility. 1015 */ logNavbarOrientation(String methodName)1016 private void logNavbarOrientation(String methodName) { 1017 boolean isViewVisible = (mView != null) && (mView.getVisibility() == View.VISIBLE); 1018 boolean isSecondaryHandleVisible = 1019 (mOrientationHandle != null) && (mOrientationHandle.getVisibility() 1020 == View.VISIBLE); 1021 mNavbarOrientationTrackingLogger.logPrimaryAndSecondaryVisibility(methodName, isViewVisible, 1022 mShowOrientedHandleForImmersiveMode, isSecondaryHandleVisible, mCurrentRotation, 1023 mStartingQuickSwitchRotation); 1024 } 1025 parseCurrentSysuiState()1026 private void parseCurrentSysuiState() { 1027 NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState(); 1028 if (state.mWindowStateDisplayId == mDisplayId) { 1029 mNavigationBarWindowState = state.mWindowState; 1030 } 1031 } 1032 reconfigureHomeLongClick()1033 private void reconfigureHomeLongClick() { 1034 if (mView.getHomeButton().getCurrentView() == null) { 1035 return; 1036 } 1037 if (mHomeButtonLongPressDurationMs.isPresent() 1038 || mOverrideHomeButtonLongPressDurationMs.isPresent() 1039 || mOverrideHomeButtonLongPressSlopMultiplier.isPresent() 1040 || !mLongPressHomeEnabled) { 1041 mView.getHomeButton().getCurrentView().setLongClickable(false); 1042 mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false); 1043 mView.getHomeButton().setOnLongClickListener(null); 1044 } else { 1045 mView.getHomeButton().getCurrentView().setLongClickable(true); 1046 mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled( 1047 mHomeButtonLongPressHapticEnabled); 1048 mView.getHomeButton().setOnLongClickListener(this::onHomeLongClick); 1049 } 1050 } 1051 deltaRotation(int oldRotation, int newRotation)1052 private int deltaRotation(int oldRotation, int newRotation) { 1053 int delta = newRotation - oldRotation; 1054 if (delta < 0) delta += 4; 1055 return delta; 1056 } 1057 dump(PrintWriter pw)1058 public void dump(PrintWriter pw) { 1059 pw.println("NavigationBar (displayId=" + mDisplayId + "):"); 1060 pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); 1061 pw.println(" mCurrentRotation=" + mCurrentRotation); 1062 pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); 1063 pw.println(" mOverrideHomeButtonLongPressDurationMs=" 1064 + mOverrideHomeButtonLongPressDurationMs); 1065 pw.println(" mOverrideHomeButtonLongPressSlopMultiplier=" 1066 + mOverrideHomeButtonLongPressSlopMultiplier); 1067 pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled); 1068 pw.println(" mNavigationBarWindowState=" 1069 + windowStateToString(mNavigationBarWindowState)); 1070 pw.println(" mTransitionMode=" 1071 + BarTransitions.modeToString(mTransitionMode)); 1072 pw.println(" mTransientShown=" + mTransientShown); 1073 pw.println(" mTransientShownFromGestureOnSystemBar=" 1074 + mTransientShownFromGestureOnSystemBar); 1075 pw.println(" mScreenPinningActive=" + mScreenPinningActive); 1076 dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions()); 1077 1078 pw.println(" mOrientedHandleSamplingRegion: " + mOrientedHandleSamplingRegion); 1079 mView.dump(pw); 1080 mRegionSamplingHelper.dump(pw); 1081 if (mAutoHideController != null) { 1082 mAutoHideController.dump(pw); 1083 } 1084 } 1085 1086 // ----- CommandQueue Callbacks ----- 1087 1088 @Override setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)1089 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, 1090 boolean showImeSwitcher) { 1091 if (displayId != mDisplayId) { 1092 return; 1093 } 1094 boolean imeShown = mNavBarHelper.isImeShown(vis); 1095 showImeSwitcher = imeShown && showImeSwitcher; 1096 int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition, 1097 imeShown, showImeSwitcher); 1098 if (hints == mNavigationIconHints) return; 1099 1100 setNavigationIconHints(hints); 1101 checkBarModes(); 1102 updateSystemUiStateFlags(); 1103 } 1104 1105 @Override setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state)1106 public void setWindowState( 1107 int displayId, @WindowType int window, @WindowVisibleState int state) { 1108 if (displayId == mDisplayId 1109 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 1110 && mNavigationBarWindowState != state) { 1111 mNavigationBarWindowState = state; 1112 updateSystemUiStateFlags(); 1113 mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN; 1114 if (mOrientationHandle != null 1115 && mStartingQuickSwitchRotation != -1) { 1116 orientSecondaryHomeHandle(); 1117 } 1118 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 1119 setWindowVisible(isNavBarWindowVisible()); 1120 } 1121 } 1122 1123 @Override onRotationProposal(final int rotation, boolean isValid)1124 public void onRotationProposal(final int rotation, boolean isValid) { 1125 // The CommandQueue callbacks are added when the view is created to ensure we track other 1126 // states, but until the view is attached (at the next traversal), the view's display is 1127 // not valid. Just ignore the rotation in this case. 1128 if (!mView.isAttachedToWindow()) return; 1129 1130 final boolean rotateSuggestionsDisabled = RotationButtonController 1131 .hasDisable2RotateSuggestionFlag(mDisabledFlags2); 1132 final RotationButtonController rotationButtonController = 1133 mView.getRotationButtonController(); 1134 if (DEBUG_ROTATION) { 1135 Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation) 1136 + ", isValid=" + isValid + ", mNavBarWindowState=" 1137 + StatusBarManager.windowStateToString(mNavigationBarWindowState) 1138 + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled 1139 + ", isRotateButtonVisible=" 1140 + rotationButtonController.getRotationButton().isVisible()); 1141 } 1142 // Respect the disabled flag, no need for action as flag change callback will handle hiding 1143 if (rotateSuggestionsDisabled) return; 1144 1145 rotationButtonController.onRotationProposal(rotation, isValid); 1146 } 1147 1148 @Override onRecentsAnimationStateChanged(boolean running)1149 public void onRecentsAnimationStateChanged(boolean running) { 1150 mView.getRotationButtonController().setRecentsAnimationRunning(running); 1151 } 1152 1153 /** Restores the appearance and the transient saved state to {@link NavigationBar}. */ restoreAppearanceAndTransientState()1154 public void restoreAppearanceAndTransientState() { 1155 final int transitionMode = transitionMode(mTransientShown, mAppearance); 1156 mTransitionMode = transitionMode; 1157 checkNavBarModes(); 1158 if (mAutoHideController != null) { 1159 mAutoHideController.touchAutoHide(); 1160 } 1161 if (mLightBarController != null) { 1162 mLightBarController.onNavigationBarAppearanceChanged(mAppearance, 1163 true /* nbModeChanged */, transitionMode, false /* navbarColorManagedByIme */); 1164 } 1165 } 1166 1167 @Override onSystemBarAttributesChanged(int displayId, @Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName, LetterboxDetails[] letterboxDetails)1168 public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, 1169 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, 1170 @Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName, 1171 LetterboxDetails[] letterboxDetails) { 1172 if (displayId != mDisplayId) { 1173 return; 1174 } 1175 boolean nbModeChanged = false; 1176 if (mAppearance != appearance) { 1177 mAppearance = appearance; 1178 nbModeChanged = updateTransitionMode(transitionMode(mTransientShown, appearance)); 1179 } 1180 if (mLightBarController != null) { 1181 mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, 1182 mTransitionMode, navbarColorManagedByIme); 1183 } 1184 if (mBehavior != behavior) { 1185 mBehavior = behavior; 1186 mView.setBehavior(behavior); 1187 updateSystemUiStateFlags(); 1188 } 1189 } 1190 1191 @Override showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar)1192 public void showTransient(int displayId, @InsetsType int types, boolean isGestureOnSystemBar) { 1193 if (displayId != mDisplayId) { 1194 return; 1195 } 1196 if ((types & WindowInsets.Type.navigationBars()) == 0) { 1197 return; 1198 } 1199 if (!mTransientShown) { 1200 mTransientShown = true; 1201 mTransientShownFromGestureOnSystemBar = isGestureOnSystemBar; 1202 handleTransientChanged(); 1203 } 1204 } 1205 1206 @Override abortTransient(int displayId, @InsetsType int types)1207 public void abortTransient(int displayId, @InsetsType int types) { 1208 if (displayId != mDisplayId) { 1209 return; 1210 } 1211 if ((types & WindowInsets.Type.navigationBars()) == 0) { 1212 return; 1213 } 1214 clearTransient(); 1215 } 1216 clearTransient()1217 private void clearTransient() { 1218 if (mTransientShown) { 1219 mTransientShown = false; 1220 mTransientShownFromGestureOnSystemBar = false; 1221 handleTransientChanged(); 1222 } 1223 } 1224 handleTransientChanged()1225 private void handleTransientChanged() { 1226 mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTransientShown); 1227 1228 final int transitionMode = transitionMode(mTransientShown, mAppearance); 1229 if (updateTransitionMode(transitionMode) && mLightBarController != null) { 1230 mLightBarController.onNavigationBarModeChanged(transitionMode); 1231 } 1232 } 1233 1234 // Returns true if the bar mode is changed. updateTransitionMode(int barMode)1235 private boolean updateTransitionMode(int barMode) { 1236 if (mTransitionMode != barMode) { 1237 mTransitionMode = barMode; 1238 checkNavBarModes(); 1239 if (mAutoHideController != null) { 1240 mAutoHideController.touchAutoHide(); 1241 } 1242 return true; 1243 } 1244 return false; 1245 } 1246 1247 @Override disable(int displayId, int state1, int state2, boolean animate)1248 public void disable(int displayId, int state1, int state2, boolean animate) { 1249 if (displayId != mDisplayId) { 1250 return; 1251 } 1252 // Navigation bar flags are in both state1 and state2. 1253 final int masked = state1 & (StatusBarManager.DISABLE_HOME 1254 | StatusBarManager.DISABLE_RECENT 1255 | StatusBarManager.DISABLE_BACK 1256 | StatusBarManager.DISABLE_SEARCH); 1257 if (masked != mDisabledFlags1) { 1258 mDisabledFlags1 = masked; 1259 mView.setDisabledFlags(state1, mSysUiFlagsContainer); 1260 updateScreenPinningGestures(); 1261 } 1262 1263 // Only default display supports rotation suggestions. 1264 if (mIsOnDefaultDisplay) { 1265 final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS); 1266 if (masked2 != mDisabledFlags2) { 1267 mDisabledFlags2 = masked2; 1268 setDisabled2Flags(masked2); 1269 } 1270 } 1271 } 1272 setDisabled2Flags(int state2)1273 private void setDisabled2Flags(int state2) { 1274 // Method only called on change of disable2 flags 1275 mView.getRotationButtonController().onDisable2FlagChanged(state2); 1276 } 1277 1278 // ----- Internal stuff ----- 1279 refreshLayout(int layoutDirection)1280 private void refreshLayout(int layoutDirection) { 1281 mView.setLayoutDirection(layoutDirection); 1282 } 1283 shouldDisableNavbarGestures()1284 private boolean shouldDisableNavbarGestures() { 1285 return !mDeviceProvisionedController.isDeviceProvisioned() 1286 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; 1287 } 1288 repositionNavigationBar(int rotation)1289 private void repositionNavigationBar(int rotation) { 1290 if (mView == null || !mView.isAttachedToWindow()) return; 1291 1292 prepareNavigationBarView(); 1293 1294 mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation)); 1295 } 1296 updateScreenPinningGestures()1297 private void updateScreenPinningGestures() { 1298 // Change the cancel pin gesture to home and back if recents button is invisible 1299 ButtonDispatcher backButton = mView.getBackButton(); 1300 ButtonDispatcher recentsButton = mView.getRecentsButton(); 1301 if (mScreenPinningActive) { 1302 boolean recentsVisible = mView.isRecentsButtonVisible(); 1303 backButton.setOnLongClickListener(recentsVisible 1304 ? this::onLongPressBackRecents 1305 : this::onLongPressBackHome); 1306 recentsButton.setOnLongClickListener(this::onLongPressBackRecents); 1307 } else { 1308 backButton.setOnLongClickListener(null); 1309 recentsButton.setOnLongClickListener(null); 1310 } 1311 // Note, this needs to be set after even if we're setting the listener to null 1312 backButton.setLongClickable(mScreenPinningActive); 1313 recentsButton.setLongClickable(mScreenPinningActive); 1314 } 1315 notifyNavigationBarScreenOn()1316 private void notifyNavigationBarScreenOn() { 1317 mView.updateNavButtonIcons(); 1318 } 1319 prepareNavigationBarView()1320 private void prepareNavigationBarView() { 1321 mView.reorient(); 1322 1323 ButtonDispatcher recentsButton = mView.getRecentsButton(); 1324 recentsButton.setOnClickListener(this::onRecentsClick); 1325 recentsButton.setOnTouchListener(this::onRecentsTouch); 1326 1327 ButtonDispatcher homeButton = mView.getHomeButton(); 1328 homeButton.setOnTouchListener(this::onHomeTouch); 1329 homeButton.setNavBarButtonClickLogger(mNavBarButtonClickLogger); 1330 1331 ButtonDispatcher backButton = mView.getBackButton(); 1332 backButton.setNavBarButtonClickLogger(mNavBarButtonClickLogger); 1333 1334 reconfigureHomeLongClick(); 1335 1336 ButtonDispatcher accessibilityButton = mView.getAccessibilityButton(); 1337 accessibilityButton.setOnClickListener(this::onAccessibilityClick); 1338 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); 1339 updateAccessibilityStateFlags(); 1340 1341 ButtonDispatcher imeSwitcherButton = mView.getImeSwitchButton(); 1342 imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick); 1343 1344 updateScreenPinningGestures(); 1345 } 1346 1347 @VisibleForTesting onHomeTouch(View v, MotionEvent event)1348 boolean onHomeTouch(View v, MotionEvent event) { 1349 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 1350 return true; 1351 } 1352 // If an incoming call is ringing, HOME is totally disabled. 1353 // (The user is already on the InCallUI at this point, 1354 // and their ONLY options are to answer or reject the call.) 1355 final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get(); 1356 switch (event.getAction()) { 1357 case MotionEvent.ACTION_DOWN: 1358 if (mCurrentDownEvent != null) { 1359 mCurrentDownEvent.recycle(); 1360 } 1361 mCurrentDownEvent = MotionEvent.obtain(event); 1362 mHomeBlockedThisTouch = false; 1363 if (mTelecomManagerOptional.isPresent() 1364 && mTelecomManagerOptional.get().isRinging()) { 1365 if (mKeyguardStateController.isShowing()) { 1366 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + 1367 "No heads up"); 1368 mHomeBlockedThisTouch = true; 1369 return true; 1370 } 1371 } 1372 if (mLongPressHomeEnabled) { 1373 if (mOverrideHomeButtonLongPressDurationMs.isPresent()) { 1374 Log.d(TAG, "ACTION_DOWN Launcher override duration: " 1375 + mOverrideHomeButtonLongPressDurationMs.get()); 1376 mHandler.postDelayed(mOnVariableDurationHomeLongClick, 1377 mOverrideHomeButtonLongPressDurationMs.get()); 1378 } else if (mOverrideHomeButtonLongPressSlopMultiplier.isPresent()) { 1379 // If override timeout doesn't exist but override touch slop exists, we use 1380 // system default long press duration 1381 Log.d(TAG, "ACTION_DOWN default duration: " 1382 + ViewConfiguration.getLongPressTimeout()); 1383 mHandler.postDelayed(mOnVariableDurationHomeLongClick, 1384 ViewConfiguration.getLongPressTimeout()); 1385 } else { 1386 mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> { 1387 Log.d(TAG, "ACTION_DOWN original duration: " + longPressDuration); 1388 mHandler.postDelayed(mOnVariableDurationHomeLongClick, 1389 longPressDuration); 1390 }); 1391 } 1392 } 1393 break; 1394 case MotionEvent.ACTION_MOVE: 1395 if (!mHandler.hasCallbacks(mOnVariableDurationHomeLongClick)) { 1396 Log.v(TAG, "ACTION_MOVE no callback. Don't handle touch slop."); 1397 break; 1398 } 1399 Log.v(TAG, "ACTION_MOVE handle touch slop"); 1400 float customSlopMultiplier = mOverrideHomeButtonLongPressSlopMultiplier.orElse(1f); 1401 float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); 1402 float calculatedTouchSlop = 1403 customSlopMultiplier * QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON * touchSlop; 1404 float touchSlopSquared = calculatedTouchSlop * calculatedTouchSlop; 1405 1406 float dx = event.getX() - mCurrentDownEvent.getX(); 1407 float dy = event.getY() - mCurrentDownEvent.getY(); 1408 double distanceSquared = (dx * dx) + (dy * dy); 1409 if (distanceSquared > touchSlopSquared) { 1410 Log.i(TAG, "Touch slop passed. Abort."); 1411 mView.abortCurrentGesture(); 1412 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 1413 } 1414 break; 1415 case MotionEvent.ACTION_UP: 1416 case MotionEvent.ACTION_CANCEL: 1417 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 1418 centralSurfacesOptional.ifPresent(CentralSurfaces::awakenDreams); 1419 break; 1420 } 1421 return false; 1422 } 1423 onVerticalChanged(boolean isVertical)1424 private void onVerticalChanged(boolean isVertical) { 1425 // This check can probably be safely removed. It only remained to reduce regression 1426 // risk for a broad change that removed the CentralSurfaces reference in the if block 1427 if (mCentralSurfacesOptionalLazy.get().isPresent()) { 1428 mShadeViewController.setQsScrimEnabled(!isVertical); 1429 } 1430 } 1431 onNavigationTouch(View v, MotionEvent event)1432 private boolean onNavigationTouch(View v, MotionEvent event) { 1433 if (mAutoHideController != null) { 1434 mAutoHideController.checkUserAutoHide(event); 1435 } 1436 return false; 1437 } 1438 1439 @VisibleForTesting onHomeLongClick(View v)1440 boolean onHomeLongClick(View v) { 1441 if (!mView.isRecentsButtonVisible() && mScreenPinningActive) { 1442 return onLongPressBackHome(v); 1443 } 1444 if (shouldDisableNavbarGestures()) { 1445 return false; 1446 } 1447 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS); 1448 mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS); 1449 Bundle args = new Bundle(); 1450 args.putInt( 1451 AssistManager.INVOCATION_TYPE_KEY, 1452 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); 1453 // If Launcher has requested to override long press home, add a delay for the ripple. 1454 // TODO(b/304146255): Remove this delay once we can exclude 3-button nav from screenshot. 1455 boolean delayAssistInvocation = mAssistManagerLazy.get().shouldOverrideAssist( 1456 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); 1457 // In practice, I think v should always be a KeyButtonView, but just being safe. 1458 if (delayAssistInvocation && v instanceof KeyButtonView) { 1459 ((KeyButtonView) v).setOnRippleInvisibleRunnable( 1460 () -> mAssistManagerLazy.get().startAssist(args)); 1461 } else { 1462 mAssistManagerLazy.get().startAssist(args); 1463 } 1464 mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams); 1465 mView.abortCurrentGesture(); 1466 return true; 1467 } 1468 1469 // additional optimization when we have software system buttons - start loading the recent 1470 // tasks on touch down onRecentsTouch(View v, MotionEvent event)1471 private boolean onRecentsTouch(View v, MotionEvent event) { 1472 int action = event.getAction() & MotionEvent.ACTION_MASK; 1473 if (action == MotionEvent.ACTION_DOWN) { 1474 mCommandQueue.preloadRecentApps(); 1475 } else if (action == MotionEvent.ACTION_CANCEL) { 1476 mCommandQueue.cancelPreloadRecentApps(); 1477 } else if (action == MotionEvent.ACTION_UP) { 1478 if (!v.isPressed()) { 1479 mCommandQueue.cancelPreloadRecentApps(); 1480 } 1481 } 1482 return false; 1483 } 1484 onRecentsClick(View v)1485 private void onRecentsClick(View v) { 1486 mNavBarButtonClickLogger.logRecentsButtonClick(); 1487 1488 if (LatencyTracker.isEnabled(mContext)) { 1489 LatencyTracker.getInstance(mContext).onActionStart( 1490 LatencyTracker.ACTION_TOGGLE_RECENTS); 1491 } 1492 mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams); 1493 mCommandQueue.toggleRecentApps(); 1494 } 1495 onImeSwitcherClick(View v)1496 private void onImeSwitcherClick(View v) { 1497 mNavBarButtonClickLogger.logImeSwitcherClick(); 1498 mInputMethodManager.showInputMethodPickerFromSystem( 1499 true /* showAuxiliarySubtypes */, mDisplayId); 1500 mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP); 1501 }; 1502 onLongPressBackHome(View v)1503 private boolean onLongPressBackHome(View v) { 1504 return onLongPressNavigationButtons(v, R.id.back, R.id.home); 1505 } 1506 onLongPressBackRecents(View v)1507 private boolean onLongPressBackRecents(View v) { 1508 return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps); 1509 } 1510 1511 /** 1512 * This handles long-press of both back and recents/home. Back is the common button with 1513 * combination of recents if it is visible or home if recents is invisible. 1514 * They are handled together to capture them both being long-pressed 1515 * at the same time to exit screen pinning (lock task). 1516 * 1517 * When accessibility mode is on, only a long-press from recents/home 1518 * is required to exit. 1519 * 1520 * In all other circumstances we try to pass through long-press events 1521 * for Back, so that apps can still use it. Which can be from two things. 1522 * 1) Not currently in screen pinning (lock task). 1523 * 2) Back is long-pressed without recents/home. 1524 */ onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2)1525 private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) { 1526 try { 1527 boolean sendBackLongPress = false; 1528 IActivityTaskManager activityManager = ActivityTaskManager.getService(); 1529 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); 1530 boolean inLockTaskMode = activityManager.isInLockTaskMode(); 1531 boolean stopLockTaskMode = false; 1532 try { 1533 if (inLockTaskMode && !touchExplorationEnabled) { 1534 long time = System.currentTimeMillis(); 1535 1536 // If we recently long-pressed the other button then they were 1537 // long-pressed 'together' 1538 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERANCE) { 1539 stopLockTaskMode = true; 1540 return true; 1541 } else if (v.getId() == btnId1) { 1542 ButtonDispatcher button = btnId2 == R.id.recent_apps 1543 ? mView.getRecentsButton() : mView.getHomeButton(); 1544 if (!button.getCurrentView().isPressed()) { 1545 // If we aren't pressing recents/home right now then they presses 1546 // won't be together, so send the standard long-press action. 1547 sendBackLongPress = true; 1548 } 1549 } 1550 mLastLockToAppLongPress = time; 1551 } else { 1552 // If this is back still need to handle sending the long-press event. 1553 if (v.getId() == btnId1) { 1554 sendBackLongPress = true; 1555 } else if (touchExplorationEnabled && inLockTaskMode) { 1556 // When in accessibility mode a long press that is recents/home (not back) 1557 // should stop lock task. 1558 stopLockTaskMode = true; 1559 return true; 1560 } else if (v.getId() == btnId2) { 1561 return btnId2 == R.id.recent_apps 1562 ? false 1563 : onHomeLongClick(mView.getHomeButton().getCurrentView()); 1564 } 1565 } 1566 } finally { 1567 if (stopLockTaskMode) { 1568 activityManager.stopSystemLockTaskMode(); 1569 // When exiting refresh disabled flags. 1570 mView.updateNavButtonIcons(); 1571 } 1572 } 1573 1574 if (sendBackLongPress) { 1575 KeyButtonView keyButtonView = (KeyButtonView) v; 1576 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 1577 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 1578 return true; 1579 } 1580 } catch (RemoteException e) { 1581 Log.d(TAG, "Unable to reach activity manager", e); 1582 } 1583 return false; 1584 } 1585 onAccessibilityClick(View v)1586 private void onAccessibilityClick(View v) { 1587 mNavBarButtonClickLogger.logAccessibilityButtonClick(); 1588 final Display display = v.getDisplay(); 1589 mAccessibilityManager.notifyAccessibilityButtonClicked( 1590 display != null ? display.getDisplayId() : mDisplayTracker.getDefaultDisplayId()); 1591 } 1592 onAccessibilityLongClick(View v)1593 private boolean onAccessibilityLongClick(View v) { 1594 final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 1595 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 1596 final String chooserClassName = AccessibilityButtonChooserActivity.class.getName(); 1597 intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); 1598 mContext.startActivityAsUser(intent, mUserTracker.getUserHandle()); 1599 return true; 1600 } 1601 updateAccessibilityStateFlags()1602 void updateAccessibilityStateFlags() { 1603 mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled(); 1604 if (mView != null) { 1605 long a11yFlags = mNavBarHelper.getA11yButtonState(); 1606 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 1607 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 1608 mView.setAccessibilityButtonState(clickable, longClickable); 1609 } 1610 updateSystemUiStateFlags(); 1611 } 1612 updateSystemUiStateFlags()1613 public void updateSystemUiStateFlags() { 1614 long a11yFlags = mNavBarHelper.getA11yButtonState(); 1615 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 1616 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 1617 1618 mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable) 1619 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable) 1620 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) 1621 .setFlag(SYSUI_STATE_IME_SHOWING, 1622 (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) 1623 .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, 1624 (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0) 1625 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, 1626 allowSystemGestureIgnoringBarVisibility()) 1627 .commitUpdate(mDisplayId); 1628 } 1629 updateAssistantEntrypoints(boolean assistantAvailable, boolean longPressHomeEnabled)1630 private void updateAssistantEntrypoints(boolean assistantAvailable, 1631 boolean longPressHomeEnabled) { 1632 if (mOverviewProxyService.getProxy() != null) { 1633 try { 1634 mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable, 1635 longPressHomeEnabled); 1636 } catch (RemoteException e) { 1637 Log.w(TAG, "Unable to send assistant availability data to launcher"); 1638 } 1639 } 1640 reconfigureHomeLongClick(); 1641 } 1642 1643 // ----- Methods that DisplayNavigationBarController talks to ----- 1644 1645 /** Applies auto dimming animation on navigation bar when touched. */ touchAutoDim()1646 public void touchAutoDim() { 1647 getBarTransitions().setAutoDim(false); 1648 mHandler.removeCallbacks(mAutoDim); 1649 int state = mStatusBarStateController.getState(); 1650 if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) { 1651 mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS); 1652 } 1653 } 1654 setLightBarController(LightBarController lightBarController)1655 public void setLightBarController(LightBarController lightBarController) { 1656 mLightBarController = lightBarController; 1657 if (mLightBarController != null) { 1658 mLightBarController.setNavigationBar( 1659 getBarTransitions().getLightTransitionsController()); 1660 } 1661 } 1662 setWindowVisible(boolean visible)1663 private void setWindowVisible(boolean visible) { 1664 mRegionSamplingHelper.setWindowVisible(visible); 1665 mView.setWindowVisible(visible); 1666 } 1667 1668 /** Sets {@link AutoHideController} to the navigation bar. */ setAutoHideController(AutoHideController autoHideController)1669 private void setAutoHideController(AutoHideController autoHideController) { 1670 mAutoHideController = autoHideController; 1671 if (mAutoHideController != null) { 1672 mAutoHideController.setNavigationBar(mAutoHideUiElement); 1673 } 1674 mView.setAutoHideController(autoHideController); 1675 } 1676 isTransientShown()1677 private boolean isTransientShown() { 1678 return mTransientShown; 1679 } 1680 checkBarModes()1681 private void checkBarModes() { 1682 // We only have status bar on default display now. 1683 if (mIsOnDefaultDisplay) { 1684 mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::checkBarModes); 1685 } else { 1686 checkNavBarModes(); 1687 } 1688 } 1689 isNavBarWindowVisible()1690 public boolean isNavBarWindowVisible() { 1691 return mNavigationBarWindowState == WINDOW_STATE_SHOWING; 1692 } 1693 allowSystemGestureIgnoringBarVisibility()1694 private boolean allowSystemGestureIgnoringBarVisibility() { 1695 return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 1696 } 1697 1698 /** 1699 * Checks current navigation bar mode and make transitions. 1700 */ checkNavBarModes()1701 public void checkNavBarModes() { 1702 final boolean anim = 1703 mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive) 1704 .orElse(false) 1705 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN; 1706 getBarTransitions().transitionTo(mTransitionMode, anim); 1707 } 1708 disableAnimationsDuringHide(long delay)1709 public void disableAnimationsDuringHide(long delay) { 1710 mView.setLayoutTransitionsEnabled(false); 1711 mHandler.postDelayed(mEnableLayoutTransitions, 1712 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); 1713 } 1714 1715 /** 1716 * Performs transitions on navigation bar. 1717 * 1718 * @param barMode transition bar mode. 1719 * @param animate shows animations if {@code true}. 1720 */ transitionTo(@ransitionMode int barMode, boolean animate)1721 public void transitionTo(@TransitionMode int barMode, boolean animate) { 1722 getBarTransitions().transitionTo(barMode, animate); 1723 } 1724 getBarTransitions()1725 public NavigationBarTransitions getBarTransitions() { 1726 return mNavigationBarTransitions; 1727 } 1728 finishBarAnimations()1729 public void finishBarAnimations() { 1730 getBarTransitions().finishAnimations(); 1731 } 1732 getBarLayoutParams(int rotation)1733 private WindowManager.LayoutParams getBarLayoutParams(int rotation) { 1734 WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); 1735 lp.paramsForRotation = new WindowManager.LayoutParams[4]; 1736 for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { 1737 lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot); 1738 } 1739 return lp; 1740 } 1741 getBarLayoutParamsForRotation(int rotation)1742 private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { 1743 int width = WindowManager.LayoutParams.MATCH_PARENT; 1744 int height = WindowManager.LayoutParams.MATCH_PARENT; 1745 int insetsHeight = -1; 1746 int gravity = Gravity.BOTTOM; 1747 boolean navBarCanMove = true; 1748 final Context userContext = mUserContextProvider.createCurrentUserContext(mContext); 1749 if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) { 1750 Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds(); 1751 navBarCanMove = displaySize.width() != displaySize.height() 1752 && userContext.getResources().getBoolean( 1753 com.android.internal.R.bool.config_navBarCanMove); 1754 } 1755 if (!navBarCanMove) { 1756 height = userContext.getResources().getDimensionPixelSize( 1757 com.android.internal.R.dimen.navigation_bar_frame_height); 1758 insetsHeight = userContext.getResources().getDimensionPixelSize( 1759 com.android.internal.R.dimen.navigation_bar_height); 1760 } else { 1761 switch (rotation) { 1762 case ROTATION_UNDEFINED: 1763 case Surface.ROTATION_0: 1764 case Surface.ROTATION_180: 1765 height = userContext.getResources().getDimensionPixelSize( 1766 com.android.internal.R.dimen.navigation_bar_frame_height); 1767 insetsHeight = userContext.getResources().getDimensionPixelSize( 1768 com.android.internal.R.dimen.navigation_bar_height); 1769 break; 1770 case Surface.ROTATION_90: 1771 gravity = Gravity.RIGHT; 1772 width = userContext.getResources().getDimensionPixelSize( 1773 com.android.internal.R.dimen.navigation_bar_width); 1774 break; 1775 case Surface.ROTATION_270: 1776 gravity = Gravity.LEFT; 1777 width = userContext.getResources().getDimensionPixelSize( 1778 com.android.internal.R.dimen.navigation_bar_width); 1779 break; 1780 } 1781 } 1782 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1783 width, 1784 height, 1785 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 1786 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1787 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1788 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1789 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 1790 | WindowManager.LayoutParams.FLAG_SLIPPERY, 1791 PixelFormat.TRANSLUCENT); 1792 lp.gravity = gravity; 1793 lp.providedInsets = getInsetsFrameProvider(insetsHeight, userContext); 1794 1795 lp.token = new Binder(); 1796 lp.accessibilityTitle = userContext.getString(R.string.nav_bar); 1797 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC 1798 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; 1799 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 1800 lp.windowAnimations = 0; 1801 lp.setTitle("NavigationBar" + userContext.getDisplayId()); 1802 lp.setFitInsetsTypes(0 /* types */); 1803 lp.setTrustedOverlay(); 1804 return lp; 1805 } 1806 getInsetsFrameProvider(int insetsHeight, Context userContext)1807 private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) { 1808 final InsetsFrameProvider navBarProvider = 1809 new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars()); 1810 if (!ENABLE_HIDE_IME_CAPTION_BAR) { 1811 navBarProvider.setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] { 1812 new InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, null) 1813 }); 1814 } 1815 if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) { 1816 navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight)); 1817 } 1818 final boolean needsScrim = userContext.getResources().getBoolean( 1819 com.android.internal.R.bool.config_navBarNeedsScrim); 1820 navBarProvider.setFlags(needsScrim ? 0 : FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM); 1821 1822 final InsetsFrameProvider tappableElementProvider = new InsetsFrameProvider( 1823 mInsetsSourceOwner, 0, WindowInsets.Type.tappableElement()); 1824 final boolean tapThrough = userContext.getResources().getBoolean( 1825 com.android.internal.R.bool.config_navBarTapThrough); 1826 if (tapThrough) { 1827 tappableElementProvider.setInsetsSize(Insets.NONE); 1828 } 1829 1830 final int gestureHeight = userContext.getResources().getDimensionPixelSize( 1831 com.android.internal.R.dimen.navigation_bar_gesture_height); 1832 final boolean handlingGesture = mEdgeBackGestureHandler.isHandlingGestures(); 1833 final InsetsFrameProvider mandatoryGestureProvider = new InsetsFrameProvider( 1834 mInsetsSourceOwner, 0, WindowInsets.Type.mandatorySystemGestures()); 1835 if (handlingGesture) { 1836 mandatoryGestureProvider.setInsetsSize(Insets.of(0, 0, 0, gestureHeight)); 1837 } 1838 final int gestureInsetsLeft = handlingGesture 1839 ? mEdgeBackGestureHandler.getEdgeWidthLeft() : 0; 1840 final int gestureInsetsRight = handlingGesture 1841 ? mEdgeBackGestureHandler.getEdgeWidthRight() : 0; 1842 return new InsetsFrameProvider[] { 1843 navBarProvider, 1844 tappableElementProvider, 1845 mandatoryGestureProvider, 1846 new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.systemGestures()) 1847 .setSource(InsetsFrameProvider.SOURCE_DISPLAY) 1848 .setInsetsSize(Insets.of(gestureInsetsLeft, 0, 0, 0)) 1849 .setMinimalInsetsSizeInDisplayCutoutSafe( 1850 Insets.of(gestureInsetsLeft, 0, 0, 0)), 1851 new InsetsFrameProvider(mInsetsSourceOwner, 1, WindowInsets.Type.systemGestures()) 1852 .setSource(InsetsFrameProvider.SOURCE_DISPLAY) 1853 .setInsetsSize(Insets.of(0, 0, gestureInsetsRight, 0)) 1854 .setMinimalInsetsSizeInDisplayCutoutSafe( 1855 Insets.of(0, 0, gestureInsetsRight, 0)) 1856 }; 1857 } 1858 canShowSecondaryHandle()1859 private boolean canShowSecondaryHandle() { 1860 return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null; 1861 } 1862 1863 private final UserTracker.Callback mUserChangedCallback = 1864 new UserTracker.Callback() { 1865 @Override 1866 public void onUserChanged(int newUser, @NonNull Context userContext) { 1867 // The accessibility settings may be different for the new user 1868 updateAccessibilityStateFlags(); 1869 } 1870 }; 1871 1872 @VisibleForTesting getNavigationIconHints()1873 int getNavigationIconHints() { 1874 return mNavigationIconHints; 1875 } 1876 setNavigationIconHints(int hints)1877 private void setNavigationIconHints(int hints) { 1878 if (hints == mNavigationIconHints) return; 1879 if (!isLargeScreen(mContext)) { 1880 // All IME functions handled by launcher via Sysui flags for large screen 1881 final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 1882 final boolean oldBackAlt = 1883 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 1884 if (newBackAlt != oldBackAlt) { 1885 mView.onImeVisibilityChanged(newBackAlt); 1886 mImeVisible = newBackAlt; 1887 } 1888 1889 mView.setNavigationIconHints(hints); 1890 } 1891 if (DEBUG) { 1892 android.widget.Toast.makeText(mContext, 1893 "Navigation icon hints = " + hints, 1894 500).show(); 1895 } 1896 mNavigationIconHints = hints; 1897 } 1898 1899 /** 1900 * @param inScreenSpace Whether to return values in screen space or window space 1901 * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds 1902 * @return 1903 */ getButtonLocations(boolean inScreenSpace, boolean useNearestRegion)1904 Region getButtonLocations(boolean inScreenSpace, boolean useNearestRegion) { 1905 if (useNearestRegion && !inScreenSpace) { 1906 // We currently don't support getting the nearest region in anything but screen space 1907 useNearestRegion = false; 1908 } 1909 Region region = new Region(); 1910 Map<View, Rect> touchRegionCache = mView.getButtonTouchRegionCache(); 1911 updateButtonLocation( 1912 region, touchRegionCache, mView.getBackButton(), inScreenSpace, useNearestRegion); 1913 updateButtonLocation( 1914 region, touchRegionCache, mView.getHomeButton(), inScreenSpace, useNearestRegion); 1915 updateButtonLocation(region, touchRegionCache, mView.getRecentsButton(), inScreenSpace, 1916 useNearestRegion); 1917 updateButtonLocation(region, touchRegionCache, mView.getImeSwitchButton(), inScreenSpace, 1918 useNearestRegion); 1919 updateButtonLocation( 1920 region, touchRegionCache, mView.getAccessibilityButton(), inScreenSpace, 1921 useNearestRegion); 1922 if (mView.getFloatingRotationButton().isVisible()) { 1923 // Note: this button is floating so the nearest region doesn't apply 1924 updateButtonLocation( 1925 region, mView.getFloatingRotationButton().getCurrentView(), inScreenSpace); 1926 } 1927 return region; 1928 } 1929 updateButtonLocation( Region region, Map<View, Rect> touchRegionCache, ButtonDispatcher button, boolean inScreenSpace, boolean useNearestRegion)1930 private void updateButtonLocation( 1931 Region region, 1932 Map<View, Rect> touchRegionCache, 1933 ButtonDispatcher button, 1934 boolean inScreenSpace, 1935 boolean useNearestRegion) { 1936 if (button == null) { 1937 return; 1938 } 1939 View view = button.getCurrentView(); 1940 if (view == null || !button.isVisible()) { 1941 return; 1942 } 1943 // If the button is tappable from perspective of NearestTouchFrame, then we'll 1944 // include the regions where the tap is valid instead of just the button layout location 1945 if (useNearestRegion && touchRegionCache.containsKey(view)) { 1946 region.op(touchRegionCache.get(view), Region.Op.UNION); 1947 return; 1948 } 1949 updateButtonLocation(region, view, inScreenSpace); 1950 } 1951 updateButtonLocation(Region region, View view, boolean inScreenSpace)1952 private void updateButtonLocation(Region region, View view, boolean inScreenSpace) { 1953 Rect bounds = new Rect(); 1954 if (inScreenSpace) { 1955 view.getBoundsOnScreen(bounds); 1956 } else { 1957 int[] location = new int[2]; 1958 view.getLocationInWindow(location); 1959 bounds.set(location[0], location[1], 1960 location[0] + view.getWidth(), 1961 location[1] + view.getHeight()); 1962 } 1963 region.op(bounds, Region.Op.UNION); 1964 } 1965 setOrientedHandleSamplingRegion(Rect orientedHandleSamplingRegion)1966 void setOrientedHandleSamplingRegion(Rect orientedHandleSamplingRegion) { 1967 mOrientedHandleSamplingRegion = orientedHandleSamplingRegion; 1968 mRegionSamplingHelper.updateSamplingRect(); 1969 } 1970 calculateSamplingRect()1971 private Rect calculateSamplingRect() { 1972 mSamplingBounds.setEmpty(); 1973 // TODO: Extend this to 2/3 button layout as well 1974 View view = mView.getHomeHandle().getCurrentView(); 1975 1976 if (view != null) { 1977 int[] pos = new int[2]; 1978 view.getLocationOnScreen(pos); 1979 Point displaySize = new Point(); 1980 view.getContext().getDisplay().getRealSize(displaySize); 1981 final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin, 1982 displaySize.y - mView.getNavBarHeight(), 1983 pos[0] + view.getWidth() + mNavColorSampleMargin, 1984 displaySize.y); 1985 mSamplingBounds.set(samplingBounds); 1986 } 1987 1988 return mSamplingBounds; 1989 } 1990 setNavigationBarLumaSamplingEnabled(boolean enable)1991 void setNavigationBarLumaSamplingEnabled(boolean enable) { 1992 if (enable) { 1993 mRegionSamplingHelper.start(mSamplingBounds); 1994 } else { 1995 mRegionSamplingHelper.stop(); 1996 } 1997 } 1998 setNavBarMode(int mode)1999 private void setNavBarMode(int mode) { 2000 mView.setNavBarMode(mode, mNavigationModeController.getImeDrawsImeNavBar()); 2001 if (isGesturalMode(mode)) { 2002 mRegionSamplingHelper.start(mSamplingBounds); 2003 } else { 2004 mRegionSamplingHelper.stop(); 2005 } 2006 } 2007 onBarTransition(int newMode)2008 void onBarTransition(int newMode) { 2009 if (newMode == MODE_OPAQUE) { 2010 // If the nav bar background is opaque, stop auto tinting since we know the icons are 2011 // showing over a dark background 2012 mRegionSamplingHelper.stop(); 2013 getBarTransitions().getLightTransitionsController().setIconsDark( 2014 false /* dark */, true /* animate */); 2015 } else { 2016 mRegionSamplingHelper.start(mSamplingBounds); 2017 } 2018 } 2019 2020 private final ModeChangedListener mModeChangedListener = new ModeChangedListener() { 2021 @Override 2022 public void onNavigationModeChanged(int mode) { 2023 mNavBarMode = mode; 2024 2025 if (!QuickStepContract.isGesturalMode(mode)) { 2026 // Reset the override alpha 2027 if (getBarTransitions() != null) { 2028 getBarTransitions().setBackgroundOverrideAlpha(1f); 2029 } 2030 } 2031 2032 // Update the window layout params when the nav mode changes as that will affect the 2033 // system gesture insets 2034 setNavBarMode(mode); 2035 repositionNavigationBar(mCurrentRotation); 2036 2037 if (!canShowSecondaryHandle()) { 2038 resetSecondaryHandle(); 2039 } 2040 mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); 2041 } 2042 }; 2043 2044 private final Gefingerpoken mTouchHandler = new Gefingerpoken() { 2045 private boolean mDeadZoneConsuming; 2046 2047 @Override 2048 public boolean onInterceptTouchEvent(MotionEvent ev) { 2049 if (isGesturalMode(mNavBarMode) && mImeVisible 2050 && ev.getAction() == MotionEvent.ACTION_DOWN) { 2051 SysUiStatsLog.write(SysUiStatsLog.IME_TOUCH_REPORTED, 2052 (int) ev.getX(), (int) ev.getY()); 2053 } 2054 return shouldDeadZoneConsumeTouchEvents(ev); 2055 } 2056 2057 @Override 2058 public boolean onTouchEvent(MotionEvent ev) { 2059 shouldDeadZoneConsumeTouchEvents(ev); 2060 return false; 2061 } 2062 2063 private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) { 2064 int action = event.getActionMasked(); 2065 if (action == MotionEvent.ACTION_DOWN) { 2066 mDeadZoneConsuming = false; 2067 } 2068 if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) { 2069 switch (action) { 2070 case MotionEvent.ACTION_DOWN: 2071 // Allow gestures starting in the deadzone to be slippery 2072 mView.setSlippery(true); 2073 mDeadZoneConsuming = true; 2074 break; 2075 case MotionEvent.ACTION_CANCEL: 2076 case MotionEvent.ACTION_UP: 2077 // When a gesture started in the deadzone is finished, restore 2078 // slippery state 2079 mView.updateSlippery(); 2080 mDeadZoneConsuming = false; 2081 break; 2082 } 2083 return true; 2084 } 2085 return false; 2086 } 2087 }; 2088 } 2089