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.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 20 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; 21 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 22 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 23 import static android.app.StatusBarManager.WindowType; 24 import static android.app.StatusBarManager.WindowVisibleState; 25 import static android.app.StatusBarManager.windowStateToString; 26 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; 27 import static android.view.Display.DEFAULT_DISPLAY; 28 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 29 import static android.view.InsetsState.containsType; 30 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; 31 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; 32 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS; 33 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 34 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 35 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 36 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; 37 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; 38 39 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; 40 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; 41 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE; 42 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 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_NAV_BAR_HIDDEN; 48 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 49 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; 50 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 51 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 52 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 53 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; 54 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; 55 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; 56 57 import android.accessibilityservice.AccessibilityServiceInfo; 58 import android.annotation.IdRes; 59 import android.annotation.Nullable; 60 import android.app.ActivityManager; 61 import android.app.ActivityTaskManager; 62 import android.app.IActivityTaskManager; 63 import android.app.StatusBarManager; 64 import android.content.BroadcastReceiver; 65 import android.content.ContentResolver; 66 import android.content.Context; 67 import android.content.Intent; 68 import android.content.IntentFilter; 69 import android.content.res.Configuration; 70 import android.database.ContentObserver; 71 import android.graphics.PixelFormat; 72 import android.graphics.Rect; 73 import android.graphics.RectF; 74 import android.inputmethodservice.InputMethodService; 75 import android.net.Uri; 76 import android.os.Binder; 77 import android.os.Bundle; 78 import android.os.Handler; 79 import android.os.IBinder; 80 import android.os.Looper; 81 import android.os.RemoteException; 82 import android.os.UserHandle; 83 import android.provider.DeviceConfig; 84 import android.provider.Settings; 85 import android.telecom.TelecomManager; 86 import android.text.TextUtils; 87 import android.util.Log; 88 import android.view.Display; 89 import android.view.Gravity; 90 import android.view.HapticFeedbackConstants; 91 import android.view.IWindowManager; 92 import android.view.InsetsState.InternalInsetsType; 93 import android.view.KeyEvent; 94 import android.view.LayoutInflater; 95 import android.view.MotionEvent; 96 import android.view.Surface; 97 import android.view.View; 98 import android.view.ViewTreeObserver; 99 import android.view.WindowInsetsController.Appearance; 100 import android.view.WindowInsetsController.Behavior; 101 import android.view.WindowManager; 102 import android.view.accessibility.AccessibilityEvent; 103 import android.view.accessibility.AccessibilityManager; 104 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; 105 import android.view.inputmethod.InputMethodManager; 106 107 import androidx.annotation.VisibleForTesting; 108 109 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; 110 import com.android.internal.logging.MetricsLogger; 111 import com.android.internal.logging.UiEvent; 112 import com.android.internal.logging.UiEventLogger; 113 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 114 import com.android.internal.util.LatencyTracker; 115 import com.android.internal.view.AppearanceRegion; 116 import com.android.systemui.Dependency; 117 import com.android.systemui.R; 118 import com.android.systemui.accessibility.AccessibilityButtonModeObserver; 119 import com.android.systemui.accessibility.SystemActions; 120 import com.android.systemui.assist.AssistManager; 121 import com.android.systemui.broadcast.BroadcastDispatcher; 122 import com.android.systemui.dagger.qualifiers.Main; 123 import com.android.systemui.model.SysUiState; 124 import com.android.systemui.navigationbar.buttons.ButtonDispatcher; 125 import com.android.systemui.navigationbar.buttons.KeyButtonView; 126 import com.android.systemui.navigationbar.buttons.RotationContextButton; 127 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle; 128 import com.android.systemui.plugins.DarkIconDispatcher; 129 import com.android.systemui.plugins.statusbar.StatusBarStateController; 130 import com.android.systemui.recents.OverviewProxyService; 131 import com.android.systemui.recents.Recents; 132 import com.android.systemui.settings.UserTracker; 133 import com.android.systemui.shared.system.ActivityManagerWrapper; 134 import com.android.systemui.shared.system.QuickStepContract; 135 import com.android.systemui.statusbar.AutoHideUiElement; 136 import com.android.systemui.statusbar.CommandQueue; 137 import com.android.systemui.statusbar.CommandQueue.Callbacks; 138 import com.android.systemui.statusbar.NotificationRemoteInputManager; 139 import com.android.systemui.statusbar.NotificationShadeDepthController; 140 import com.android.systemui.statusbar.StatusBarState; 141 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 142 import com.android.systemui.statusbar.phone.AutoHideController; 143 import com.android.systemui.statusbar.phone.BarTransitions; 144 import com.android.systemui.statusbar.phone.LightBarController; 145 import com.android.systemui.statusbar.phone.ShadeController; 146 import com.android.systemui.statusbar.phone.StatusBar; 147 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; 148 import com.android.systemui.statusbar.policy.BatteryController; 149 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 150 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; 151 import com.android.wm.shell.pip.Pip; 152 153 import java.io.PrintWriter; 154 import java.util.List; 155 import java.util.Locale; 156 import java.util.Optional; 157 import java.util.function.Consumer; 158 159 import dagger.Lazy; 160 161 /** 162 * Contains logic for a navigation bar view. 163 */ 164 public class NavigationBar implements View.OnAttachStateChangeListener, 165 Callbacks, NavigationModeController.ModeChangedListener, 166 AccessibilityButtonModeObserver.ModeChangedListener { 167 168 public static final String TAG = "NavigationBar"; 169 private static final boolean DEBUG = false; 170 private static final String EXTRA_DISABLE_STATE = "disabled_state"; 171 private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; 172 private static final String EXTRA_APPEARANCE = "appearance"; 173 private static final String EXTRA_BEHAVIOR = "behavior"; 174 private static final String EXTRA_TRANSIENT_STATE = "transient_state"; 175 176 /** Allow some time inbetween the long press for back and recents. */ 177 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; 178 private static final long AUTODIM_TIMEOUT_MS = 2250; 179 180 private final Context mContext; 181 private final WindowManager mWindowManager; 182 private final AccessibilityManager mAccessibilityManager; 183 private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; 184 private final DeviceProvisionedController mDeviceProvisionedController; 185 private final StatusBarStateController mStatusBarStateController; 186 private final MetricsLogger mMetricsLogger; 187 private final Lazy<AssistManager> mAssistManagerLazy; 188 private final SysUiState mSysUiFlagsContainer; 189 private final Lazy<StatusBar> mStatusBarLazy; 190 private final ShadeController mShadeController; 191 private final NotificationRemoteInputManager mNotificationRemoteInputManager; 192 private final OverviewProxyService mOverviewProxyService; 193 private final NavigationModeController mNavigationModeController; 194 private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; 195 private final BroadcastDispatcher mBroadcastDispatcher; 196 private final CommandQueue mCommandQueue; 197 private final Optional<Pip> mPipOptional; 198 private final Optional<LegacySplitScreen> mSplitScreenOptional; 199 private final Optional<Recents> mRecentsOptional; 200 private final SystemActions mSystemActions; 201 private final Handler mHandler; 202 private final NavigationBarOverlayController mNavbarOverlayController; 203 private final UiEventLogger mUiEventLogger; 204 private final UserTracker mUserTracker; 205 private final NotificationShadeDepthController mNotificationShadeDepthController; 206 207 private Bundle mSavedState; 208 private NavigationBarView mNavigationBarView; 209 210 private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 211 212 private int mNavigationIconHints = 0; 213 private @TransitionMode int mNavigationBarMode; 214 private ContentResolver mContentResolver; 215 private boolean mAssistantAvailable; 216 private boolean mLongPressHomeEnabled; 217 private boolean mAssistantTouchGestureEnabled; 218 219 private int mDisabledFlags1; 220 private int mDisabledFlags2; 221 private long mLastLockToAppLongPress; 222 223 private Locale mLocale; 224 private int mLayoutDirection; 225 226 private boolean mAllowForceNavBarHandleOpaque; 227 private boolean mForceNavBarHandleOpaque; 228 private Optional<Long> mHomeButtonLongPressDurationMs; 229 private boolean mIsCurrentUserSetup; 230 231 /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ 232 private @Appearance int mAppearance; 233 234 /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */ 235 private @Behavior int mBehavior; 236 237 private boolean mTransientShown; 238 private int mNavBarMode = NAV_BAR_MODE_3BUTTON; 239 private LightBarController mLightBarController; 240 private AutoHideController mAutoHideController; 241 242 @VisibleForTesting 243 public int mDisplayId; 244 private boolean mIsOnDefaultDisplay; 245 public boolean mHomeBlockedThisTouch; 246 247 /** 248 * When user is QuickSwitching between apps of different orientations, we'll draw a fake 249 * home handle on the orientation they originally touched down to start their swipe 250 * gesture to indicate to them that they can continue in that orientation without having to 251 * rotate the phone 252 * The secondary handle will show when we get 253 * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the 254 * original handle hidden and we'll flip the visibilities once the 255 * {@link #mTasksFrozenListener} fires 256 */ 257 private QuickswitchOrientedNavHandle mOrientationHandle; 258 private WindowManager.LayoutParams mOrientationParams; 259 private int mStartingQuickSwitchRotation = -1; 260 private int mCurrentRotation; 261 private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener; 262 private boolean mShowOrientedHandleForImmersiveMode; 263 264 @com.android.internal.annotations.VisibleForTesting 265 public enum NavBarActionEvent implements UiEventLogger.UiEventEnum { 266 267 @UiEvent(doc = "Assistant invoked via home button long press.") 268 NAVBAR_ASSIST_LONGPRESS(550); 269 270 private final int mId; 271 NavBarActionEvent(int id)272 NavBarActionEvent(int id) { 273 mId = id; 274 } 275 276 @Override getId()277 public int getId() { 278 return mId; 279 } 280 } 281 282 private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() { 283 @Override 284 public void synchronizeState() { 285 checkNavBarModes(); 286 } 287 288 @Override 289 public boolean shouldHideOnTouch() { 290 return !mNotificationRemoteInputManager.getController().isRemoteInputActive(); 291 } 292 293 @Override 294 public boolean isVisible() { 295 return isTransientShown(); 296 } 297 298 @Override 299 public void hide() { 300 clearTransient(); 301 } 302 }; 303 304 private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { 305 @Override 306 public void onConnectionChanged(boolean isConnected) { 307 mNavigationBarView.updateStates(); 308 updateScreenPinningGestures(); 309 310 // Send the assistant availability upon connection 311 if (isConnected) { 312 updateAssistantEntrypoints(); 313 } 314 } 315 316 @Override 317 public void onQuickStepStarted() { 318 // Use navbar dragging as a signal to hide the rotate button 319 mNavigationBarView.getRotationButtonController().setRotateSuggestionButtonState(false); 320 321 // Hide the notifications panel when quick step starts 322 mShadeController.collapsePanel(true /* animate */); 323 } 324 325 @Override 326 public void onPrioritizedRotation(@Surface.Rotation int rotation) { 327 mStartingQuickSwitchRotation = rotation; 328 if (rotation == -1) { 329 mShowOrientedHandleForImmersiveMode = false; 330 } 331 orientSecondaryHomeHandle(); 332 } 333 334 @Override 335 public void startAssistant(Bundle bundle) { 336 mAssistManagerLazy.get().startAssist(bundle); 337 } 338 339 @Override 340 public void onNavBarButtonAlphaChanged(float alpha, boolean animate) { 341 if (!mIsCurrentUserSetup) { 342 // If the current user is not yet setup, then don't update any button alphas 343 return; 344 } 345 if (QuickStepContract.isLegacyMode(mNavBarMode)) { 346 // Don't allow the bar buttons to be affected by the alpha 347 return; 348 } 349 350 ButtonDispatcher buttonDispatcher = null; 351 boolean forceVisible = false; 352 if (QuickStepContract.isGesturalMode(mNavBarMode)) { 353 // Disallow home handle animations when in gestural 354 animate = false; 355 forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque; 356 buttonDispatcher = mNavigationBarView.getHomeHandle(); 357 if (getBarTransitions() != null) { 358 getBarTransitions().setBackgroundOverrideAlpha(alpha); 359 } 360 } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) { 361 buttonDispatcher = mNavigationBarView.getBackButton(); 362 } 363 if (buttonDispatcher != null) { 364 buttonDispatcher.setVisibility( 365 (forceVisible || alpha > 0) ? View.VISIBLE : View.INVISIBLE); 366 buttonDispatcher.setAlpha(forceVisible ? 1f : alpha, animate); 367 } 368 } 369 370 @Override 371 public void onHomeRotationEnabled(boolean enabled) { 372 mNavigationBarView.getRotationButtonController().setHomeRotationEnabled(enabled); 373 } 374 375 @Override 376 public void onOverviewShown(boolean fromHome) { 377 // If the overview has fixed orientation that may change display to natural rotation, 378 // we don't want the user rotation to be reset. So after user returns to application, 379 // it can keep in the original rotation. 380 mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); 381 } 382 383 @Override 384 public void onToggleRecentApps() { 385 // The same case as onOverviewShown but only for 3-button navigation. 386 mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); 387 } 388 }; 389 390 private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener = 391 new NavigationBarTransitions.DarkIntensityListener() { 392 @Override 393 public void onDarkIntensity(float darkIntensity) { 394 mOrientationHandle.setDarkIntensity(darkIntensity); 395 } 396 }; 397 398 private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); 399 private final Runnable mEnableLayoutTransitions = () -> 400 mNavigationBarView.setLayoutTransitionsEnabled(true); 401 private final Runnable mOnVariableDurationHomeLongClick = () -> { 402 if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) { 403 mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback( 404 HapticFeedbackConstants.LONG_PRESS, 405 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 406 } 407 }; 408 409 private final ContentObserver mAssistContentObserver = new ContentObserver( 410 new Handler(Looper.getMainLooper())) { 411 @Override 412 public void onChange(boolean selfChange, Uri uri) { 413 updateAssistantEntrypoints(); 414 } 415 }; 416 417 private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = 418 new DeviceConfig.OnPropertiesChangedListener() { 419 @Override 420 public void onPropertiesChanged(DeviceConfig.Properties properties) { 421 if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) { 422 mForceNavBarHandleOpaque = properties.getBoolean( 423 NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); 424 } 425 426 if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) { 427 mHomeButtonLongPressDurationMs = Optional.of( 428 properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0) 429 ).filter(duration -> duration != 0); 430 reconfigureHomeLongClick(); 431 } 432 } 433 }; 434 435 private final DeviceProvisionedController.DeviceProvisionedListener mUserSetupListener = 436 new DeviceProvisionedController.DeviceProvisionedListener() { 437 @Override 438 public void onUserSetupChanged() { 439 mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); 440 } 441 }; 442 443 private final NotificationShadeDepthController.DepthListener mDepthListener = 444 new NotificationShadeDepthController.DepthListener() { 445 boolean mHasBlurs; 446 447 @Override 448 public void onWallpaperZoomOutChanged(float zoomOut) { 449 } 450 451 @Override 452 public void onBlurRadiusChanged(int radius) { 453 boolean hasBlurs = radius != 0; 454 if (hasBlurs == mHasBlurs) { 455 return; 456 } 457 mHasBlurs = hasBlurs; 458 mNavigationBarView.setWindowHasBlurs(hasBlurs); 459 } 460 }; 461 NavigationBar(Context context, WindowManager windowManager, Lazy<AssistManager> assistManagerLazy, AccessibilityManager accessibilityManager, AccessibilityManagerWrapper accessibilityManagerWrapper, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, AccessibilityButtonModeObserver accessibilityButtonModeObserver, StatusBarStateController statusBarStateController, SysUiState sysUiFlagsContainer, BroadcastDispatcher broadcastDispatcher, CommandQueue commandQueue, Optional<Pip> pipOptional, Optional<LegacySplitScreen> splitScreenOptional, Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy, ShadeController shadeController, NotificationRemoteInputManager notificationRemoteInputManager, NotificationShadeDepthController notificationShadeDepthController, SystemActions systemActions, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, UiEventLogger uiEventLogger, UserTracker userTracker)462 public NavigationBar(Context context, 463 WindowManager windowManager, 464 Lazy<AssistManager> assistManagerLazy, 465 AccessibilityManager accessibilityManager, 466 AccessibilityManagerWrapper accessibilityManagerWrapper, 467 DeviceProvisionedController deviceProvisionedController, 468 MetricsLogger metricsLogger, 469 OverviewProxyService overviewProxyService, 470 NavigationModeController navigationModeController, 471 AccessibilityButtonModeObserver accessibilityButtonModeObserver, 472 StatusBarStateController statusBarStateController, 473 SysUiState sysUiFlagsContainer, 474 BroadcastDispatcher broadcastDispatcher, 475 CommandQueue commandQueue, 476 Optional<Pip> pipOptional, 477 Optional<LegacySplitScreen> splitScreenOptional, 478 Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy, 479 ShadeController shadeController, 480 NotificationRemoteInputManager notificationRemoteInputManager, 481 NotificationShadeDepthController notificationShadeDepthController, 482 SystemActions systemActions, 483 @Main Handler mainHandler, 484 NavigationBarOverlayController navbarOverlayController, 485 UiEventLogger uiEventLogger, 486 UserTracker userTracker) { 487 mContext = context; 488 mWindowManager = windowManager; 489 mAccessibilityManager = accessibilityManager; 490 mAccessibilityManagerWrapper = accessibilityManagerWrapper; 491 mDeviceProvisionedController = deviceProvisionedController; 492 mStatusBarStateController = statusBarStateController; 493 mMetricsLogger = metricsLogger; 494 mAssistManagerLazy = assistManagerLazy; 495 mSysUiFlagsContainer = sysUiFlagsContainer; 496 mStatusBarLazy = statusBarLazy; 497 mShadeController = shadeController; 498 mNotificationRemoteInputManager = notificationRemoteInputManager; 499 mOverviewProxyService = overviewProxyService; 500 mNavigationModeController = navigationModeController; 501 mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; 502 mBroadcastDispatcher = broadcastDispatcher; 503 mCommandQueue = commandQueue; 504 mPipOptional = pipOptional; 505 mSplitScreenOptional = splitScreenOptional; 506 mRecentsOptional = recentsOptional; 507 mSystemActions = systemActions; 508 mHandler = mainHandler; 509 mNavbarOverlayController = navbarOverlayController; 510 mUiEventLogger = uiEventLogger; 511 mUserTracker = userTracker; 512 mNotificationShadeDepthController = notificationShadeDepthController; 513 514 mNavBarMode = mNavigationModeController.addListener(this); 515 mAccessibilityButtonModeObserver.addListener(this); 516 } 517 getView()518 public NavigationBarView getView() { 519 return mNavigationBarView; 520 } 521 createView(Bundle savedState)522 public View createView(Bundle savedState) { 523 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 524 WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, 525 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 526 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 527 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 528 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 529 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 530 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 531 | WindowManager.LayoutParams.FLAG_SLIPPERY, 532 PixelFormat.TRANSLUCENT); 533 lp.token = new Binder(); 534 lp.accessibilityTitle = mContext.getString(R.string.nav_bar); 535 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; 536 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 537 lp.windowAnimations = 0; 538 lp.setTitle("NavigationBar" + mContext.getDisplayId()); 539 lp.setFitInsetsTypes(0 /* types */); 540 lp.setTrustedOverlay(); 541 542 NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate( 543 R.layout.navigation_bar_window, null); 544 View barView = LayoutInflater.from(frame.getContext()).inflate( 545 R.layout.navigation_bar, frame); 546 barView.addOnAttachStateChangeListener(this); 547 mNavigationBarView = barView.findViewById(R.id.navigation_bar_view); 548 549 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView); 550 mContext.getSystemService(WindowManager.class).addView(frame, lp); 551 mDisplayId = mContext.getDisplayId(); 552 mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; 553 554 mCommandQueue.addCallback(this); 555 mAssistantAvailable = mAssistManagerLazy.get() 556 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; 557 mContentResolver = mContext.getContentResolver(); 558 mContentResolver.registerContentObserver( 559 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), 560 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL); 561 mContentResolver.registerContentObserver( 562 Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED), 563 false, mAssistContentObserver, UserHandle.USER_ALL); 564 mContentResolver.registerContentObserver( 565 Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED), 566 false, mAssistContentObserver, UserHandle.USER_ALL); 567 mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean( 568 R.bool.allow_force_nav_bar_handle_opaque); 569 mForceNavBarHandleOpaque = DeviceConfig.getBoolean( 570 DeviceConfig.NAMESPACE_SYSTEMUI, 571 NAV_BAR_HANDLE_FORCE_OPAQUE, 572 /* defaultValue = */ true); 573 mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong( 574 DeviceConfig.NAMESPACE_SYSTEMUI, 575 HOME_BUTTON_LONG_PRESS_DURATION_MS, 576 /* defaultValue = */ 0 577 )).filter(duration -> duration != 0); 578 DeviceConfig.addOnPropertiesChangedListener( 579 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); 580 updateAssistantEntrypoints(); 581 582 if (savedState != null) { 583 mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0); 584 mDisabledFlags2 = savedState.getInt(EXTRA_DISABLE2_STATE, 0); 585 mAppearance = savedState.getInt(EXTRA_APPEARANCE, 0); 586 mBehavior = savedState.getInt(EXTRA_BEHAVIOR, 0); 587 mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false); 588 } 589 mSavedState = savedState; 590 591 // Respect the latest disabled-flags. 592 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 593 594 mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); 595 mDeviceProvisionedController.addCallback(mUserSetupListener); 596 mNotificationShadeDepthController.addListener(mDepthListener); 597 598 setAccessibilityFloatingMenuModeIfNeeded(); 599 600 return barView; 601 } 602 destroyView()603 public void destroyView() { 604 mCommandQueue.removeCallback(this); 605 mContext.getSystemService(WindowManager.class).removeViewImmediate( 606 mNavigationBarView.getRootView()); 607 mNavigationModeController.removeListener(this); 608 mAccessibilityButtonModeObserver.removeListener(this); 609 610 mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); 611 mContentResolver.unregisterContentObserver(mAssistContentObserver); 612 mDeviceProvisionedController.removeCallback(mUserSetupListener); 613 mNotificationShadeDepthController.removeListener(mDepthListener); 614 615 DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); 616 } 617 618 @Override onViewAttachedToWindow(View v)619 public void onViewAttachedToWindow(View v) { 620 final Display display = v.getDisplay(); 621 mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController()); 622 mNavigationBarView.setDisabledFlags(mDisabledFlags1); 623 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); 624 mNavigationBarView.setOnTouchListener(this::onNavigationTouch); 625 if (mSavedState != null) { 626 mNavigationBarView.getLightTransitionsController().restoreState(mSavedState); 627 } 628 mNavigationBarView.setNavigationIconHints(mNavigationIconHints); 629 mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); 630 mNavigationBarView.setBehavior(mBehavior); 631 632 mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); 633 634 mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener); 635 mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener); 636 637 prepareNavigationBarView(); 638 checkNavBarModes(); 639 640 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 641 filter.addAction(Intent.ACTION_SCREEN_ON); 642 filter.addAction(Intent.ACTION_USER_SWITCHED); 643 mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, 644 Handler.getMain(), UserHandle.ALL); 645 notifyNavigationBarScreenOn(); 646 647 mOverviewProxyService.addCallback(mOverviewProxyListener); 648 updateSystemUiStateFlags(-1); 649 650 // Currently there is no accelerometer sensor on non-default display. 651 if (mIsOnDefaultDisplay) { 652 final RotationButtonController rotationButtonController = 653 mNavigationBarView.getRotationButtonController(); 654 rotationButtonController.addRotationCallback(mRotationWatcher); 655 656 // Reset user rotation pref to match that of the WindowManager if starting in locked 657 // mode. This will automatically happen when switching from auto-rotate to locked mode. 658 if (display != null && rotationButtonController.isRotationLocked()) { 659 rotationButtonController.setRotationLockedAtAngle(display.getRotation()); 660 } 661 } else { 662 mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS; 663 } 664 setDisabled2Flags(mDisabledFlags2); 665 666 initSecondaryHomeHandleForRotation(); 667 668 // Unfortunately, we still need it because status bar needs LightBarController 669 // before notifications creation. We cannot directly use getLightBarController() 670 // from NavigationBarFragment directly. 671 LightBarController lightBarController = mIsOnDefaultDisplay 672 ? Dependency.get(LightBarController.class) 673 : new LightBarController(mContext, 674 Dependency.get(DarkIconDispatcher.class), 675 Dependency.get(BatteryController.class), 676 Dependency.get(NavigationModeController.class)); 677 setLightBarController(lightBarController); 678 679 // TODO(b/118592525): to support multi-display, we start to add something which is 680 // per-display, while others may be global. I think it's time to 681 // add a new class maybe named DisplayDependency to solve 682 // per-display Dependency problem. 683 AutoHideController autoHideController = mIsOnDefaultDisplay 684 ? Dependency.get(AutoHideController.class) 685 : new AutoHideController(mContext, mHandler, 686 Dependency.get(IWindowManager.class)); 687 setAutoHideController(autoHideController); 688 restoreAppearanceAndTransientState(); 689 } 690 691 @Override onViewDetachedFromWindow(View v)692 public void onViewDetachedFromWindow(View v) { 693 mNavigationBarView.getBarTransitions().destroy(); 694 mNavigationBarView.getLightTransitionsController().destroy(mContext); 695 mOverviewProxyService.removeCallback(mOverviewProxyListener); 696 mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); 697 if (mOrientationHandle != null) { 698 resetSecondaryHandle(); 699 getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); 700 mWindowManager.removeView(mOrientationHandle); 701 mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener( 702 mOrientationHandleGlobalLayoutListener); 703 } 704 mHandler.removeCallbacks(mAutoDim); 705 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 706 mHandler.removeCallbacks(mEnableLayoutTransitions); 707 mNavigationBarView = null; 708 mOrientationHandle = null; 709 } 710 711 // TODO: Remove this when we update nav bar recreation onSaveInstanceState(Bundle outState)712 public void onSaveInstanceState(Bundle outState) { 713 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); 714 outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); 715 outState.putInt(EXTRA_APPEARANCE, mAppearance); 716 outState.putInt(EXTRA_BEHAVIOR, mBehavior); 717 outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); 718 mNavigationBarView.getLightTransitionsController().saveState(outState); 719 } 720 721 /** 722 * Called when a non-reloading configuration change happens and we need to update. 723 */ onConfigurationChanged(Configuration newConfig)724 public void onConfigurationChanged(Configuration newConfig) { 725 final Locale locale = mContext.getResources().getConfiguration().locale; 726 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 727 if (!locale.equals(mLocale) || ld != mLayoutDirection) { 728 if (DEBUG) { 729 Log.v(TAG, String.format( 730 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 731 locale, ld)); 732 } 733 mLocale = locale; 734 mLayoutDirection = ld; 735 refreshLayout(ld); 736 } 737 738 repositionNavigationBar(); 739 if (canShowSecondaryHandle()) { 740 int rotation = newConfig.windowConfiguration.getRotation(); 741 if (rotation != mCurrentRotation) { 742 mCurrentRotation = rotation; 743 orientSecondaryHomeHandle(); 744 } 745 } 746 } 747 initSecondaryHomeHandleForRotation()748 private void initSecondaryHomeHandleForRotation() { 749 if (mNavBarMode != NAV_BAR_MODE_GESTURAL) { 750 return; 751 } 752 753 mOrientationHandle = new QuickswitchOrientedNavHandle(mContext); 754 mOrientationHandle.setId(R.id.secondary_home_handle); 755 756 getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener); 757 mOrientationParams = new WindowManager.LayoutParams(0, 0, 758 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 759 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 760 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 761 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 762 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 763 | WindowManager.LayoutParams.FLAG_SLIPPERY, 764 PixelFormat.TRANSLUCENT); 765 mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); 766 mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; 767 mWindowManager.addView(mOrientationHandle, mOrientationParams); 768 mOrientationHandle.setVisibility(View.GONE); 769 mOrientationParams.setFitInsetsTypes(0 /* types*/); 770 mOrientationHandleGlobalLayoutListener = 771 () -> { 772 if (mStartingQuickSwitchRotation == -1) { 773 return; 774 } 775 776 RectF boundsOnScreen = mOrientationHandle.computeHomeHandleBounds(); 777 mOrientationHandle.mapRectFromViewToScreenCoords(boundsOnScreen, true); 778 Rect boundsRounded = new Rect(); 779 boundsOnScreen.roundOut(boundsRounded); 780 mNavigationBarView.setOrientedHandleSamplingRegion(boundsRounded); 781 }; 782 mOrientationHandle.getViewTreeObserver().addOnGlobalLayoutListener( 783 mOrientationHandleGlobalLayoutListener); 784 } 785 orientSecondaryHomeHandle()786 private void orientSecondaryHomeHandle() { 787 if (!canShowSecondaryHandle()) { 788 return; 789 } 790 791 if (mStartingQuickSwitchRotation == -1 || mSplitScreenOptional 792 .map(LegacySplitScreen::isDividerVisible).orElse(false)) { 793 // Hide the secondary home handle if we are in multiwindow since apps in multiwindow 794 // aren't allowed to set the display orientation 795 resetSecondaryHandle(); 796 } else { 797 int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation); 798 if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) { 799 // Curious if starting quickswitch can change between the if check and our delta 800 Log.d(TAG, "secondary nav delta rotation: " + deltaRotation 801 + " current: " + mCurrentRotation 802 + " starting: " + mStartingQuickSwitchRotation); 803 } 804 int height = 0; 805 int width = 0; 806 Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds(); 807 mOrientationHandle.setDeltaRotation(deltaRotation); 808 switch (deltaRotation) { 809 case Surface.ROTATION_90: 810 case Surface.ROTATION_270: 811 height = dispSize.height(); 812 width = mNavigationBarView.getHeight(); 813 break; 814 case Surface.ROTATION_180: 815 case Surface.ROTATION_0: 816 // TODO(b/152683657): Need to determine best UX for this 817 if (!mShowOrientedHandleForImmersiveMode) { 818 resetSecondaryHandle(); 819 return; 820 } 821 width = dispSize.width(); 822 height = mNavigationBarView.getHeight(); 823 break; 824 } 825 826 mOrientationParams.gravity = 827 deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM : 828 (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT); 829 mOrientationParams.height = height; 830 mOrientationParams.width = width; 831 mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams); 832 mNavigationBarView.setVisibility(View.GONE); 833 mOrientationHandle.setVisibility(View.VISIBLE); 834 } 835 } 836 resetSecondaryHandle()837 private void resetSecondaryHandle() { 838 if (mOrientationHandle != null) { 839 // Case where nav mode is changed w/o ever invoking a quickstep 840 // mOrientedHandle is initialized lazily 841 mOrientationHandle.setVisibility(View.GONE); 842 } 843 mNavigationBarView.setVisibility(View.VISIBLE); 844 mNavigationBarView.setOrientedHandleSamplingRegion(null); 845 } 846 reconfigureHomeLongClick()847 private void reconfigureHomeLongClick() { 848 if (mNavigationBarView.getHomeButton().getCurrentView() == null) { 849 return; 850 } 851 if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) { 852 mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false); 853 mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false); 854 mNavigationBarView.getHomeButton().setOnLongClickListener(null); 855 } else { 856 mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true); 857 mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true); 858 mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick); 859 } 860 } 861 deltaRotation(int oldRotation, int newRotation)862 private int deltaRotation(int oldRotation, int newRotation) { 863 int delta = newRotation - oldRotation; 864 if (delta < 0) delta += 4; 865 return delta; 866 } 867 dump(PrintWriter pw)868 public void dump(PrintWriter pw) { 869 pw.println("NavigationBar (displayId=" + mDisplayId + "):"); 870 pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); 871 pw.println(" mCurrentRotation=" + mCurrentRotation); 872 pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); 873 pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled); 874 pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled); 875 pw.println(" mNavigationBarWindowState=" 876 + windowStateToString(mNavigationBarWindowState)); 877 pw.println(" mNavigationBarMode=" 878 + BarTransitions.modeToString(mNavigationBarMode)); 879 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 880 mNavigationBarView.dump(pw); 881 } 882 883 // ----- CommandQueue Callbacks ----- 884 885 @Override setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)886 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, 887 boolean showImeSwitcher) { 888 if (displayId != mDisplayId) { 889 return; 890 } 891 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 892 int hints = mNavigationIconHints; 893 switch (backDisposition) { 894 case InputMethodService.BACK_DISPOSITION_DEFAULT: 895 case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: 896 case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: 897 if (imeShown) { 898 hints |= NAVIGATION_HINT_BACK_ALT; 899 } else { 900 hints &= ~NAVIGATION_HINT_BACK_ALT; 901 } 902 break; 903 case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: 904 hints &= ~NAVIGATION_HINT_BACK_ALT; 905 break; 906 } 907 if (showImeSwitcher) { 908 hints |= NAVIGATION_HINT_IME_SHOWN; 909 } else { 910 hints &= ~NAVIGATION_HINT_IME_SHOWN; 911 } 912 if (hints == mNavigationIconHints) return; 913 914 mNavigationIconHints = hints; 915 mNavigationBarView.setNavigationIconHints(hints); 916 checkBarModes(); 917 updateSystemUiStateFlags(-1); 918 } 919 920 @Override setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state)921 public void setWindowState( 922 int displayId, @WindowType int window, @WindowVisibleState int state) { 923 if (displayId == mDisplayId 924 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 925 && mNavigationBarWindowState != state) { 926 mNavigationBarWindowState = state; 927 updateSystemUiStateFlags(-1); 928 mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN; 929 if (mOrientationHandle != null 930 && mStartingQuickSwitchRotation != -1) { 931 orientSecondaryHomeHandle(); 932 } 933 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 934 mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); 935 } 936 } 937 938 @Override onRotationProposal(final int rotation, boolean isValid)939 public void onRotationProposal(final int rotation, boolean isValid) { 940 final int winRotation = mNavigationBarView.getDisplay().getRotation(); 941 final boolean rotateSuggestionsDisabled = RotationButtonController 942 .hasDisable2RotateSuggestionFlag(mDisabledFlags2); 943 final RotationButtonController rotationButtonController = 944 mNavigationBarView.getRotationButtonController(); 945 final RotationButton rotationButton = rotationButtonController.getRotationButton(); 946 947 if (RotationContextButton.DEBUG_ROTATION) { 948 Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation) 949 + ", winRotation=" + Surface.rotationToString(winRotation) 950 + ", isValid=" + isValid + ", mNavBarWindowState=" 951 + StatusBarManager.windowStateToString(mNavigationBarWindowState) 952 + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled 953 + ", isRotateButtonVisible=" + rotationButton.isVisible()); 954 } 955 956 // Respect the disabled flag, no need for action as flag change callback will handle hiding 957 if (rotateSuggestionsDisabled) return; 958 959 rotationButtonController.onRotationProposal(rotation, winRotation, isValid); 960 } 961 962 @Override onRecentsAnimationStateChanged(boolean running)963 public void onRecentsAnimationStateChanged(boolean running) { 964 if (running) { 965 mNavbarOverlayController.setButtonState(/* visible */false, /* force */true); 966 } 967 mNavigationBarView.getRotationButtonController().setRecentsAnimationRunning(running); 968 } 969 970 /** Restores the appearance and the transient saved state to {@link NavigationBar}. */ restoreAppearanceAndTransientState()971 public void restoreAppearanceAndTransientState() { 972 final int barMode = barMode(mTransientShown, mAppearance); 973 mNavigationBarMode = barMode; 974 checkNavBarModes(); 975 if (mAutoHideController != null) { 976 mAutoHideController.touchAutoHide(); 977 } 978 if (mLightBarController != null) { 979 mLightBarController.onNavigationBarAppearanceChanged(mAppearance, 980 true /* nbModeChanged */, barMode, false /* navbarColorManagedByIme */); 981 } 982 } 983 984 @Override onSystemBarAttributesChanged(int displayId, @Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @Behavior int behavior, boolean isFullscreen)985 public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, 986 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, 987 @Behavior int behavior, boolean isFullscreen) { 988 if (displayId != mDisplayId) { 989 return; 990 } 991 boolean nbModeChanged = false; 992 if (mAppearance != appearance) { 993 mAppearance = appearance; 994 nbModeChanged = updateBarMode(barMode(mTransientShown, appearance)); 995 } 996 if (mLightBarController != null) { 997 mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, 998 mNavigationBarMode, navbarColorManagedByIme); 999 } 1000 if (mBehavior != behavior) { 1001 mBehavior = behavior; 1002 mNavigationBarView.setBehavior(behavior); 1003 updateSystemUiStateFlags(-1); 1004 } 1005 } 1006 1007 @Override showTransient(int displayId, @InternalInsetsType int[] types)1008 public void showTransient(int displayId, @InternalInsetsType int[] types) { 1009 if (displayId != mDisplayId) { 1010 return; 1011 } 1012 if (!containsType(types, ITYPE_NAVIGATION_BAR)) { 1013 return; 1014 } 1015 if (!mTransientShown) { 1016 mTransientShown = true; 1017 handleTransientChanged(); 1018 } 1019 } 1020 1021 @Override abortTransient(int displayId, @InternalInsetsType int[] types)1022 public void abortTransient(int displayId, @InternalInsetsType int[] types) { 1023 if (displayId != mDisplayId) { 1024 return; 1025 } 1026 if (!containsType(types, ITYPE_NAVIGATION_BAR)) { 1027 return; 1028 } 1029 clearTransient(); 1030 } 1031 clearTransient()1032 private void clearTransient() { 1033 if (mTransientShown) { 1034 mTransientShown = false; 1035 handleTransientChanged(); 1036 } 1037 } 1038 handleTransientChanged()1039 private void handleTransientChanged() { 1040 mNavigationBarView.onTransientStateChanged(mTransientShown); 1041 final int barMode = barMode(mTransientShown, mAppearance); 1042 if (updateBarMode(barMode) && mLightBarController != null) { 1043 mLightBarController.onNavigationBarModeChanged(barMode); 1044 } 1045 } 1046 1047 // Returns true if the bar mode is changed. updateBarMode(int barMode)1048 private boolean updateBarMode(int barMode) { 1049 if (mNavigationBarMode != barMode) { 1050 mNavigationBarMode = barMode; 1051 checkNavBarModes(); 1052 if (mAutoHideController != null) { 1053 mAutoHideController.touchAutoHide(); 1054 } 1055 return true; 1056 } 1057 return false; 1058 } 1059 barMode(boolean isTransient, int appearance)1060 private static @TransitionMode int barMode(boolean isTransient, int appearance) { 1061 final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS; 1062 if (isTransient) { 1063 return MODE_SEMI_TRANSPARENT; 1064 } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) { 1065 return MODE_LIGHTS_OUT; 1066 } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) { 1067 return MODE_LIGHTS_OUT_TRANSPARENT; 1068 } else if ((appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) != 0) { 1069 return MODE_OPAQUE; 1070 } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS) != 0) { 1071 return MODE_SEMI_TRANSPARENT; 1072 } else { 1073 return MODE_TRANSPARENT; 1074 } 1075 } 1076 1077 @Override disable(int displayId, int state1, int state2, boolean animate)1078 public void disable(int displayId, int state1, int state2, boolean animate) { 1079 if (displayId != mDisplayId) { 1080 return; 1081 } 1082 // Navigation bar flags are in both state1 and state2. 1083 final int masked = state1 & (StatusBarManager.DISABLE_HOME 1084 | StatusBarManager.DISABLE_RECENT 1085 | StatusBarManager.DISABLE_BACK 1086 | StatusBarManager.DISABLE_SEARCH); 1087 if (masked != mDisabledFlags1) { 1088 mDisabledFlags1 = masked; 1089 mNavigationBarView.setDisabledFlags(state1); 1090 updateScreenPinningGestures(); 1091 } 1092 1093 // Only default display supports rotation suggestions. 1094 if (mIsOnDefaultDisplay) { 1095 final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS); 1096 if (masked2 != mDisabledFlags2) { 1097 mDisabledFlags2 = masked2; 1098 setDisabled2Flags(masked2); 1099 } 1100 } 1101 } 1102 setDisabled2Flags(int state2)1103 private void setDisabled2Flags(int state2) { 1104 // Method only called on change of disable2 flags 1105 mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2); 1106 } 1107 1108 // ----- Internal stuff ----- 1109 refreshLayout(int layoutDirection)1110 private void refreshLayout(int layoutDirection) { 1111 mNavigationBarView.setLayoutDirection(layoutDirection); 1112 } 1113 shouldDisableNavbarGestures()1114 private boolean shouldDisableNavbarGestures() { 1115 return !mDeviceProvisionedController.isDeviceProvisioned() 1116 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; 1117 } 1118 repositionNavigationBar()1119 private void repositionNavigationBar() { 1120 if (!mNavigationBarView.isAttachedToWindow()) return; 1121 1122 prepareNavigationBarView(); 1123 1124 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(), 1125 ((View) mNavigationBarView.getParent()).getLayoutParams()); 1126 } 1127 updateScreenPinningGestures()1128 private void updateScreenPinningGestures() { 1129 // Change the cancel pin gesture to home and back if recents button is invisible 1130 boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive(); 1131 ButtonDispatcher backButton = mNavigationBarView.getBackButton(); 1132 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); 1133 if (pinningActive) { 1134 boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible(); 1135 backButton.setOnLongClickListener(recentsVisible 1136 ? this::onLongPressBackRecents 1137 : this::onLongPressBackHome); 1138 recentsButton.setOnLongClickListener(this::onLongPressBackRecents); 1139 } else { 1140 backButton.setOnLongClickListener(null); 1141 recentsButton.setOnLongClickListener(null); 1142 } 1143 // Note, this needs to be set after even if we're setting the listener to null 1144 backButton.setLongClickable(pinningActive); 1145 recentsButton.setLongClickable(pinningActive); 1146 } 1147 notifyNavigationBarScreenOn()1148 private void notifyNavigationBarScreenOn() { 1149 mNavigationBarView.updateNavButtonIcons(); 1150 } 1151 prepareNavigationBarView()1152 private void prepareNavigationBarView() { 1153 mNavigationBarView.reorient(); 1154 1155 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); 1156 recentsButton.setOnClickListener(this::onRecentsClick); 1157 recentsButton.setOnTouchListener(this::onRecentsTouch); 1158 1159 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); 1160 homeButton.setOnTouchListener(this::onHomeTouch); 1161 1162 reconfigureHomeLongClick(); 1163 1164 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); 1165 accessibilityButton.setOnClickListener(this::onAccessibilityClick); 1166 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); 1167 updateAccessibilityServicesState(mAccessibilityManager); 1168 1169 ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton(); 1170 imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick); 1171 1172 updateScreenPinningGestures(); 1173 } 1174 1175 @VisibleForTesting onHomeTouch(View v, MotionEvent event)1176 boolean onHomeTouch(View v, MotionEvent event) { 1177 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 1178 return true; 1179 } 1180 // If an incoming call is ringing, HOME is totally disabled. 1181 // (The user is already on the InCallUI at this point, 1182 // and their ONLY options are to answer or reject the call.) 1183 switch (event.getAction()) { 1184 case MotionEvent.ACTION_DOWN: 1185 mHomeBlockedThisTouch = false; 1186 TelecomManager telecomManager = 1187 mContext.getSystemService(TelecomManager.class); 1188 if (telecomManager != null && telecomManager.isRinging()) { 1189 if (mStatusBarLazy.get().isKeyguardShowing()) { 1190 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + 1191 "No heads up"); 1192 mHomeBlockedThisTouch = true; 1193 return true; 1194 } 1195 } 1196 if (mLongPressHomeEnabled) { 1197 mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> { 1198 mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration); 1199 }); 1200 } 1201 break; 1202 case MotionEvent.ACTION_UP: 1203 case MotionEvent.ACTION_CANCEL: 1204 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 1205 mStatusBarLazy.get().awakenDreams(); 1206 break; 1207 } 1208 return false; 1209 } 1210 onVerticalChanged(boolean isVertical)1211 private void onVerticalChanged(boolean isVertical) { 1212 mStatusBarLazy.get().setQsScrimEnabled(!isVertical); 1213 } 1214 onNavigationTouch(View v, MotionEvent event)1215 private boolean onNavigationTouch(View v, MotionEvent event) { 1216 if (mAutoHideController != null) { 1217 mAutoHideController.checkUserAutoHide(event); 1218 } 1219 return false; 1220 } 1221 1222 @VisibleForTesting onHomeLongClick(View v)1223 boolean onHomeLongClick(View v) { 1224 if (!mNavigationBarView.isRecentsButtonVisible() 1225 && ActivityManagerWrapper.getInstance().isScreenPinningActive()) { 1226 return onLongPressBackHome(v); 1227 } 1228 if (shouldDisableNavbarGestures()) { 1229 return false; 1230 } 1231 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS); 1232 mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS); 1233 Bundle args = new Bundle(); 1234 args.putInt( 1235 AssistManager.INVOCATION_TYPE_KEY, 1236 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); 1237 mAssistManagerLazy.get().startAssist(args); 1238 mStatusBarLazy.get().awakenDreams(); 1239 mNavigationBarView.abortCurrentGesture(); 1240 return true; 1241 } 1242 1243 // additional optimization when we have software system buttons - start loading the recent 1244 // tasks on touch down onRecentsTouch(View v, MotionEvent event)1245 private boolean onRecentsTouch(View v, MotionEvent event) { 1246 int action = event.getAction() & MotionEvent.ACTION_MASK; 1247 if (action == MotionEvent.ACTION_DOWN) { 1248 mCommandQueue.preloadRecentApps(); 1249 } else if (action == MotionEvent.ACTION_CANCEL) { 1250 mCommandQueue.cancelPreloadRecentApps(); 1251 } else if (action == MotionEvent.ACTION_UP) { 1252 if (!v.isPressed()) { 1253 mCommandQueue.cancelPreloadRecentApps(); 1254 } 1255 } 1256 return false; 1257 } 1258 onRecentsClick(View v)1259 private void onRecentsClick(View v) { 1260 if (LatencyTracker.isEnabled(mContext)) { 1261 LatencyTracker.getInstance(mContext).onActionStart( 1262 LatencyTracker.ACTION_TOGGLE_RECENTS); 1263 } 1264 mStatusBarLazy.get().awakenDreams(); 1265 mCommandQueue.toggleRecentApps(); 1266 } 1267 onImeSwitcherClick(View v)1268 private void onImeSwitcherClick(View v) { 1269 mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem( 1270 true /* showAuxiliarySubtypes */, mDisplayId); 1271 }; 1272 onLongPressBackHome(View v)1273 private boolean onLongPressBackHome(View v) { 1274 return onLongPressNavigationButtons(v, R.id.back, R.id.home); 1275 } 1276 onLongPressBackRecents(View v)1277 private boolean onLongPressBackRecents(View v) { 1278 return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps); 1279 } 1280 1281 /** 1282 * This handles long-press of both back and recents/home. Back is the common button with 1283 * combination of recents if it is visible or home if recents is invisible. 1284 * They are handled together to capture them both being long-pressed 1285 * at the same time to exit screen pinning (lock task). 1286 * 1287 * When accessibility mode is on, only a long-press from recents/home 1288 * is required to exit. 1289 * 1290 * In all other circumstances we try to pass through long-press events 1291 * for Back, so that apps can still use it. Which can be from two things. 1292 * 1) Not currently in screen pinning (lock task). 1293 * 2) Back is long-pressed without recents/home. 1294 */ onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2)1295 private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) { 1296 try { 1297 boolean sendBackLongPress = false; 1298 IActivityTaskManager activityManager = ActivityTaskManager.getService(); 1299 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); 1300 boolean inLockTaskMode = activityManager.isInLockTaskMode(); 1301 boolean stopLockTaskMode = false; 1302 try { 1303 if (inLockTaskMode && !touchExplorationEnabled) { 1304 long time = System.currentTimeMillis(); 1305 1306 // If we recently long-pressed the other button then they were 1307 // long-pressed 'together' 1308 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { 1309 stopLockTaskMode = true; 1310 return true; 1311 } else if (v.getId() == btnId1) { 1312 ButtonDispatcher button = btnId2 == R.id.recent_apps 1313 ? mNavigationBarView.getRecentsButton() 1314 : mNavigationBarView.getHomeButton(); 1315 if (!button.getCurrentView().isPressed()) { 1316 // If we aren't pressing recents/home right now then they presses 1317 // won't be together, so send the standard long-press action. 1318 sendBackLongPress = true; 1319 } 1320 } 1321 mLastLockToAppLongPress = time; 1322 } else { 1323 // If this is back still need to handle sending the long-press event. 1324 if (v.getId() == btnId1) { 1325 sendBackLongPress = true; 1326 } else if (touchExplorationEnabled && inLockTaskMode) { 1327 // When in accessibility mode a long press that is recents/home (not back) 1328 // should stop lock task. 1329 stopLockTaskMode = true; 1330 return true; 1331 } else if (v.getId() == btnId2) { 1332 return btnId2 == R.id.recent_apps 1333 ? onLongPressRecents() 1334 : onHomeLongClick( 1335 mNavigationBarView.getHomeButton().getCurrentView()); 1336 } 1337 } 1338 } finally { 1339 if (stopLockTaskMode) { 1340 activityManager.stopSystemLockTaskMode(); 1341 // When exiting refresh disabled flags. 1342 mNavigationBarView.updateNavButtonIcons(); 1343 } 1344 } 1345 1346 if (sendBackLongPress) { 1347 KeyButtonView keyButtonView = (KeyButtonView) v; 1348 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 1349 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 1350 return true; 1351 } 1352 } catch (RemoteException e) { 1353 Log.d(TAG, "Unable to reach activity manager", e); 1354 } 1355 return false; 1356 } 1357 onLongPressRecents()1358 private boolean onLongPressRecents() { 1359 if (mRecentsOptional.isPresent() || !ActivityTaskManager.supportsMultiWindow(mContext) 1360 || ActivityManager.isLowRamDeviceStatic() 1361 // If we are connected to the overview service, then disable the recents button 1362 || mOverviewProxyService.getProxy() != null 1363 || !mSplitScreenOptional.map(splitScreen -> 1364 splitScreen.getDividerView().getSnapAlgorithm().isSplitScreenFeasible()) 1365 .orElse(false)) { 1366 return false; 1367 } 1368 1369 return mStatusBarLazy.get().toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS, 1370 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS); 1371 } 1372 onAccessibilityClick(View v)1373 private void onAccessibilityClick(View v) { 1374 final Display display = v.getDisplay(); 1375 mAccessibilityManager.notifyAccessibilityButtonClicked( 1376 display != null ? display.getDisplayId() : DEFAULT_DISPLAY); 1377 } 1378 onAccessibilityLongClick(View v)1379 private boolean onAccessibilityLongClick(View v) { 1380 final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 1381 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 1382 final String chooserClassName = AccessibilityButtonChooserActivity.class.getName(); 1383 intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); 1384 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 1385 return true; 1386 } 1387 updateAccessibilityServicesState(AccessibilityManager accessibilityManager)1388 void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { 1389 boolean[] feedbackEnabled = new boolean[1]; 1390 int a11yFlags = getA11yButtonState(feedbackEnabled); 1391 1392 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 1393 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 1394 mNavigationBarView.setAccessibilityButtonState(clickable, longClickable); 1395 1396 updateSystemUiStateFlags(a11yFlags); 1397 } 1398 setAccessibilityFloatingMenuModeIfNeeded()1399 private void setAccessibilityFloatingMenuModeIfNeeded() { 1400 if (QuickStepContract.isGesturalMode(mNavBarMode)) { 1401 Settings.Secure.putIntForUser(mContentResolver, 1402 Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 1403 ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU, UserHandle.USER_CURRENT); 1404 } 1405 } 1406 updateSystemUiStateFlags(int a11yFlags)1407 public void updateSystemUiStateFlags(int a11yFlags) { 1408 if (a11yFlags < 0) { 1409 a11yFlags = getA11yButtonState(null); 1410 } 1411 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 1412 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 1413 1414 mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable) 1415 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable) 1416 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) 1417 .setFlag(SYSUI_STATE_IME_SHOWING, 1418 (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) 1419 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, 1420 allowSystemGestureIgnoringBarVisibility()) 1421 .commitUpdate(mDisplayId); 1422 registerAction(clickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON); 1423 registerAction(longClickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER); 1424 } 1425 registerAction(boolean register, int actionId)1426 private void registerAction(boolean register, int actionId) { 1427 if (register) { 1428 mSystemActions.register(actionId); 1429 } else { 1430 mSystemActions.unregister(actionId); 1431 } 1432 } 1433 1434 /** 1435 * Returns the system UI flags corresponding the the current accessibility button state 1436 * 1437 * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled. 1438 */ getA11yButtonState(@ullable boolean[] outFeedbackEnabled)1439 public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) { 1440 boolean feedbackEnabled = false; 1441 // AccessibilityManagerService resolves services for the current user since the local 1442 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission 1443 final List<AccessibilityServiceInfo> services = 1444 mAccessibilityManager.getEnabledAccessibilityServiceList( 1445 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 1446 final List<String> a11yButtonTargets = 1447 mAccessibilityManager.getAccessibilityShortcutTargets( 1448 AccessibilityManager.ACCESSIBILITY_BUTTON); 1449 final int requestingServices = a11yButtonTargets.size(); 1450 for (int i = services.size() - 1; i >= 0; --i) { 1451 AccessibilityServiceInfo info = services.get(i); 1452 if (info.feedbackType != 0 && info.feedbackType != 1453 AccessibilityServiceInfo.FEEDBACK_GENERIC) { 1454 feedbackEnabled = true; 1455 } 1456 } 1457 1458 if (outFeedbackEnabled != null) { 1459 outFeedbackEnabled[0] = feedbackEnabled; 1460 } 1461 1462 // If accessibility button is floating menu mode, click and long click state should be 1463 // disabled. 1464 if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode() 1465 == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { 1466 return 0; 1467 } 1468 1469 return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0) 1470 | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0); 1471 } 1472 updateAssistantEntrypoints()1473 private void updateAssistantEntrypoints() { 1474 mAssistantAvailable = mAssistManagerLazy.get() 1475 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; 1476 boolean longPressDefault = mContext.getResources().getBoolean( 1477 com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault); 1478 mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver, 1479 Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0, 1480 mUserTracker.getUserId()) != 0; 1481 boolean gestureDefault = mContext.getResources().getBoolean( 1482 com.android.internal.R.bool.config_assistTouchGestureEnabledDefault); 1483 mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver, 1484 Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0, 1485 mUserTracker.getUserId()) != 0; 1486 if (mOverviewProxyService.getProxy() != null) { 1487 try { 1488 mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable 1489 && mAssistantTouchGestureEnabled 1490 && QuickStepContract.isGesturalMode(mNavBarMode)); 1491 } catch (RemoteException e) { 1492 Log.w(TAG, "Unable to send assistant availability data to launcher"); 1493 } 1494 } 1495 reconfigureHomeLongClick(); 1496 } 1497 1498 // ----- Methods that DisplayNavigationBarController talks to ----- 1499 1500 /** Applies auto dimming animation on navigation bar when touched. */ touchAutoDim()1501 public void touchAutoDim() { 1502 getBarTransitions().setAutoDim(false); 1503 mHandler.removeCallbacks(mAutoDim); 1504 int state = mStatusBarStateController.getState(); 1505 if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) { 1506 mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS); 1507 } 1508 } 1509 setLightBarController(LightBarController lightBarController)1510 public void setLightBarController(LightBarController lightBarController) { 1511 mLightBarController = lightBarController; 1512 if (mLightBarController != null) { 1513 mLightBarController.setNavigationBar( 1514 mNavigationBarView.getLightTransitionsController()); 1515 } 1516 } 1517 1518 /** Sets {@link AutoHideController} to the navigation bar. */ setAutoHideController(AutoHideController autoHideController)1519 public void setAutoHideController(AutoHideController autoHideController) { 1520 mAutoHideController = autoHideController; 1521 if (mAutoHideController != null) { 1522 mAutoHideController.setNavigationBar(mAutoHideUiElement); 1523 } 1524 mNavigationBarView.setAutoHideController(autoHideController); 1525 } 1526 isTransientShown()1527 private boolean isTransientShown() { 1528 return mTransientShown; 1529 } 1530 checkBarModes()1531 private void checkBarModes() { 1532 // We only have status bar on default display now. 1533 if (mIsOnDefaultDisplay) { 1534 mStatusBarLazy.get().checkBarModes(); 1535 } else { 1536 checkNavBarModes(); 1537 } 1538 } 1539 isNavBarWindowVisible()1540 public boolean isNavBarWindowVisible() { 1541 return mNavigationBarWindowState == WINDOW_STATE_SHOWING; 1542 } 1543 allowSystemGestureIgnoringBarVisibility()1544 private boolean allowSystemGestureIgnoringBarVisibility() { 1545 return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 1546 } 1547 1548 /** 1549 * Checks current navigation bar mode and make transitions. 1550 */ checkNavBarModes()1551 public void checkNavBarModes() { 1552 final boolean anim = mStatusBarLazy.get().isDeviceInteractive() 1553 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN; 1554 mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim); 1555 } 1556 1557 @Override onNavigationModeChanged(int mode)1558 public void onNavigationModeChanged(int mode) { 1559 mNavBarMode = mode; 1560 if (!QuickStepContract.isGesturalMode(mode)) { 1561 // Reset the override alpha 1562 if (getBarTransitions() != null) { 1563 getBarTransitions().setBackgroundOverrideAlpha(1f); 1564 } 1565 } 1566 updateScreenPinningGestures(); 1567 setAccessibilityFloatingMenuModeIfNeeded(); 1568 1569 if (!canShowSecondaryHandle()) { 1570 resetSecondaryHandle(); 1571 } 1572 } 1573 1574 @Override onAccessibilityButtonModeChanged(int mode)1575 public void onAccessibilityButtonModeChanged(int mode) { 1576 updateAccessibilityServicesState(mAccessibilityManager); 1577 } 1578 disableAnimationsDuringHide(long delay)1579 public void disableAnimationsDuringHide(long delay) { 1580 mNavigationBarView.setLayoutTransitionsEnabled(false); 1581 mHandler.postDelayed(mEnableLayoutTransitions, 1582 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); 1583 } 1584 1585 /** 1586 * Performs transitions on navigation bar. 1587 * 1588 * @param barMode transition bar mode. 1589 * @param animate shows animations if {@code true}. 1590 */ transitionTo(@ransitionMode int barMode, boolean animate)1591 public void transitionTo(@TransitionMode int barMode, boolean animate) { 1592 getBarTransitions().transitionTo(barMode, animate); 1593 } 1594 getBarTransitions()1595 public NavigationBarTransitions getBarTransitions() { 1596 return mNavigationBarView.getBarTransitions(); 1597 } 1598 finishBarAnimations()1599 public void finishBarAnimations() { 1600 mNavigationBarView.getBarTransitions().finishAnimations(); 1601 } 1602 1603 private final AccessibilityServicesStateChangeListener mAccessibilityListener = 1604 this::updateAccessibilityServicesState; 1605 canShowSecondaryHandle()1606 private boolean canShowSecondaryHandle() { 1607 return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null; 1608 } 1609 1610 private final Consumer<Integer> mRotationWatcher = rotation -> { 1611 if (mNavigationBarView.needsReorient(rotation)) { 1612 repositionNavigationBar(); 1613 } 1614 }; 1615 1616 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1617 @Override 1618 public void onReceive(Context context, Intent intent) { 1619 // TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is 1620 // async, but we've already cleared the fields. Just return early in this case. 1621 if (mNavigationBarView == null) { 1622 return; 1623 } 1624 String action = intent.getAction(); 1625 if (Intent.ACTION_SCREEN_OFF.equals(action) 1626 || Intent.ACTION_SCREEN_ON.equals(action)) { 1627 notifyNavigationBarScreenOn(); 1628 mNavigationBarView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action)); 1629 } 1630 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 1631 // The accessibility settings may be different for the new user 1632 updateAccessibilityServicesState(mAccessibilityManager); 1633 } 1634 } 1635 }; 1636 1637 @VisibleForTesting getNavigationIconHints()1638 int getNavigationIconHints() { 1639 return mNavigationIconHints; 1640 } 1641 } 1642