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