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