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