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