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.MotionEvent.ACTION_DOWN; 20 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK; 21 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; 22 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; 23 24 import android.animation.LayoutTransition; 25 import android.animation.LayoutTransition.TransitionListener; 26 import android.animation.ObjectAnimator; 27 import android.animation.TimeInterpolator; 28 import android.animation.ValueAnimator; 29 import android.annotation.DrawableRes; 30 import android.annotation.StyleRes; 31 import android.app.StatusBarManager; 32 import android.content.Context; 33 import android.content.res.Configuration; 34 import android.graphics.Canvas; 35 import android.graphics.Point; 36 import android.graphics.Rect; 37 import android.graphics.drawable.AnimatedVectorDrawable; 38 import android.graphics.drawable.Drawable; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.Message; 42 import android.os.SystemProperties; 43 import android.support.annotation.ColorInt; 44 import android.util.AttributeSet; 45 import android.util.Log; 46 import android.util.SparseArray; 47 import android.view.ContextThemeWrapper; 48 import android.view.Display; 49 import android.view.MotionEvent; 50 import android.view.Surface; 51 import android.view.View; 52 import android.view.ViewGroup; 53 import android.view.WindowInsets; 54 import android.view.WindowManager; 55 import android.view.accessibility.AccessibilityNodeInfo; 56 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 57 import android.view.inputmethod.InputMethodManager; 58 import android.widget.FrameLayout; 59 60 import com.android.settingslib.Utils; 61 import com.android.systemui.Dependency; 62 import com.android.systemui.DockedStackExistsListener; 63 import com.android.systemui.OverviewProxyService; 64 import com.android.systemui.R; 65 import com.android.systemui.RecentsComponent; 66 import com.android.systemui.SysUiServiceProvider; 67 import com.android.systemui.plugins.PluginListener; 68 import com.android.systemui.plugins.PluginManager; 69 import com.android.systemui.plugins.statusbar.phone.NavGesture; 70 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; 71 import com.android.systemui.recents.Recents; 72 import com.android.systemui.recents.RecentsOnboarding; 73 import com.android.systemui.shared.recents.IOverviewProxy; 74 import com.android.systemui.shared.system.ActivityManagerWrapper; 75 import com.android.systemui.shared.system.NavigationBarCompat; 76 import com.android.systemui.shared.system.WindowManagerWrapper; 77 import com.android.systemui.stackdivider.Divider; 78 import com.android.systemui.statusbar.policy.DeadZone; 79 import com.android.systemui.statusbar.policy.KeyButtonDrawable; 80 import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable; 81 82 import java.io.FileDescriptor; 83 import java.io.PrintWriter; 84 import java.util.function.Consumer; 85 86 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; 87 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; 88 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; 89 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION; 90 91 public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> { 92 final static boolean DEBUG = false; 93 final static String TAG = "StatusBar/NavBarView"; 94 95 // slippery nav bar when everything is disabled, e.g. during setup 96 final static boolean SLIPPERY_WHEN_DISABLED = true; 97 98 final static boolean ALTERNATE_CAR_MODE_UI = false; 99 100 final Display mDisplay; 101 View mCurrentView = null; 102 View[] mRotatedViews = new View[4]; 103 104 boolean mVertical; 105 private int mCurrentRotation = -1; 106 107 boolean mShowMenu; 108 boolean mShowAccessibilityButton; 109 boolean mLongClickableAccessibilityButton; 110 boolean mShowRotateButton; 111 int mDisabledFlags = 0; 112 int mNavigationIconHints = 0; 113 114 private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE; 115 private Rect mHomeButtonBounds = new Rect(); 116 private Rect mBackButtonBounds = new Rect(); 117 private Rect mRecentsButtonBounds = new Rect(); 118 private Rect mRotationButtonBounds = new Rect(); 119 private int[] mTmpPosition = new int[2]; 120 private Rect mTmpRect = new Rect(); 121 122 private KeyButtonDrawable mBackIcon; 123 private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon; 124 private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon; 125 private KeyButtonDrawable mHomeDefaultIcon, mHomeCarModeIcon; 126 private KeyButtonDrawable mRecentIcon; 127 private KeyButtonDrawable mDockedIcon; 128 private KeyButtonDrawable mImeIcon; 129 private KeyButtonDrawable mMenuIcon; 130 private KeyButtonDrawable mAccessibilityIcon; 131 private TintedKeyButtonDrawable mRotateSuggestionIcon; 132 133 private GestureHelper mGestureHelper; 134 private final DeadZone mDeadZone; 135 private boolean mDeadZoneConsuming = false; 136 private final NavigationBarTransitions mBarTransitions; 137 private final OverviewProxyService mOverviewProxyService; 138 139 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288) 140 final static boolean WORKAROUND_INVALID_LAYOUT = true; 141 final static int MSG_CHECK_INVALID_LAYOUT = 8686; 142 143 // performs manual animation in sync with layout transitions 144 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 145 146 private OnVerticalChangedListener mOnVerticalChangedListener; 147 private boolean mLayoutTransitionsEnabled = true; 148 private boolean mWakeAndUnlocking; 149 private boolean mUseCarModeUi = false; 150 private boolean mInCarMode = false; 151 private boolean mDockedStackExists; 152 153 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>(); 154 private Configuration mConfiguration; 155 156 private NavigationBarInflaterView mNavigationInflaterView; 157 private RecentsComponent mRecentsComponent; 158 private Divider mDivider; 159 private RecentsOnboarding mRecentsOnboarding; 160 private NotificationPanelView mPanelView; 161 162 private int mRotateBtnStyle = R.style.RotateButtonCCWStart90; 163 164 private class NavTransitionListener implements TransitionListener { 165 private boolean mBackTransitioning; 166 private boolean mHomeAppearing; 167 private long mStartDelay; 168 private long mDuration; 169 private TimeInterpolator mInterpolator; 170 171 @Override startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)172 public void startTransition(LayoutTransition transition, ViewGroup container, 173 View view, int transitionType) { 174 if (view.getId() == R.id.back) { 175 mBackTransitioning = true; 176 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 177 mHomeAppearing = true; 178 mStartDelay = transition.getStartDelay(transitionType); 179 mDuration = transition.getDuration(transitionType); 180 mInterpolator = transition.getInterpolator(transitionType); 181 } 182 } 183 184 @Override endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)185 public void endTransition(LayoutTransition transition, ViewGroup container, 186 View view, int transitionType) { 187 if (view.getId() == R.id.back) { 188 mBackTransitioning = false; 189 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 190 mHomeAppearing = false; 191 } 192 } 193 onBackAltCleared()194 public void onBackAltCleared() { 195 ButtonDispatcher backButton = getBackButton(); 196 197 // When dismissing ime during unlock, force the back button to run the same appearance 198 // animation as home (if we catch this condition early enough). 199 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE 200 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 201 getBackButton().setAlpha(0); 202 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1); 203 a.setStartDelay(mStartDelay); 204 a.setDuration(mDuration); 205 a.setInterpolator(mInterpolator); 206 a.start(); 207 } 208 } 209 } 210 211 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() { 212 @Override 213 public void onClick(View view) { 214 mContext.getSystemService(InputMethodManager.class) 215 .showInputMethodPicker(true /* showAuxiliarySubtypes */); 216 } 217 }; 218 219 private class H extends Handler { handleMessage(Message m)220 public void handleMessage(Message m) { 221 switch (m.what) { 222 case MSG_CHECK_INVALID_LAYOUT: 223 final String how = "" + m.obj; 224 final int w = getWidth(); 225 final int h = getHeight(); 226 final int vw = getCurrentView().getWidth(); 227 final int vh = getCurrentView().getHeight(); 228 229 if (h != vh || w != vw) { 230 Log.w(TAG, String.format( 231 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)", 232 how, w, h, vw, vh)); 233 if (WORKAROUND_INVALID_LAYOUT) { 234 requestLayout(); 235 } 236 } 237 break; 238 } 239 } 240 } 241 242 private final AccessibilityDelegate mQuickStepAccessibilityDelegate 243 = new AccessibilityDelegate() { 244 private AccessibilityAction mToggleOverviewAction; 245 246 @Override 247 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 248 super.onInitializeAccessibilityNodeInfo(host, info); 249 if (mToggleOverviewAction == null) { 250 mToggleOverviewAction = new AccessibilityAction(R.id.action_toggle_overview, 251 getContext().getString(R.string.quick_step_accessibility_toggle_overview)); 252 } 253 info.addAction(mToggleOverviewAction); 254 } 255 256 @Override 257 public boolean performAccessibilityAction(View host, int action, Bundle args) { 258 switch (action) { 259 case R.id.action_toggle_overview: 260 SysUiServiceProvider.getComponent(getContext(), Recents.class) 261 .toggleRecentApps(); 262 break; 263 default: 264 return super.performAccessibilityAction(host, action, args); 265 } 266 return true; 267 } 268 }; 269 NavigationBarView(Context context, AttributeSet attrs)270 public NavigationBarView(Context context, AttributeSet attrs) { 271 super(context, attrs); 272 273 mDisplay = ((WindowManager) context.getSystemService( 274 Context.WINDOW_SERVICE)).getDefaultDisplay(); 275 276 mVertical = false; 277 mShowMenu = false; 278 279 mShowAccessibilityButton = false; 280 mLongClickableAccessibilityButton = false; 281 282 mOverviewProxyService = Dependency.get(OverviewProxyService.class); 283 mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService); 284 285 mConfiguration = new Configuration(); 286 mConfiguration.updateFrom(context.getResources().getConfiguration()); 287 reloadNavIcons(); 288 289 mBarTransitions = new NavigationBarTransitions(this); 290 291 mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); 292 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); 293 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); 294 mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu)); 295 mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher)); 296 mButtonDispatchers.put(R.id.accessibility_button, 297 new ButtonDispatcher(R.id.accessibility_button)); 298 mButtonDispatchers.put(R.id.rotate_suggestion, 299 new ButtonDispatcher(R.id.rotate_suggestion)); 300 mButtonDispatchers.put(R.id.menu_container, 301 new ButtonDispatcher(R.id.menu_container)); 302 mDeadZone = new DeadZone(this); 303 } 304 getBarTransitions()305 public BarTransitions getBarTransitions() { 306 return mBarTransitions; 307 } 308 getLightTransitionsController()309 public LightBarTransitionsController getLightTransitionsController() { 310 return mBarTransitions.getLightTransitionsController(); 311 } 312 setComponents(RecentsComponent recentsComponent, Divider divider, NotificationPanelView panel)313 public void setComponents(RecentsComponent recentsComponent, Divider divider, 314 NotificationPanelView panel) { 315 mRecentsComponent = recentsComponent; 316 mDivider = divider; 317 mPanelView = panel; 318 if (mGestureHelper instanceof NavigationBarGestureHelper) { 319 ((NavigationBarGestureHelper) mGestureHelper).setComponents( 320 recentsComponent, divider, this); 321 } 322 } 323 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)324 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 325 mOnVerticalChangedListener = onVerticalChangedListener; 326 notifyVerticalChangedListener(mVertical); 327 } 328 329 @Override onInterceptTouchEvent(MotionEvent event)330 public boolean onInterceptTouchEvent(MotionEvent event) { 331 if (shouldDeadZoneConsumeTouchEvents(event)) { 332 return true; 333 } 334 switch (event.getActionMasked()) { 335 case ACTION_DOWN: 336 int x = (int) event.getX(); 337 int y = (int) event.getY(); 338 mDownHitTarget = HIT_TARGET_NONE; 339 if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) { 340 mDownHitTarget = HIT_TARGET_BACK; 341 } else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) { 342 mDownHitTarget = HIT_TARGET_HOME; 343 } else if (getRecentsButton().isVisible() && mRecentsButtonBounds.contains(x, y)) { 344 mDownHitTarget = HIT_TARGET_OVERVIEW; 345 } else if (getRotateSuggestionButton().isVisible() 346 && mRotationButtonBounds.contains(x, y)) { 347 mDownHitTarget = HIT_TARGET_ROTATION; 348 } 349 break; 350 } 351 return mGestureHelper.onInterceptTouchEvent(event); 352 } 353 354 @Override onTouchEvent(MotionEvent event)355 public boolean onTouchEvent(MotionEvent event) { 356 if (shouldDeadZoneConsumeTouchEvents(event)) { 357 return true; 358 } 359 if (mGestureHelper.onTouchEvent(event)) { 360 return true; 361 } 362 return super.onTouchEvent(event); 363 } 364 shouldDeadZoneConsumeTouchEvents(MotionEvent event)365 private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) { 366 if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) { 367 switch (event.getActionMasked()) { 368 case MotionEvent.ACTION_DOWN: 369 // Allow gestures starting in the deadzone to be slippery 370 setSlippery(true); 371 mDeadZoneConsuming = true; 372 break; 373 case MotionEvent.ACTION_CANCEL: 374 case MotionEvent.ACTION_UP: 375 // When a gesture started in the deadzone is finished, restore slippery state 376 updateSlippery(); 377 mDeadZoneConsuming = false; 378 break; 379 } 380 return true; 381 } 382 return false; 383 } 384 getDownHitTarget()385 public @NavigationBarCompat.HitTarget int getDownHitTarget() { 386 return mDownHitTarget; 387 } 388 abortCurrentGesture()389 public void abortCurrentGesture() { 390 getHomeButton().abortCurrentGesture(); 391 } 392 393 private H mHandler = new H(); 394 getCurrentView()395 public View getCurrentView() { 396 return mCurrentView; 397 } 398 getAllViews()399 public View[] getAllViews() { 400 return mRotatedViews; 401 } 402 getRecentsButton()403 public ButtonDispatcher getRecentsButton() { 404 return mButtonDispatchers.get(R.id.recent_apps); 405 } 406 getMenuButton()407 public ButtonDispatcher getMenuButton() { 408 return mButtonDispatchers.get(R.id.menu); 409 } 410 getBackButton()411 public ButtonDispatcher getBackButton() { 412 return mButtonDispatchers.get(R.id.back); 413 } 414 getHomeButton()415 public ButtonDispatcher getHomeButton() { 416 return mButtonDispatchers.get(R.id.home); 417 } 418 getImeSwitchButton()419 public ButtonDispatcher getImeSwitchButton() { 420 return mButtonDispatchers.get(R.id.ime_switcher); 421 } 422 getAccessibilityButton()423 public ButtonDispatcher getAccessibilityButton() { 424 return mButtonDispatchers.get(R.id.accessibility_button); 425 } 426 getRotateSuggestionButton()427 public ButtonDispatcher getRotateSuggestionButton() { 428 return mButtonDispatchers.get(R.id.rotate_suggestion); 429 } 430 getMenuContainer()431 public ButtonDispatcher getMenuContainer() { 432 return mButtonDispatchers.get(R.id.menu_container); 433 } 434 getButtonDispatchers()435 public SparseArray<ButtonDispatcher> getButtonDispatchers() { 436 return mButtonDispatchers; 437 } 438 isRecentsButtonVisible()439 public boolean isRecentsButtonVisible() { 440 return getRecentsButton().getVisibility() == View.VISIBLE; 441 } 442 isOverviewEnabled()443 public boolean isOverviewEnabled() { 444 return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0; 445 } 446 isQuickStepSwipeUpEnabled()447 public boolean isQuickStepSwipeUpEnabled() { 448 return mOverviewProxyService.shouldShowSwipeUpUI() && isOverviewEnabled(); 449 } 450 isQuickScrubEnabled()451 public boolean isQuickScrubEnabled() { 452 return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true) 453 && mOverviewProxyService.isEnabled() && isOverviewEnabled() 454 && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0); 455 } 456 457 // TODO(b/80003212): change car mode icons to vector icons. updateCarModeIcons(Context ctx)458 private void updateCarModeIcons(Context ctx) { 459 mBackCarModeIcon = getDrawable(ctx, 460 R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode); 461 mBackLandCarModeIcon = mBackCarModeIcon; 462 mBackAltCarModeIcon = getDrawable(ctx, 463 R.drawable.ic_sysbar_back_ime_carmode, R.drawable.ic_sysbar_back_ime_carmode); 464 mBackAltLandCarModeIcon = mBackAltCarModeIcon; 465 mHomeCarModeIcon = getDrawable(ctx, 466 R.drawable.ic_sysbar_home_carmode, R.drawable.ic_sysbar_home_carmode); 467 } 468 reloadNavIcons()469 private void reloadNavIcons() { 470 updateIcons(mContext, Configuration.EMPTY, mConfiguration); 471 } 472 updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig)473 private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) { 474 int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme); 475 int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme); 476 Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme); 477 Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme); 478 479 if (oldConfig.orientation != newConfig.orientation 480 || oldConfig.densityDpi != newConfig.densityDpi) { 481 mDockedIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_docked); 482 mHomeDefaultIcon = getHomeDrawable(lightContext, darkContext); 483 } 484 if (oldConfig.densityDpi != newConfig.densityDpi 485 || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) { 486 mBackIcon = getBackDrawable(lightContext, darkContext); 487 mRecentIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_recent); 488 mMenuIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_menu); 489 490 mAccessibilityIcon = getDrawable(lightContext, darkContext, 491 R.drawable.ic_sysbar_accessibility_button, false /* hasShadow */); 492 493 mImeIcon = getDrawable(lightContext, darkContext, R.drawable.ic_ime_switcher_default, 494 false /* hasShadow */); 495 496 updateRotateSuggestionButtonStyle(mRotateBtnStyle, false); 497 498 if (ALTERNATE_CAR_MODE_UI) { 499 updateCarModeIcons(ctx); 500 } 501 } 502 } 503 getBackDrawable(Context lightContext, Context darkContext)504 public KeyButtonDrawable getBackDrawable(Context lightContext, Context darkContext) { 505 KeyButtonDrawable drawable = chooseNavigationIconDrawable(lightContext, darkContext, 506 R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_quick_step); 507 orientBackButton(drawable); 508 return drawable; 509 } 510 getHomeDrawable(Context lightContext, Context darkContext)511 public KeyButtonDrawable getHomeDrawable(Context lightContext, Context darkContext) { 512 final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI(); 513 KeyButtonDrawable drawable = quickStepEnabled 514 ? getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_home_quick_step) 515 : getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_home, 516 false /* hasShadow */); 517 orientHomeButton(drawable); 518 return drawable; 519 } 520 orientBackButton(KeyButtonDrawable drawable)521 private void orientBackButton(KeyButtonDrawable drawable) { 522 final boolean useAltBack = 523 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 524 drawable.setRotation(useAltBack 525 ? -90 : (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) ? 180 : 0); 526 } 527 orientHomeButton(KeyButtonDrawable drawable)528 private void orientHomeButton(KeyButtonDrawable drawable) { 529 drawable.setRotation(mVertical ? 90 : 0); 530 } 531 chooseNavigationIconDrawable(Context lightContext, Context darkContext, @DrawableRes int icon, @DrawableRes int quickStepIcon)532 private KeyButtonDrawable chooseNavigationIconDrawable(Context lightContext, 533 Context darkContext, @DrawableRes int icon, @DrawableRes int quickStepIcon) { 534 final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI(); 535 return quickStepEnabled 536 ? getDrawable(lightContext, darkContext, quickStepIcon) 537 : getDrawable(lightContext, darkContext, icon); 538 } 539 getDrawable(Context lightContext, Context darkContext, @DrawableRes int icon)540 private KeyButtonDrawable getDrawable(Context lightContext, Context darkContext, 541 @DrawableRes int icon) { 542 return getDrawable(lightContext, darkContext, icon, true /* hasShadow */); 543 } 544 getDrawable(Context lightContext, Context darkContext, @DrawableRes int icon, boolean hasShadow)545 private KeyButtonDrawable getDrawable(Context lightContext, Context darkContext, 546 @DrawableRes int icon, boolean hasShadow) { 547 return KeyButtonDrawable.create(lightContext, lightContext.getDrawable(icon), 548 darkContext.getDrawable(icon), hasShadow); 549 } 550 getDrawable(Context ctx, @DrawableRes int lightIcon, @DrawableRes int darkIcon)551 private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int lightIcon, 552 @DrawableRes int darkIcon) { 553 // Legacy image icons using separate light and dark images will not support shadows 554 return KeyButtonDrawable.create(ctx, ctx.getDrawable(lightIcon), 555 ctx.getDrawable(darkIcon), false /* hasShadow */); 556 } 557 getDrawable(Context ctx, @DrawableRes int icon, @ColorInt int lightColor, @ColorInt int darkColor)558 private TintedKeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon, 559 @ColorInt int lightColor, @ColorInt int darkColor) { 560 return TintedKeyButtonDrawable.create(ctx.getDrawable(icon), lightColor, darkColor); 561 } 562 563 @Override setLayoutDirection(int layoutDirection)564 public void setLayoutDirection(int layoutDirection) { 565 reloadNavIcons(); 566 567 super.setLayoutDirection(layoutDirection); 568 } 569 getBackIconWithAlt(boolean carMode, boolean landscape)570 private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) { 571 return landscape 572 ? carMode ? mBackAltLandCarModeIcon : mBackIcon 573 : carMode ? mBackAltCarModeIcon : mBackIcon; 574 } 575 getBackIcon(boolean carMode, boolean landscape)576 private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) { 577 return landscape 578 ? carMode ? mBackLandCarModeIcon : mBackIcon 579 : carMode ? mBackCarModeIcon : mBackIcon; 580 } 581 setNavigationIconHints(int hints)582 public void setNavigationIconHints(int hints) { 583 if (hints == mNavigationIconHints) return; 584 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 585 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) { 586 mTransitionListener.onBackAltCleared(); 587 } 588 if (DEBUG) { 589 android.widget.Toast.makeText(getContext(), 590 "Navigation icon hints = " + hints, 591 500).show(); 592 } 593 mNavigationIconHints = hints; 594 updateNavButtonIcons(); 595 } 596 setDisabledFlags(int disabledFlags)597 public void setDisabledFlags(int disabledFlags) { 598 if (mDisabledFlags == disabledFlags) return; 599 600 final boolean overviewEnabledBefore = isOverviewEnabled(); 601 mDisabledFlags = disabledFlags; 602 603 // Update icons if overview was just enabled to ensure the correct icons are present 604 if (!overviewEnabledBefore && isOverviewEnabled()) { 605 reloadNavIcons(); 606 } 607 608 updateNavButtonIcons(); 609 updateSlippery(); 610 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); 611 } 612 updateNavButtonIcons()613 public void updateNavButtonIcons() { 614 // We have to replace or restore the back and home button icons when exiting or entering 615 // carmode, respectively. Recents are not available in CarMode in nav bar so change 616 // to recent icon is not required. 617 final boolean useAltBack = 618 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 619 KeyButtonDrawable backIcon = useAltBack 620 ? getBackIconWithAlt(mUseCarModeUi, mVertical) 621 : getBackIcon(mUseCarModeUi, mVertical); 622 KeyButtonDrawable homeIcon = mUseCarModeUi ? mHomeCarModeIcon : mHomeDefaultIcon; 623 if (!mUseCarModeUi) { 624 orientBackButton(backIcon); 625 orientHomeButton(homeIcon); 626 } 627 getHomeButton().setImageDrawable(homeIcon); 628 getBackButton().setImageDrawable(backIcon); 629 630 updateRecentsIcon(); 631 632 // Update IME button visibility, a11y and rotate button always overrides the appearance 633 final boolean showImeButton = 634 !mShowAccessibilityButton && 635 !mShowRotateButton && 636 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0); 637 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE); 638 getImeSwitchButton().setImageDrawable(mImeIcon); 639 640 // Update menu button, visibility logic in method 641 setMenuVisibility(mShowMenu, true); 642 getMenuButton().setImageDrawable(mMenuIcon); 643 644 // Update rotate button, visibility altered by a11y button logic 645 getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon); 646 647 // Update a11y button, visibility logic in state method 648 setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton); 649 getAccessibilityButton().setImageDrawable(mAccessibilityIcon); 650 651 mBarTransitions.reapplyDarkIntensity(); 652 653 boolean disableHome = ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 654 655 // Always disable recents when alternate car mode UI is active. 656 boolean disableRecent = mUseCarModeUi || !isOverviewEnabled(); 657 658 boolean disableBack = ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack; 659 660 // When screen pinning, don't hide back and home when connected service or back and 661 // recents buttons when disconnected from launcher service in screen pinning mode, 662 // as they are used for exiting. 663 final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive(); 664 if (mOverviewProxyService.isEnabled()) { 665 // Use interaction flags to show/hide navigation buttons but will be shown if required 666 // to exit screen pinning. 667 final int flags = mOverviewProxyService.getInteractionFlags(); 668 disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0; 669 if (pinningActive) { 670 disableBack = disableHome = false; 671 } 672 } else if (pinningActive) { 673 disableBack = disableRecent = false; 674 } 675 676 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 677 if (navButtons != null) { 678 LayoutTransition lt = navButtons.getLayoutTransition(); 679 if (lt != null) { 680 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 681 lt.addTransitionListener(mTransitionListener); 682 } 683 } 684 } 685 686 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 687 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 688 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 689 } 690 inScreenPinning()691 public boolean inScreenPinning() { 692 return ActivityManagerWrapper.getInstance().isScreenPinningActive(); 693 } 694 setLayoutTransitionsEnabled(boolean enabled)695 public void setLayoutTransitionsEnabled(boolean enabled) { 696 mLayoutTransitionsEnabled = enabled; 697 updateLayoutTransitionsEnabled(); 698 } 699 setWakeAndUnlocking(boolean wakeAndUnlocking)700 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 701 setUseFadingAnimations(wakeAndUnlocking); 702 mWakeAndUnlocking = wakeAndUnlocking; 703 updateLayoutTransitionsEnabled(); 704 } 705 updateLayoutTransitionsEnabled()706 private void updateLayoutTransitionsEnabled() { 707 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 708 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 709 LayoutTransition lt = navButtons.getLayoutTransition(); 710 if (lt != null) { 711 if (enabled) { 712 lt.enableTransitionType(LayoutTransition.APPEARING); 713 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 714 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 715 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 716 } else { 717 lt.disableTransitionType(LayoutTransition.APPEARING); 718 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 719 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 720 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 721 } 722 } 723 } 724 setUseFadingAnimations(boolean useFadingAnimations)725 private void setUseFadingAnimations(boolean useFadingAnimations) { 726 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent()) 727 .getLayoutParams(); 728 if (lp != null) { 729 boolean old = lp.windowAnimations != 0; 730 if (!old && useFadingAnimations) { 731 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 732 } else if (old && !useFadingAnimations) { 733 lp.windowAnimations = 0; 734 } else { 735 return; 736 } 737 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 738 wm.updateViewLayout((View) getParent(), lp); 739 } 740 } 741 onNavigationButtonLongPress(View v)742 public void onNavigationButtonLongPress(View v) { 743 mGestureHelper.onNavigationButtonLongPress(v); 744 } 745 onPanelExpandedChange(boolean expanded)746 public void onPanelExpandedChange(boolean expanded) { 747 updateSlippery(); 748 } 749 updateStates()750 public void updateStates() { 751 final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI(); 752 753 if (mNavigationInflaterView != null) { 754 // Reinflate the navbar if needed, no-op unless the swipe up state changes 755 mNavigationInflaterView.onLikelyDefaultLayoutChange(); 756 } 757 758 updateSlippery(); 759 reloadNavIcons(); 760 updateNavButtonIcons(); 761 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); 762 WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(!showSwipeUpUI); 763 getHomeButton().setAccessibilityDelegate( 764 showSwipeUpUI ? mQuickStepAccessibilityDelegate : null); 765 } 766 updateSlippery()767 private void updateSlippery() { 768 setSlippery(!isQuickStepSwipeUpEnabled() || mPanelView.isFullyExpanded()); 769 } 770 setSlippery(boolean slippery)771 private void setSlippery(boolean slippery) { 772 boolean changed = false; 773 final ViewGroup navbarView = ((ViewGroup) getParent()); 774 final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView 775 .getLayoutParams(); 776 if (lp == null) { 777 return; 778 } 779 if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) { 780 lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; 781 changed = true; 782 } else if (!slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0) { 783 lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; 784 changed = true; 785 } 786 if (changed) { 787 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 788 wm.updateViewLayout(navbarView, lp); 789 } 790 } 791 setMenuVisibility(final boolean show)792 public void setMenuVisibility(final boolean show) { 793 setMenuVisibility(show, false); 794 } 795 setMenuVisibility(final boolean show, final boolean force)796 public void setMenuVisibility(final boolean show, final boolean force) { 797 if (!force && mShowMenu == show) return; 798 799 mShowMenu = show; 800 801 // Only show Menu if IME switcher, rotate and Accessibility buttons are not shown. 802 final boolean shouldShow = mShowMenu && 803 !mShowAccessibilityButton && 804 !mShowRotateButton && 805 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0); 806 807 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE); 808 } 809 setAccessibilityButtonState(final boolean visible, final boolean longClickable)810 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { 811 mShowAccessibilityButton = visible; 812 mLongClickableAccessibilityButton = longClickable; 813 if (visible) { 814 // Accessibility button overrides Menu, IME switcher and rotate buttons. 815 setMenuVisibility(false, true); 816 getImeSwitchButton().setVisibility(View.INVISIBLE); 817 setRotateButtonVisibility(false); 818 } 819 820 getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 821 getAccessibilityButton().setLongClickable(longClickable); 822 } 823 updateRotateSuggestionButtonStyle(@tyleRes int style, boolean setIcon)824 public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) { 825 mRotateBtnStyle = style; 826 final Context ctx = getContext(); 827 828 // Extract the dark and light tints 829 final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme); 830 final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme); 831 Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme); 832 Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme); 833 final int lightColor = Utils.getColorAttr(lightContext, R.attr.singleToneColor); 834 final int darkColor = Utils.getColorAttr(darkContext, R.attr.singleToneColor); 835 836 // Use the supplied style to set the icon's rotation parameters 837 Context rotateContext = new ContextThemeWrapper(ctx, style); 838 839 // Recreate the icon and set it if needed 840 TintedKeyButtonDrawable priorIcon = mRotateSuggestionIcon; 841 mRotateSuggestionIcon = getDrawable(rotateContext, R.drawable.ic_sysbar_rotate_button, 842 lightColor, darkColor); 843 844 // Apply any prior set dark intensity 845 if (priorIcon != null && priorIcon.isDarkIntensitySet()) { 846 mRotateSuggestionIcon.setDarkIntensity(priorIcon.getDarkIntensity()); 847 } 848 849 if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon); 850 } 851 setRotateButtonVisibility(final boolean visible)852 public int setRotateButtonVisibility(final boolean visible) { 853 // Never show if a11y is visible 854 final boolean adjVisible = visible && !mShowAccessibilityButton; 855 final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE; 856 857 // No need to do anything if the request matches the current state 858 if (vis == getRotateSuggestionButton().getVisibility()) return vis; 859 860 getRotateSuggestionButton().setVisibility(vis); 861 mShowRotateButton = visible; 862 863 // Stop any active animations if hidden 864 if (!visible) { 865 Drawable d = mRotateSuggestionIcon.getDrawable(0); 866 if (d instanceof AnimatedVectorDrawable) { 867 AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d; 868 avd.clearAnimationCallbacks(); 869 avd.reset(); 870 } 871 } 872 873 // Hide/restore other button visibility, if necessary 874 updateNavButtonIcons(); 875 876 // Return applied visibility 877 return vis; 878 } 879 isRotateButtonVisible()880 public boolean isRotateButtonVisible() { return mShowRotateButton; } 881 882 /** 883 * @return the button at the given {@param x} and {@param y}. 884 */ getButtonAtPosition(int x, int y)885 ButtonDispatcher getButtonAtPosition(int x, int y) { 886 for (int i = 0; i < mButtonDispatchers.size(); i++) { 887 ButtonDispatcher button = mButtonDispatchers.valueAt(i); 888 View buttonView = button.getCurrentView(); 889 if (buttonView != null) { 890 buttonView.getHitRect(mTmpRect); 891 offsetDescendantRectToMyCoords(buttonView, mTmpRect); 892 if (mTmpRect.contains(x, y)) { 893 return button; 894 } 895 } 896 } 897 return null; 898 } 899 900 @Override onFinishInflate()901 public void onFinishInflate() { 902 mNavigationInflaterView = findViewById(R.id.navigation_inflater); 903 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 904 905 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 906 907 DockedStackExistsListener.register(mDockedListener); 908 updateRotatedViews(); 909 } 910 onDarkIntensityChange(float intensity)911 public void onDarkIntensityChange(float intensity) { 912 if (mGestureHelper != null) { 913 mGestureHelper.onDarkIntensityChange(intensity); 914 } 915 } 916 917 @Override onDraw(Canvas canvas)918 protected void onDraw(Canvas canvas) { 919 mGestureHelper.onDraw(canvas); 920 mDeadZone.onDraw(canvas); 921 super.onDraw(canvas); 922 } 923 924 @Override onLayout(boolean changed, int left, int top, int right, int bottom)925 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 926 super.onLayout(changed, left, top, right, bottom); 927 updateButtonLocationOnScreen(getBackButton(), mBackButtonBounds); 928 updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds); 929 updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds); 930 updateButtonLocationOnScreen(getRotateSuggestionButton(), mRotationButtonBounds); 931 mGestureHelper.onLayout(changed, left, top, right, bottom); 932 mRecentsOnboarding.setNavBarHeight(getMeasuredHeight()); 933 } 934 updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds)935 private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) { 936 View view = button.getCurrentView(); 937 if (view == null) { 938 buttonBounds.setEmpty(); 939 return; 940 } 941 // Temporarily reset the translation back to origin to get the position in window 942 final float posX = view.getTranslationX(); 943 final float posY = view.getTranslationY(); 944 view.setTranslationX(0); 945 view.setTranslationY(0); 946 view.getLocationInWindow(mTmpPosition); 947 buttonBounds.set(mTmpPosition[0], mTmpPosition[1], 948 mTmpPosition[0] + view.getMeasuredWidth(), 949 mTmpPosition[1] + view.getMeasuredHeight()); 950 view.setTranslationX(posX); 951 view.setTranslationY(posY); 952 } 953 updateRotatedViews()954 private void updateRotatedViews() { 955 mRotatedViews[Surface.ROTATION_0] = 956 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); 957 mRotatedViews[Surface.ROTATION_270] = 958 mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90); 959 960 updateCurrentView(); 961 } 962 needsReorient(int rotation)963 public boolean needsReorient(int rotation) { 964 return mCurrentRotation != rotation; 965 } 966 updateCurrentView()967 private void updateCurrentView() { 968 final int rot = mDisplay.getRotation(); 969 for (int i=0; i<4; i++) { 970 mRotatedViews[i].setVisibility(View.GONE); 971 } 972 mCurrentView = mRotatedViews[rot]; 973 mCurrentView.setVisibility(View.VISIBLE); 974 mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90); 975 mNavigationInflaterView.updateButtonDispatchersCurrentView(); 976 updateLayoutTransitionsEnabled(); 977 mCurrentRotation = rot; 978 } 979 updateRecentsIcon()980 private void updateRecentsIcon() { 981 mDockedIcon.setRotation(mDockedStackExists && mVertical ? 90 : 0); 982 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 983 mBarTransitions.reapplyDarkIntensity(); 984 } 985 isVertical()986 public boolean isVertical() { 987 return mVertical; 988 } 989 reorient()990 public void reorient() { 991 updateCurrentView(); 992 993 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); 994 mDeadZone.onConfigurationChanged(mCurrentRotation); 995 996 // force the low profile & disabled states into compliance 997 mBarTransitions.init(); 998 setMenuVisibility(mShowMenu, true /* force */); 999 1000 if (DEBUG) { 1001 Log.d(TAG, "reorient(): rot=" + mCurrentRotation); 1002 } 1003 1004 // Resolve layout direction if not resolved since components changing layout direction such 1005 // as changing languages will recreate this view and the direction will be resolved later 1006 if (!isLayoutDirectionResolved()) { 1007 resolveLayoutDirection(); 1008 } 1009 updateTaskSwitchHelper(); 1010 updateNavButtonIcons(); 1011 1012 getHomeButton().setVertical(mVertical); 1013 } 1014 updateTaskSwitchHelper()1015 private void updateTaskSwitchHelper() { 1016 if (mGestureHelper == null) return; 1017 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 1018 mGestureHelper.setBarState(mVertical, isRtl); 1019 } 1020 1021 @Override onSizeChanged(int w, int h, int oldw, int oldh)1022 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 1023 if (DEBUG) Log.d(TAG, String.format( 1024 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh)); 1025 1026 final boolean newVertical = w > 0 && h > w; 1027 if (newVertical != mVertical) { 1028 mVertical = newVertical; 1029 //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n")); 1030 reorient(); 1031 notifyVerticalChangedListener(newVertical); 1032 } 1033 1034 postCheckForInvalidLayout("sizeChanged"); 1035 super.onSizeChanged(w, h, oldw, oldh); 1036 } 1037 notifyVerticalChangedListener(boolean newVertical)1038 private void notifyVerticalChangedListener(boolean newVertical) { 1039 if (mOnVerticalChangedListener != null) { 1040 mOnVerticalChangedListener.onVerticalChanged(newVertical); 1041 } 1042 } 1043 1044 @Override onConfigurationChanged(Configuration newConfig)1045 protected void onConfigurationChanged(Configuration newConfig) { 1046 super.onConfigurationChanged(newConfig); 1047 boolean uiCarModeChanged = updateCarMode(newConfig); 1048 updateTaskSwitchHelper(); 1049 updateIcons(getContext(), mConfiguration, newConfig); 1050 updateRecentsIcon(); 1051 mRecentsOnboarding.onConfigurationChanged(newConfig); 1052 if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi 1053 || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) { 1054 // If car mode or density changes, we need to reset the icons. 1055 updateNavButtonIcons(); 1056 } 1057 mConfiguration.updateFrom(newConfig); 1058 } 1059 1060 /** 1061 * If the configuration changed, update the carmode and return that it was updated. 1062 */ updateCarMode(Configuration newConfig)1063 private boolean updateCarMode(Configuration newConfig) { 1064 boolean uiCarModeChanged = false; 1065 if (newConfig != null) { 1066 int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK; 1067 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 1068 1069 if (isCarMode != mInCarMode) { 1070 mInCarMode = isCarMode; 1071 if (ALTERNATE_CAR_MODE_UI) { 1072 mUseCarModeUi = isCarMode; 1073 uiCarModeChanged = true; 1074 } else { 1075 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 1076 mUseCarModeUi = false; 1077 } 1078 } 1079 } 1080 return uiCarModeChanged; 1081 } 1082 1083 /* 1084 @Override 1085 protected void onLayout (boolean changed, int left, int top, int right, int bottom) { 1086 if (DEBUG) Log.d(TAG, String.format( 1087 "onLayout: %s (%d,%d,%d,%d)", 1088 changed?"changed":"notchanged", left, top, right, bottom)); 1089 super.onLayout(changed, left, top, right, bottom); 1090 } 1091 1092 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else 1093 // fails, any touch on the display will fix the layout. 1094 @Override 1095 public boolean onInterceptTouchEvent(MotionEvent ev) { 1096 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString()); 1097 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 1098 postCheckForInvalidLayout("touch"); 1099 } 1100 return super.onInterceptTouchEvent(ev); 1101 } 1102 */ 1103 1104 getResourceName(int resId)1105 private String getResourceName(int resId) { 1106 if (resId != 0) { 1107 final android.content.res.Resources res = getContext().getResources(); 1108 try { 1109 return res.getResourceName(resId); 1110 } catch (android.content.res.Resources.NotFoundException ex) { 1111 return "(unknown)"; 1112 } 1113 } else { 1114 return "(null)"; 1115 } 1116 } 1117 postCheckForInvalidLayout(final String how)1118 private void postCheckForInvalidLayout(final String how) { 1119 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget(); 1120 } 1121 visibilityToString(int vis)1122 private static String visibilityToString(int vis) { 1123 switch (vis) { 1124 case View.INVISIBLE: 1125 return "INVISIBLE"; 1126 case View.GONE: 1127 return "GONE"; 1128 default: 1129 return "VISIBLE"; 1130 } 1131 } 1132 1133 @Override onAttachedToWindow()1134 protected void onAttachedToWindow() { 1135 super.onAttachedToWindow(); 1136 requestApplyInsets(); 1137 reorient(); 1138 onPluginDisconnected(null); // Create default gesture helper 1139 Dependency.get(PluginManager.class).addPluginListener(this, 1140 NavGesture.class, false /* Only one */); 1141 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); 1142 } 1143 1144 @Override onDetachedFromWindow()1145 protected void onDetachedFromWindow() { 1146 super.onDetachedFromWindow(); 1147 Dependency.get(PluginManager.class).removePluginListener(this); 1148 if (mGestureHelper != null) { 1149 mGestureHelper.destroy(); 1150 } 1151 setUpSwipeUpOnboarding(false); 1152 } 1153 setUpSwipeUpOnboarding(boolean connectedToOverviewProxy)1154 private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) { 1155 if (connectedToOverviewProxy) { 1156 mRecentsOnboarding.onConnectedToLauncher(); 1157 } else { 1158 mRecentsOnboarding.onDisconnectedFromLauncher(); 1159 } 1160 } 1161 1162 @Override onPluginConnected(NavGesture plugin, Context context)1163 public void onPluginConnected(NavGesture plugin, Context context) { 1164 mGestureHelper = plugin.getGestureHelper(); 1165 updateTaskSwitchHelper(); 1166 } 1167 1168 @Override onPluginDisconnected(NavGesture plugin)1169 public void onPluginDisconnected(NavGesture plugin) { 1170 NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext()); 1171 defaultHelper.setComponents(mRecentsComponent, mDivider, this); 1172 if (mGestureHelper != null) { 1173 mGestureHelper.destroy(); 1174 } 1175 mGestureHelper = defaultHelper; 1176 updateTaskSwitchHelper(); 1177 } 1178 dump(FileDescriptor fd, PrintWriter pw, String[] args)1179 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1180 pw.println("NavigationBarView {"); 1181 final Rect r = new Rect(); 1182 final Point size = new Point(); 1183 mDisplay.getRealSize(size); 1184 1185 pw.println(String.format(" this: " + StatusBar.viewInfo(this) 1186 + " " + visibilityToString(getVisibility()))); 1187 1188 getWindowVisibleDisplayFrame(r); 1189 final boolean offscreen = r.right > size.x || r.bottom > size.y; 1190 pw.println(" window: " 1191 + r.toShortString() 1192 + " " + visibilityToString(getWindowVisibility()) 1193 + (offscreen ? " OFFSCREEN!" : "")); 1194 1195 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s %f", 1196 getResourceName(getCurrentView().getId()), 1197 getCurrentView().getWidth(), getCurrentView().getHeight(), 1198 visibilityToString(getCurrentView().getVisibility()), 1199 getCurrentView().getAlpha())); 1200 1201 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s", 1202 mDisabledFlags, 1203 mVertical ? "true" : "false", 1204 mShowMenu ? "true" : "false")); 1205 1206 dumpButton(pw, "back", getBackButton()); 1207 dumpButton(pw, "home", getHomeButton()); 1208 dumpButton(pw, "rcnt", getRecentsButton()); 1209 dumpButton(pw, "menu", getMenuButton()); 1210 dumpButton(pw, "a11y", getAccessibilityButton()); 1211 1212 mRecentsOnboarding.dump(pw); 1213 1214 pw.println(" }"); 1215 } 1216 1217 @Override onApplyWindowInsets(WindowInsets insets)1218 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1219 setPadding(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), 1220 insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()); 1221 return super.onApplyWindowInsets(insets); 1222 } 1223 dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1224 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 1225 pw.print(" " + caption + ": "); 1226 if (button == null) { 1227 pw.print("null"); 1228 } else { 1229 pw.print(visibilityToString(button.getVisibility()) 1230 + " alpha=" + button.getAlpha() 1231 ); 1232 } 1233 pw.println(); 1234 } 1235 1236 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)1237 void onVerticalChanged(boolean isVertical); 1238 } 1239 1240 private final Consumer<Boolean> mDockedListener = exists -> mHandler.post(() -> { 1241 mDockedStackExists = exists; 1242 updateRecentsIcon(); 1243 }); 1244 } 1245