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.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons; 20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; 21 22 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; 23 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED; 25 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; 26 27 import android.animation.LayoutTransition; 28 import android.animation.LayoutTransition.TransitionListener; 29 import android.animation.ObjectAnimator; 30 import android.animation.PropertyValuesHolder; 31 import android.animation.TimeInterpolator; 32 import android.animation.ValueAnimator; 33 import android.annotation.DrawableRes; 34 import android.app.StatusBarManager; 35 import android.content.Context; 36 import android.content.res.Configuration; 37 import android.graphics.Canvas; 38 import android.graphics.Point; 39 import android.graphics.Rect; 40 import android.os.Bundle; 41 import android.os.RemoteException; 42 import android.util.AttributeSet; 43 import android.util.Log; 44 import android.util.SparseArray; 45 import android.view.ContextThemeWrapper; 46 import android.view.Display; 47 import android.view.MotionEvent; 48 import android.view.Surface; 49 import android.view.View; 50 import android.view.ViewGroup; 51 import android.view.WindowInsets; 52 import android.view.WindowInsetsController.Behavior; 53 import android.view.WindowManager; 54 import android.view.WindowManagerGlobal; 55 import android.view.accessibility.AccessibilityNodeInfo; 56 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 57 import android.widget.FrameLayout; 58 59 import androidx.annotation.Nullable; 60 61 import com.android.app.animation.Interpolators; 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.settingslib.Utils; 64 import com.android.systemui.Gefingerpoken; 65 import com.android.systemui.R; 66 import com.android.systemui.model.SysUiState; 67 import com.android.systemui.navigationbar.buttons.ButtonDispatcher; 68 import com.android.systemui.navigationbar.buttons.ContextualButton; 69 import com.android.systemui.navigationbar.buttons.ContextualButtonGroup; 70 import com.android.systemui.navigationbar.buttons.DeadZone; 71 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; 72 import com.android.systemui.navigationbar.buttons.NearestTouchFrame; 73 import com.android.systemui.navigationbar.buttons.RotationContextButton; 74 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; 75 import com.android.systemui.recents.Recents; 76 import com.android.systemui.settings.DisplayTracker; 77 import com.android.systemui.shade.ShadeViewController; 78 import com.android.systemui.shared.rotation.FloatingRotationButton; 79 import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback; 80 import com.android.systemui.shared.rotation.RotationButtonController; 81 import com.android.systemui.shared.system.QuickStepContract; 82 import com.android.systemui.statusbar.phone.AutoHideController; 83 import com.android.systemui.statusbar.phone.CentralSurfaces; 84 import com.android.systemui.statusbar.phone.LightBarTransitionsController; 85 import com.android.wm.shell.back.BackAnimation; 86 import com.android.wm.shell.pip.Pip; 87 88 import java.io.PrintWriter; 89 import java.util.Map; 90 import java.util.Optional; 91 import java.util.concurrent.Executor; 92 import java.util.function.Consumer; 93 94 /** */ 95 public class NavigationBarView extends FrameLayout { 96 final static boolean DEBUG = false; 97 final static String TAG = "NavBarView"; 98 99 final static boolean ALTERNATE_CAR_MODE_UI = false; 100 101 private Executor mBgExecutor; 102 103 // The current view is one of mHorizontal or mVertical depending on the current configuration 104 View mCurrentView = null; 105 private View mVertical; 106 private View mHorizontal; 107 108 /** Indicates that navigation bar is vertical. */ 109 private boolean mIsVertical; 110 private int mCurrentRotation = -1; 111 112 boolean mLongClickableAccessibilityButton; 113 int mDisabledFlags = 0; 114 int mNavigationIconHints = 0; 115 private int mNavBarMode; 116 private boolean mImeDrawsImeNavBar; 117 118 private KeyButtonDrawable mBackIcon; 119 private KeyButtonDrawable mHomeDefaultIcon; 120 private KeyButtonDrawable mRecentIcon; 121 private KeyButtonDrawable mDockedIcon; 122 private Context mLightContext; 123 private int mLightIconColor; 124 private int mDarkIconColor; 125 126 private EdgeBackGestureHandler mEdgeBackGestureHandler; 127 private DisplayTracker mDisplayTracker; 128 private final DeadZone mDeadZone; 129 private NavigationBarTransitions mBarTransitions; 130 @Nullable 131 private AutoHideController mAutoHideController; 132 133 // performs manual animation in sync with layout transitions 134 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 135 136 private OnVerticalChangedListener mOnVerticalChangedListener; 137 private boolean mLayoutTransitionsEnabled = true; 138 private boolean mWakeAndUnlocking; 139 private boolean mUseCarModeUi = false; 140 private boolean mInCarMode = false; 141 private boolean mDockedStackExists; 142 private boolean mScreenOn = true; 143 144 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>(); 145 private final ContextualButtonGroup mContextualButtonGroup; 146 private Configuration mConfiguration; 147 private Configuration mTmpLastConfiguration; 148 149 private NavigationBarInflaterView mNavigationInflaterView; 150 private Optional<Recents> mRecentsOptional = Optional.empty(); 151 @Nullable 152 private ShadeViewController mPanelView; 153 private RotationContextButton mRotationContextButton; 154 private FloatingRotationButton mFloatingRotationButton; 155 private RotationButtonController mRotationButtonController; 156 157 /** 158 * Helper that is responsible for showing the right toast when a disallowed activity operation 159 * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in 160 * fully locked mode we only show that unlocking is blocked. 161 */ 162 private ScreenPinningNotify mScreenPinningNotify; 163 private boolean mScreenPinningActive = false; 164 165 /** 166 * {@code true} if the IME can render the back button and the IME switcher button. 167 * 168 * <p>The value must be used when and only when 169 * {@link com.android.systemui.shared.system.QuickStepContract#isGesturalMode(int)} returns 170 * {@code true}</p> 171 * 172 * <p>Cache the value here for better performance.</p> 173 */ 174 private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons(); 175 private Gefingerpoken mTouchHandler; 176 private boolean mOverviewProxyEnabled; 177 private boolean mShowSwipeUpUi; 178 private UpdateActiveTouchRegionsCallback mUpdateActiveTouchRegionsCallback; 179 180 private class NavTransitionListener implements TransitionListener { 181 private boolean mBackTransitioning; 182 private boolean mHomeAppearing; 183 private long mStartDelay; 184 private long mDuration; 185 private TimeInterpolator mInterpolator; 186 187 @Override startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)188 public void startTransition(LayoutTransition transition, ViewGroup container, 189 View view, int transitionType) { 190 if (view.getId() == R.id.back) { 191 mBackTransitioning = true; 192 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 193 mHomeAppearing = true; 194 mStartDelay = transition.getStartDelay(transitionType); 195 mDuration = transition.getDuration(transitionType); 196 mInterpolator = transition.getInterpolator(transitionType); 197 } 198 } 199 200 @Override endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)201 public void endTransition(LayoutTransition transition, ViewGroup container, 202 View view, int transitionType) { 203 if (view.getId() == R.id.back) { 204 mBackTransitioning = false; 205 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 206 mHomeAppearing = false; 207 } 208 } 209 onBackAltCleared()210 public void onBackAltCleared() { 211 ButtonDispatcher backButton = getBackButton(); 212 213 // When dismissing ime during unlock, force the back button to run the same appearance 214 // animation as home (if we catch this condition early enough). 215 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE 216 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 217 getBackButton().setAlpha(0); 218 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1); 219 a.setStartDelay(mStartDelay); 220 a.setDuration(mDuration); 221 a.setInterpolator(mInterpolator); 222 a.start(); 223 } 224 } 225 } 226 227 private final AccessibilityDelegate mQuickStepAccessibilityDelegate = 228 new AccessibilityDelegate() { 229 private AccessibilityAction mToggleOverviewAction; 230 231 @Override 232 public void onInitializeAccessibilityNodeInfo(View host, 233 AccessibilityNodeInfo info) { 234 super.onInitializeAccessibilityNodeInfo(host, info); 235 if (mToggleOverviewAction == null) { 236 mToggleOverviewAction = new AccessibilityAction( 237 R.id.action_toggle_overview, getContext().getString( 238 R.string.quick_step_accessibility_toggle_overview)); 239 } 240 info.addAction(mToggleOverviewAction); 241 } 242 243 @Override 244 public boolean performAccessibilityAction(View host, int action, Bundle args) { 245 if (action == R.id.action_toggle_overview) { 246 mRecentsOptional.ifPresent(Recents::toggleRecentApps); 247 } else { 248 return super.performAccessibilityAction(host, action, args); 249 } 250 return true; 251 } 252 }; 253 254 private final RotationButtonUpdatesCallback mRotationButtonListener = 255 new RotationButtonUpdatesCallback() { 256 @Override 257 public void onVisibilityChanged(boolean visible) { 258 if (visible && mAutoHideController != null) { 259 // If the button will actually become visible and the navbar is about 260 // to hide, tell the statusbar to keep it around for longer 261 mAutoHideController.touchAutoHide(); 262 } 263 notifyActiveTouchRegions(); 264 } 265 266 @Override 267 public void onPositionChanged() { 268 notifyActiveTouchRegions(); 269 } 270 }; 271 NavigationBarView(Context context, AttributeSet attrs)272 public NavigationBarView(Context context, AttributeSet attrs) { 273 super(context, attrs); 274 275 final Context darkContext = new ContextThemeWrapper(context, 276 Utils.getThemeAttr(context, R.attr.darkIconTheme)); 277 mLightContext = new ContextThemeWrapper(context, 278 Utils.getThemeAttr(context, R.attr.lightIconTheme)); 279 mLightIconColor = Utils.getColorAttrDefaultColor(mLightContext, R.attr.singleToneColor); 280 mDarkIconColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); 281 mIsVertical = false; 282 mLongClickableAccessibilityButton = false; 283 284 // Set up the context group of buttons 285 mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container); 286 final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher, 287 mLightContext, R.drawable.ic_ime_switcher_default); 288 final ContextualButton accessibilityButton = 289 new ContextualButton(R.id.accessibility_button, mLightContext, 290 R.drawable.ic_sysbar_accessibility_button); 291 mContextualButtonGroup.addButton(imeSwitcherButton); 292 mContextualButtonGroup.addButton(accessibilityButton); 293 mRotationContextButton = new RotationContextButton(R.id.rotate_suggestion, 294 mLightContext, R.drawable.ic_sysbar_rotate_button_ccw_start_0); 295 mFloatingRotationButton = new FloatingRotationButton(mContext, 296 R.string.accessibility_rotate_button, 297 R.layout.rotate_suggestion, 298 R.id.rotate_suggestion, 299 R.dimen.floating_rotation_button_min_margin, 300 R.dimen.rounded_corner_content_padding, 301 R.dimen.floating_rotation_button_taskbar_left_margin, 302 R.dimen.floating_rotation_button_taskbar_bottom_margin, 303 R.dimen.floating_rotation_button_diameter, 304 R.dimen.key_button_ripple_max_width, 305 R.bool.floating_rotation_button_position_left); 306 mRotationButtonController = new RotationButtonController(mLightContext, mLightIconColor, 307 mDarkIconColor, R.drawable.ic_sysbar_rotate_button_ccw_start_0, 308 R.drawable.ic_sysbar_rotate_button_ccw_start_90, 309 R.drawable.ic_sysbar_rotate_button_cw_start_0, 310 R.drawable.ic_sysbar_rotate_button_cw_start_90, 311 () -> mCurrentRotation); 312 313 mConfiguration = new Configuration(); 314 mTmpLastConfiguration = new Configuration(); 315 mConfiguration.updateFrom(context.getResources().getConfiguration()); 316 317 mScreenPinningNotify = new ScreenPinningNotify(mContext); 318 319 mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); 320 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); 321 mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); 322 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); 323 mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton); 324 mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton); 325 mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup); 326 mDeadZone = new DeadZone(this); 327 } 328 setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler)329 public void setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler) { 330 mEdgeBackGestureHandler = edgeBackGestureHandler; 331 } 332 setBarTransitions(NavigationBarTransitions navigationBarTransitions)333 void setBarTransitions(NavigationBarTransitions navigationBarTransitions) { 334 mBarTransitions = navigationBarTransitions; 335 } 336 setAutoHideController(AutoHideController autoHideController)337 public void setAutoHideController(AutoHideController autoHideController) { 338 mAutoHideController = autoHideController; 339 } 340 getLightTransitionsController()341 public LightBarTransitionsController getLightTransitionsController() { 342 return mBarTransitions.getLightTransitionsController(); 343 } 344 setComponents(Optional<Recents> recentsOptional)345 public void setComponents(Optional<Recents> recentsOptional) { 346 mRecentsOptional = recentsOptional; 347 } 348 349 /** */ setComponents(ShadeViewController panel)350 public void setComponents(ShadeViewController panel) { 351 mPanelView = panel; 352 updatePanelSystemUiStateFlags(); 353 } 354 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)355 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 356 mOnVerticalChangedListener = onVerticalChangedListener; 357 notifyVerticalChangedListener(mIsVertical); 358 } 359 setBackgroundExecutor(Executor bgExecutor)360 public void setBackgroundExecutor(Executor bgExecutor) { 361 mBgExecutor = bgExecutor; 362 mRotationButtonController.setBgExecutor(bgExecutor); 363 } 364 setDisplayTracker(DisplayTracker displayTracker)365 public void setDisplayTracker(DisplayTracker displayTracker) { 366 mDisplayTracker = displayTracker; 367 } 368 setTouchHandler(Gefingerpoken touchHandler)369 public void setTouchHandler(Gefingerpoken touchHandler) { 370 mTouchHandler = touchHandler; 371 } 372 373 @Override onInterceptTouchEvent(MotionEvent event)374 public boolean onInterceptTouchEvent(MotionEvent event) { 375 return mTouchHandler.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event); 376 } 377 378 @Override onTouchEvent(MotionEvent event)379 public boolean onTouchEvent(MotionEvent event) { 380 mTouchHandler.onTouchEvent(event); 381 return super.onTouchEvent(event); 382 } 383 abortCurrentGesture()384 public void abortCurrentGesture() { 385 getHomeButton().abortCurrentGesture(); 386 } 387 getCurrentView()388 public View getCurrentView() { 389 return mCurrentView; 390 } 391 392 /** 393 * Applies {@param consumer} to each of the nav bar views. 394 */ forEachView(Consumer<View> consumer)395 public void forEachView(Consumer<View> consumer) { 396 if (mVertical != null) { 397 consumer.accept(mVertical); 398 } 399 if (mHorizontal != null) { 400 consumer.accept(mHorizontal); 401 } 402 } 403 getRotationButtonController()404 public RotationButtonController getRotationButtonController() { 405 return mRotationButtonController; 406 } 407 getFloatingRotationButton()408 public FloatingRotationButton getFloatingRotationButton() { 409 return mFloatingRotationButton; 410 } 411 getRecentsButton()412 public ButtonDispatcher getRecentsButton() { 413 return mButtonDispatchers.get(R.id.recent_apps); 414 } 415 getBackButton()416 public ButtonDispatcher getBackButton() { 417 return mButtonDispatchers.get(R.id.back); 418 } 419 getHomeButton()420 public ButtonDispatcher getHomeButton() { 421 return mButtonDispatchers.get(R.id.home); 422 } 423 getImeSwitchButton()424 public ButtonDispatcher getImeSwitchButton() { 425 return mButtonDispatchers.get(R.id.ime_switcher); 426 } 427 getAccessibilityButton()428 public ButtonDispatcher getAccessibilityButton() { 429 return mButtonDispatchers.get(R.id.accessibility_button); 430 } 431 getRotateSuggestionButton()432 public RotationContextButton getRotateSuggestionButton() { 433 return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion); 434 } 435 getHomeHandle()436 public ButtonDispatcher getHomeHandle() { 437 return mButtonDispatchers.get(R.id.home_handle); 438 } 439 getButtonDispatchers()440 public SparseArray<ButtonDispatcher> getButtonDispatchers() { 441 return mButtonDispatchers; 442 } 443 isRecentsButtonVisible()444 public boolean isRecentsButtonVisible() { 445 return getRecentsButton().getVisibility() == View.VISIBLE; 446 } 447 isOverviewEnabled()448 public boolean isOverviewEnabled() { 449 return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0; 450 } 451 isQuickStepSwipeUpEnabled()452 private boolean isQuickStepSwipeUpEnabled() { 453 return mShowSwipeUpUi && isOverviewEnabled(); 454 } 455 reloadNavIcons()456 private void reloadNavIcons() { 457 updateIcons(Configuration.EMPTY); 458 } 459 updateIcons(Configuration oldConfig)460 private void updateIcons(Configuration oldConfig) { 461 final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation; 462 final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi; 463 final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection(); 464 465 if (orientationChange || densityChange) { 466 mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked); 467 mHomeDefaultIcon = getHomeDrawable(); 468 } 469 if (densityChange || dirChange) { 470 mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent); 471 mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor); 472 } 473 if (orientationChange || densityChange || dirChange) { 474 mBackIcon = getBackDrawable(); 475 } 476 } 477 478 /** 479 * Updates the rotation button based on the current navigation mode. 480 */ updateRotationButton()481 void updateRotationButton() { 482 if (isGesturalMode(mNavBarMode)) { 483 mContextualButtonGroup.removeButton(R.id.rotate_suggestion); 484 mButtonDispatchers.remove(R.id.rotate_suggestion); 485 mRotationButtonController.setRotationButton(mFloatingRotationButton, 486 mRotationButtonListener); 487 } else if (mContextualButtonGroup.getContextButton(R.id.rotate_suggestion) == null) { 488 mContextualButtonGroup.addButton(mRotationContextButton); 489 mButtonDispatchers.put(R.id.rotate_suggestion, mRotationContextButton); 490 mRotationButtonController.setRotationButton(mRotationContextButton, 491 mRotationButtonListener); 492 } 493 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 494 } 495 getBackDrawable()496 public KeyButtonDrawable getBackDrawable() { 497 KeyButtonDrawable drawable = getDrawable(getBackDrawableRes()); 498 orientBackButton(drawable); 499 return drawable; 500 } 501 getBackDrawableRes()502 public @DrawableRes int getBackDrawableRes() { 503 return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back, 504 R.drawable.ic_sysbar_back_quick_step); 505 } 506 getHomeDrawable()507 public KeyButtonDrawable getHomeDrawable() { 508 KeyButtonDrawable drawable = mShowSwipeUpUi 509 ? getDrawable(R.drawable.ic_sysbar_home_quick_step) 510 : getDrawable(R.drawable.ic_sysbar_home); 511 orientHomeButton(drawable); 512 return drawable; 513 } 514 orientBackButton(KeyButtonDrawable drawable)515 private void orientBackButton(KeyButtonDrawable drawable) { 516 final boolean useAltBack = 517 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 518 final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 519 float degrees = useAltBack ? (isRtl ? 90 : -90) : 0; 520 if (drawable.getRotation() == degrees) { 521 return; 522 } 523 524 if (isGesturalMode(mNavBarMode)) { 525 drawable.setRotation(degrees); 526 return; 527 } 528 529 // Animate the back button's rotation to the new degrees and only in portrait move up the 530 // back button to line up with the other buttons 531 float targetY = !mShowSwipeUpUi && !mIsVertical && useAltBack 532 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset) 533 : 0; 534 ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable, 535 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees), 536 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY)); 537 navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 538 navBarAnimator.setDuration(200); 539 navBarAnimator.start(); 540 } 541 orientHomeButton(KeyButtonDrawable drawable)542 private void orientHomeButton(KeyButtonDrawable drawable) { 543 drawable.setRotation(mIsVertical ? 90 : 0); 544 } 545 chooseNavigationIconDrawableRes(@rawableRes int icon, @DrawableRes int quickStepIcon)546 private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon, 547 @DrawableRes int quickStepIcon) { 548 return mShowSwipeUpUi ? quickStepIcon : icon; 549 } 550 getDrawable(@rawableRes int icon)551 private KeyButtonDrawable getDrawable(@DrawableRes int icon) { 552 return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon, 553 true /* hasShadow */, null /* ovalBackgroundColor */); 554 } 555 556 /** To be called when screen lock/unlock state changes */ onScreenStateChanged(boolean isScreenOn)557 public void onScreenStateChanged(boolean isScreenOn) { 558 mScreenOn = isScreenOn; 559 } 560 setWindowVisible(boolean visible)561 public void setWindowVisible(boolean visible) { 562 mRotationButtonController.onNavigationBarWindowVisibilityChange(visible); 563 } 564 setBehavior(@ehavior int behavior)565 public void setBehavior(@Behavior int behavior) { 566 mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(), 567 behavior); 568 } 569 570 @Override setLayoutDirection(int layoutDirection)571 public void setLayoutDirection(int layoutDirection) { 572 reloadNavIcons(); 573 574 super.setLayoutDirection(layoutDirection); 575 } 576 setNavigationIconHints(int hints)577 void setNavigationIconHints(int hints) { 578 if (hints == mNavigationIconHints) return; 579 mNavigationIconHints = hints; 580 updateNavButtonIcons(); 581 } 582 onImeVisibilityChanged(boolean visible)583 void onImeVisibilityChanged(boolean visible) { 584 if (!visible) { 585 mTransitionListener.onBackAltCleared(); 586 } 587 mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible); 588 } 589 setDisabledFlags(int disabledFlags, SysUiState sysUiState)590 void setDisabledFlags(int disabledFlags, SysUiState sysUiState) { 591 if (mDisabledFlags == disabledFlags) return; 592 593 final boolean overviewEnabledBefore = isOverviewEnabled(); 594 mDisabledFlags = disabledFlags; 595 596 // Update icons if overview was just enabled to ensure the correct icons are present 597 if (!overviewEnabledBefore && isOverviewEnabled()) { 598 reloadNavIcons(); 599 } 600 601 updateNavButtonIcons(); 602 updateSlippery(); 603 updateDisabledSystemUiStateFlags(sysUiState); 604 } 605 updateNavButtonIcons()606 public void updateNavButtonIcons() { 607 // We have to replace or restore the back and home button icons when exiting or entering 608 // carmode, respectively. Recents are not available in CarMode in nav bar so change 609 // to recent icon is not required. 610 final boolean useAltBack = 611 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 612 KeyButtonDrawable backIcon = mBackIcon; 613 orientBackButton(backIcon); 614 KeyButtonDrawable homeIcon = mHomeDefaultIcon; 615 if (!mUseCarModeUi) { 616 orientHomeButton(homeIcon); 617 } 618 getHomeButton().setImageDrawable(homeIcon); 619 getBackButton().setImageDrawable(backIcon); 620 621 updateRecentsIcon(); 622 623 // Update IME button visibility, a11y and rotate button always overrides the appearance 624 boolean disableImeSwitcher = 625 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0 626 || isImeRenderingNavButtons(); 627 mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher); 628 629 mBarTransitions.reapplyDarkIntensity(); 630 631 boolean disableHome = isGesturalMode(mNavBarMode) 632 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 633 634 // Always disable recents when alternate car mode UI is active and for secondary displays. 635 boolean disableRecent = isRecentsButtonDisabled(); 636 637 // Disable the home handle if both hone and recents are disabled 638 boolean disableHomeHandle = disableRecent 639 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 640 641 boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures() 642 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)) 643 || isImeRenderingNavButtons(); 644 645 // When screen pinning, don't hide back and home when connected service or back and 646 // recents buttons when disconnected from launcher service in screen pinning mode, 647 // as they are used for exiting. 648 if (mOverviewProxyEnabled) { 649 // Force disable recents when not in legacy mode 650 disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode); 651 if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) { 652 disableBack = disableHome = false; 653 } 654 } else if (mScreenPinningActive) { 655 disableBack = disableRecent = false; 656 } 657 658 ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons); 659 if (navButtons != null) { 660 LayoutTransition lt = navButtons.getLayoutTransition(); 661 if (lt != null) { 662 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 663 lt.addTransitionListener(mTransitionListener); 664 } 665 } 666 } 667 668 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 669 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 670 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 671 getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE); 672 notifyActiveTouchRegions(); 673 } 674 675 /** 676 * Returns whether the IME is currently visible and drawing the nav buttons. 677 */ isImeRenderingNavButtons()678 boolean isImeRenderingNavButtons() { 679 return mImeDrawsImeNavBar 680 && mImeCanRenderGesturalNavButtons 681 && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0; 682 } 683 684 @VisibleForTesting isRecentsButtonDisabled()685 boolean isRecentsButtonDisabled() { 686 return mUseCarModeUi || !isOverviewEnabled() 687 || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId(); 688 } 689 getContextDisplay()690 private Display getContextDisplay() { 691 return getContext().getDisplay(); 692 } 693 setLayoutTransitionsEnabled(boolean enabled)694 public void setLayoutTransitionsEnabled(boolean enabled) { 695 mLayoutTransitionsEnabled = enabled; 696 updateLayoutTransitionsEnabled(); 697 } 698 setWakeAndUnlocking(boolean wakeAndUnlocking)699 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 700 setUseFadingAnimations(wakeAndUnlocking); 701 mWakeAndUnlocking = wakeAndUnlocking; 702 updateLayoutTransitionsEnabled(); 703 } 704 updateLayoutTransitionsEnabled()705 private void updateLayoutTransitionsEnabled() { 706 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 707 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 708 LayoutTransition lt = navButtons.getLayoutTransition(); 709 if (lt != null) { 710 if (enabled) { 711 lt.enableTransitionType(LayoutTransition.APPEARING); 712 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 713 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 714 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 715 } else { 716 lt.disableTransitionType(LayoutTransition.APPEARING); 717 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 718 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 719 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 720 } 721 } 722 } 723 setUseFadingAnimations(boolean useFadingAnimations)724 private void setUseFadingAnimations(boolean useFadingAnimations) { 725 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent()) 726 .getLayoutParams(); 727 if (lp != null) { 728 boolean old = lp.windowAnimations != 0; 729 if (!old && useFadingAnimations) { 730 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 731 } else if (old && !useFadingAnimations) { 732 lp.windowAnimations = 0; 733 } else { 734 return; 735 } 736 WindowManager wm = getContext().getSystemService(WindowManager.class); 737 wm.updateViewLayout((View) getParent(), lp); 738 } 739 } 740 onStatusBarPanelStateChanged()741 public void onStatusBarPanelStateChanged() { 742 updateSlippery(); 743 } 744 745 /** */ updateDisabledSystemUiStateFlags(SysUiState sysUiState)746 public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) { 747 int displayId = mContext.getDisplayId(); 748 749 sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED, 750 (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0) 751 .setFlag(SYSUI_STATE_HOME_DISABLED, 752 (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0) 753 .setFlag(SYSUI_STATE_SEARCH_DISABLED, 754 (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0) 755 .commitUpdate(displayId); 756 } 757 setInScreenPinning(boolean active)758 public void setInScreenPinning(boolean active) { 759 mScreenPinningActive = active; 760 } 761 updatePanelSystemUiStateFlags()762 private void updatePanelSystemUiStateFlags() { 763 if (SysUiState.DEBUG) { 764 Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView); 765 } 766 if (mPanelView != null) { 767 mPanelView.updateSystemUiStateFlags(); 768 } 769 } 770 onOverviewProxyConnectionChange(boolean enabled)771 void onOverviewProxyConnectionChange(boolean enabled) { 772 mOverviewProxyEnabled = enabled; 773 } 774 setShouldShowSwipeUpUi(boolean showSwipeUpUi)775 void setShouldShowSwipeUpUi(boolean showSwipeUpUi) { 776 mShowSwipeUpUi = showSwipeUpUi; 777 updateStates(); 778 } 779 780 /** */ updateStates()781 public void updateStates() { 782 if (mNavigationInflaterView != null) { 783 // Reinflate the navbar if needed, no-op unless the swipe up state changes 784 mNavigationInflaterView.onLikelyDefaultLayoutChange(); 785 } 786 787 updateSlippery(); 788 reloadNavIcons(); 789 updateNavButtonIcons(); 790 mBgExecutor.execute(() -> setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi)); 791 getHomeButton().setAccessibilityDelegate( 792 mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null); 793 } 794 795 /** 796 * Enable or disable haptic feedback on the navigation bar buttons. 797 */ setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled)798 private void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { 799 try { 800 WindowManagerGlobal.getWindowManagerService() 801 .setNavBarVirtualKeyHapticFeedbackEnabled(enabled); 802 } catch (RemoteException e) { 803 Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e); 804 } 805 } 806 807 /** 808 * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up 809 * is enabled, or the notifications is fully opened without being in an animated state. If 810 * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen 811 * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar. 812 */ updateSlippery()813 void updateSlippery() { 814 setSlippery(!isQuickStepSwipeUpEnabled() || 815 (mPanelView != null && mPanelView.isFullyExpanded() && !mPanelView.isCollapsing())); 816 } 817 setSlippery(boolean slippery)818 void setSlippery(boolean slippery) { 819 setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery); 820 } 821 setWindowFlag(int flags, boolean enable)822 private void setWindowFlag(int flags, boolean enable) { 823 final ViewGroup navbarView = ((ViewGroup) getParent()); 824 if (navbarView == null) { 825 return; 826 } 827 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); 828 if (lp == null || enable == ((lp.flags & flags) != 0)) { 829 return; 830 } 831 if (enable) { 832 lp.flags |= flags; 833 } else { 834 lp.flags &= ~flags; 835 } 836 WindowManager wm = getContext().getSystemService(WindowManager.class); 837 wm.updateViewLayout(navbarView, lp); 838 } 839 setNavBarMode(int mode, boolean imeDrawsImeNavBar)840 void setNavBarMode(int mode, boolean imeDrawsImeNavBar) { 841 mNavBarMode = mode; 842 mImeDrawsImeNavBar = imeDrawsImeNavBar; 843 mBarTransitions.onNavigationModeChanged(mNavBarMode); 844 mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode); 845 mRotationButtonController.onNavigationModeChanged(mNavBarMode); 846 updateRotationButton(); 847 } 848 setAccessibilityButtonState(final boolean visible, final boolean longClickable)849 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { 850 mLongClickableAccessibilityButton = longClickable; 851 getAccessibilityButton().setLongClickable(longClickable); 852 mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible); 853 } 854 855 @Override onFinishInflate()856 public void onFinishInflate() { 857 super.onFinishInflate(); 858 mNavigationInflaterView = findViewById(R.id.navigation_inflater); 859 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 860 861 updateOrientationViews(); 862 reloadNavIcons(); 863 } 864 865 @Override onDraw(Canvas canvas)866 protected void onDraw(Canvas canvas) { 867 mDeadZone.onDraw(canvas); 868 super.onDraw(canvas); 869 } 870 871 @Override onLayout(boolean changed, int left, int top, int right, int bottom)872 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 873 super.onLayout(changed, left, top, right, bottom); 874 875 notifyActiveTouchRegions(); 876 } 877 878 /** 879 * Notifies the overview service of the active touch regions. 880 */ notifyActiveTouchRegions()881 public void notifyActiveTouchRegions() { 882 if (mUpdateActiveTouchRegionsCallback != null) { 883 mUpdateActiveTouchRegionsCallback.update(); 884 } 885 } 886 setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback)887 void setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback) { 888 mUpdateActiveTouchRegionsCallback = callback; 889 notifyActiveTouchRegions(); 890 } 891 getButtonTouchRegionCache()892 Map<View, Rect> getButtonTouchRegionCache() { 893 FrameLayout navBarLayout = mIsVertical 894 ? mNavigationInflaterView.mVertical 895 : mNavigationInflaterView.mHorizontal; 896 return ((NearestTouchFrame) navBarLayout 897 .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions(); 898 } 899 updateOrientationViews()900 private void updateOrientationViews() { 901 mHorizontal = findViewById(R.id.horizontal); 902 mVertical = findViewById(R.id.vertical); 903 904 updateCurrentView(); 905 } 906 needsReorient(int rotation)907 boolean needsReorient(int rotation) { 908 return mCurrentRotation != rotation; 909 } 910 updateCurrentRotation()911 private void updateCurrentRotation() { 912 final int rotation = mConfiguration.windowConfiguration.getDisplayRotation(); 913 if (mCurrentRotation == rotation) { 914 return; 915 } 916 mCurrentRotation = rotation; 917 mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90); 918 mDeadZone.onConfigurationChanged(mCurrentRotation); 919 if (DEBUG) { 920 Log.d(TAG, "updateCurrentRotation(): rot=" + mCurrentRotation); 921 } 922 } 923 updateCurrentView()924 private void updateCurrentView() { 925 resetViews(); 926 mCurrentView = mIsVertical ? mVertical : mHorizontal; 927 mCurrentView.setVisibility(View.VISIBLE); 928 mNavigationInflaterView.setVertical(mIsVertical); 929 mNavigationInflaterView.updateButtonDispatchersCurrentView(); 930 updateLayoutTransitionsEnabled(); 931 updateCurrentRotation(); 932 } 933 resetViews()934 private void resetViews() { 935 mHorizontal.setVisibility(View.GONE); 936 mVertical.setVisibility(View.GONE); 937 } 938 updateRecentsIcon()939 private void updateRecentsIcon() { 940 mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0); 941 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 942 mBarTransitions.reapplyDarkIntensity(); 943 } 944 showPinningEnterExitToast(boolean entering)945 public void showPinningEnterExitToast(boolean entering) { 946 if (entering) { 947 mScreenPinningNotify.showPinningStartToast(); 948 } else { 949 mScreenPinningNotify.showPinningExitToast(); 950 } 951 } 952 showPinningEscapeToast()953 public void showPinningEscapeToast() { 954 mScreenPinningNotify.showEscapeToast( 955 mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible()); 956 } 957 isVertical()958 public boolean isVertical() { 959 return mIsVertical; 960 } 961 reorient()962 public void reorient() { 963 updateCurrentView(); 964 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); 965 966 // force the low profile & disabled states into compliance 967 mBarTransitions.init(); 968 969 // Resolve layout direction if not resolved since components changing layout direction such 970 // as changing languages will recreate this view and the direction will be resolved later 971 if (!isLayoutDirectionResolved()) { 972 resolveLayoutDirection(); 973 } 974 updateNavButtonIcons(); 975 976 getHomeButton().setVertical(mIsVertical); 977 } 978 979 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)980 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 981 int w = MeasureSpec.getSize(widthMeasureSpec); 982 int h = MeasureSpec.getSize(heightMeasureSpec); 983 if (DEBUG) Log.d(TAG, String.format( 984 "onMeasure: (%dx%d) old: (%dx%d)", w, h, getMeasuredWidth(), getMeasuredHeight())); 985 986 final boolean newVertical = w > 0 && h > w 987 && !isGesturalMode(mNavBarMode); 988 if (newVertical != mIsVertical) { 989 mIsVertical = newVertical; 990 if (DEBUG) { 991 Log.d(TAG, String.format("onMeasure: h=%d, w=%d, vert=%s", h, w, 992 mIsVertical ? "y" : "n")); 993 } 994 reorient(); 995 notifyVerticalChangedListener(newVertical); 996 } 997 998 if (isGesturalMode(mNavBarMode)) { 999 // Update the nav bar background to match the height of the visible nav bar 1000 int height = mIsVertical 1001 ? getResources().getDimensionPixelSize( 1002 com.android.internal.R.dimen.navigation_bar_height_landscape) 1003 : getResources().getDimensionPixelSize( 1004 com.android.internal.R.dimen.navigation_bar_height); 1005 int frameHeight = getResources().getDimensionPixelSize( 1006 com.android.internal.R.dimen.navigation_bar_frame_height); 1007 mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h)); 1008 } else { 1009 mBarTransitions.setBackgroundFrame(null); 1010 } 1011 1012 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1013 } 1014 getNavBarHeight()1015 int getNavBarHeight() { 1016 return mIsVertical 1017 ? getResources().getDimensionPixelSize( 1018 com.android.internal.R.dimen.navigation_bar_height_landscape) 1019 : getResources().getDimensionPixelSize( 1020 com.android.internal.R.dimen.navigation_bar_height); 1021 } 1022 notifyVerticalChangedListener(boolean newVertical)1023 private void notifyVerticalChangedListener(boolean newVertical) { 1024 if (mOnVerticalChangedListener != null) { 1025 mOnVerticalChangedListener.onVerticalChanged(newVertical); 1026 } 1027 } 1028 1029 @Override onConfigurationChanged(Configuration newConfig)1030 protected void onConfigurationChanged(Configuration newConfig) { 1031 super.onConfigurationChanged(newConfig); 1032 mTmpLastConfiguration.updateFrom(mConfiguration); 1033 final int changes = mConfiguration.updateFrom(newConfig); 1034 mFloatingRotationButton.onConfigurationChanged(changes); 1035 1036 boolean uiCarModeChanged = updateCarMode(); 1037 updateIcons(mTmpLastConfiguration); 1038 updateRecentsIcon(); 1039 updateCurrentRotation(); 1040 mEdgeBackGestureHandler.onConfigurationChanged(mConfiguration); 1041 if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi 1042 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) { 1043 // If car mode or density changes, we need to reset the icons. 1044 updateNavButtonIcons(); 1045 } 1046 } 1047 1048 /** 1049 * If the configuration changed, update the carmode and return that it was updated. 1050 */ updateCarMode()1051 private boolean updateCarMode() { 1052 boolean uiCarModeChanged = false; 1053 if (mConfiguration != null) { 1054 int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK; 1055 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 1056 1057 if (isCarMode != mInCarMode) { 1058 mInCarMode = isCarMode; 1059 if (ALTERNATE_CAR_MODE_UI) { 1060 mUseCarModeUi = isCarMode; 1061 uiCarModeChanged = true; 1062 } else { 1063 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 1064 mUseCarModeUi = false; 1065 } 1066 } 1067 } 1068 return uiCarModeChanged; 1069 } 1070 getResourceName(int resId)1071 private String getResourceName(int resId) { 1072 if (resId != 0) { 1073 final android.content.res.Resources res = getContext().getResources(); 1074 try { 1075 return res.getResourceName(resId); 1076 } catch (android.content.res.Resources.NotFoundException ex) { 1077 return "(unknown)"; 1078 } 1079 } else { 1080 return "(null)"; 1081 } 1082 } 1083 visibilityToString(int vis)1084 private static String visibilityToString(int vis) { 1085 switch (vis) { 1086 case View.INVISIBLE: 1087 return "INVISIBLE"; 1088 case View.GONE: 1089 return "GONE"; 1090 default: 1091 return "VISIBLE"; 1092 } 1093 } 1094 1095 @Override onAttachedToWindow()1096 protected void onAttachedToWindow() { 1097 super.onAttachedToWindow(); 1098 requestApplyInsets(); 1099 reorient(); 1100 if (mRotationButtonController != null) { 1101 mRotationButtonController.registerListeners(false /* registerRotationWatcher */); 1102 } 1103 1104 updateNavButtonIcons(); 1105 } 1106 1107 @Override onDetachedFromWindow()1108 protected void onDetachedFromWindow() { 1109 super.onDetachedFromWindow(); 1110 for (int i = 0; i < mButtonDispatchers.size(); ++i) { 1111 mButtonDispatchers.valueAt(i).onDestroy(); 1112 } 1113 if (mRotationButtonController != null) { 1114 mFloatingRotationButton.hide(); 1115 mRotationButtonController.unregisterListeners(); 1116 } 1117 } 1118 dump(PrintWriter pw)1119 void dump(PrintWriter pw) { 1120 final Rect r = new Rect(); 1121 final Point size = new Point(); 1122 getContextDisplay().getRealSize(size); 1123 1124 pw.println("NavigationBarView:"); 1125 pw.println(String.format(" this: " + CentralSurfaces.viewInfo(this) 1126 + " " + visibilityToString(getVisibility()))); 1127 1128 getWindowVisibleDisplayFrame(r); 1129 final boolean offscreen = r.right > size.x || r.bottom > size.y; 1130 pw.println(" window: " 1131 + r.toShortString() 1132 + " " + visibilityToString(getWindowVisibility()) 1133 + (offscreen ? " OFFSCREEN!" : "")); 1134 1135 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s %f", 1136 getResourceName(getCurrentView().getId()), 1137 getCurrentView().getWidth(), getCurrentView().getHeight(), 1138 visibilityToString(getCurrentView().getVisibility()), 1139 getCurrentView().getAlpha())); 1140 1141 pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f", 1142 mDisabledFlags, 1143 mIsVertical ? "true" : "false", 1144 getLightTransitionsController().getCurrentDarkIntensity())); 1145 1146 pw.println(" mScreenOn: " + mScreenOn); 1147 1148 1149 dumpButton(pw, "back", getBackButton()); 1150 dumpButton(pw, "home", getHomeButton()); 1151 dumpButton(pw, "handle", getHomeHandle()); 1152 dumpButton(pw, "rcnt", getRecentsButton()); 1153 dumpButton(pw, "rota", getRotateSuggestionButton()); 1154 dumpButton(pw, "a11y", getAccessibilityButton()); 1155 dumpButton(pw, "ime", getImeSwitchButton()); 1156 1157 if (mNavigationInflaterView != null) { 1158 mNavigationInflaterView.dump(pw); 1159 } 1160 mBarTransitions.dump(pw); 1161 mContextualButtonGroup.dump(pw); 1162 mEdgeBackGestureHandler.dump(pw); 1163 } 1164 1165 @Override onApplyWindowInsets(WindowInsets insets)1166 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1167 int leftInset = insets.getSystemWindowInsetLeft(); 1168 int rightInset = insets.getSystemWindowInsetRight(); 1169 setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset, 1170 insets.getSystemWindowInsetBottom()); 1171 // we're passing the insets onto the gesture handler since the back arrow is only 1172 // conditionally added and doesn't always get all the insets. 1173 mEdgeBackGestureHandler.setInsets(leftInset, rightInset); 1174 1175 // this allows assist handle to be drawn outside its bound so that it can align screen 1176 // bottom by translating its y position. 1177 final boolean shouldClip = 1178 !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0; 1179 setClipChildren(shouldClip); 1180 setClipToPadding(shouldClip); 1181 1182 return super.onApplyWindowInsets(insets); 1183 } 1184 addPipExclusionBoundsChangeListener(Pip pip)1185 void addPipExclusionBoundsChangeListener(Pip pip) { 1186 pip.addPipExclusionBoundsChangeListener(mPipListener); 1187 } 1188 removePipExclusionBoundsChangeListener(Pip pip)1189 void removePipExclusionBoundsChangeListener(Pip pip) { 1190 pip.removePipExclusionBoundsChangeListener(mPipListener); 1191 } 1192 registerBackAnimation(BackAnimation backAnimation)1193 void registerBackAnimation(BackAnimation backAnimation) { 1194 mEdgeBackGestureHandler.setBackAnimation(backAnimation); 1195 } 1196 dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1197 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 1198 pw.print(" " + caption + ": "); 1199 if (button == null) { 1200 pw.print("null"); 1201 } else { 1202 pw.print(visibilityToString(button.getVisibility()) 1203 + " alpha=" + button.getAlpha() 1204 ); 1205 } 1206 pw.println(); 1207 } 1208 1209 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)1210 void onVerticalChanged(boolean isVertical); 1211 } 1212 1213 private final Consumer<Boolean> mDockedListener = exists -> post(() -> { 1214 mDockedStackExists = exists; 1215 updateRecentsIcon(); 1216 }); 1217 1218 private final Consumer<Rect> mPipListener = bounds -> post(() -> { 1219 mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds); 1220 }); 1221 1222 1223 interface UpdateActiveTouchRegionsCallback { update()1224 void update(); 1225 } 1226 } 1227