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 android.animation.LayoutTransition; 20 import android.animation.LayoutTransition.TransitionListener; 21 import android.animation.ObjectAnimator; 22 import android.animation.TimeInterpolator; 23 import android.animation.ValueAnimator; 24 import android.app.ActivityManagerNative; 25 import android.app.StatusBarManager; 26 import android.content.Context; 27 import android.content.res.Configuration; 28 import android.graphics.Point; 29 import android.graphics.Rect; 30 import android.graphics.drawable.Drawable; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.RemoteException; 34 import android.util.AttributeSet; 35 import android.util.Log; 36 import android.util.SparseArray; 37 import android.view.Display; 38 import android.view.IDockedStackListener.Stub; 39 import android.view.MotionEvent; 40 import android.view.Surface; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.WindowManager; 44 import android.view.WindowManagerGlobal; 45 import android.view.inputmethod.InputMethodManager; 46 import android.widget.LinearLayout; 47 import com.android.systemui.R; 48 import com.android.systemui.RecentsComponent; 49 import com.android.systemui.stackdivider.Divider; 50 import com.android.systemui.statusbar.policy.DeadZone; 51 52 import java.io.FileDescriptor; 53 import java.io.PrintWriter; 54 55 public class NavigationBarView extends LinearLayout { 56 final static boolean DEBUG = false; 57 final static String TAG = "StatusBar/NavBarView"; 58 59 // slippery nav bar when everything is disabled, e.g. during setup 60 final static boolean SLIPPERY_WHEN_DISABLED = true; 61 62 final static boolean ALTERNATE_CAR_MODE_UI = false; 63 64 final Display mDisplay; 65 View mCurrentView = null; 66 View[] mRotatedViews = new View[4]; 67 68 boolean mVertical; 69 boolean mScreenOn; 70 private int mCurrentRotation = -1; 71 72 boolean mShowMenu; 73 int mDisabledFlags = 0; 74 int mNavigationIconHints = 0; 75 76 private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon; 77 private Drawable mBackCarModeIcon, mBackLandCarModeIcon; 78 private Drawable mBackAltCarModeIcon, mBackAltLandCarModeIcon; 79 private Drawable mHomeDefaultIcon, mHomeCarModeIcon; 80 private Drawable mRecentIcon; 81 private Drawable mDockedIcon; 82 private Drawable mImeIcon; 83 private Drawable mMenuIcon; 84 85 private NavigationBarGestureHelper mGestureHelper; 86 private DeadZone mDeadZone; 87 private final NavigationBarTransitions mBarTransitions; 88 89 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288) 90 final static boolean WORKAROUND_INVALID_LAYOUT = true; 91 final static int MSG_CHECK_INVALID_LAYOUT = 8686; 92 93 // performs manual animation in sync with layout transitions 94 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 95 96 private OnVerticalChangedListener mOnVerticalChangedListener; 97 private boolean mLayoutTransitionsEnabled = true; 98 private boolean mWakeAndUnlocking; 99 private boolean mUseCarModeUi = false; 100 private boolean mInCarMode = false; 101 private boolean mDockedStackExists; 102 103 private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>(); 104 private Configuration mConfiguration; 105 106 private NavigationBarInflaterView mNavigationInflaterView; 107 108 private class NavTransitionListener implements TransitionListener { 109 private boolean mBackTransitioning; 110 private boolean mHomeAppearing; 111 private long mStartDelay; 112 private long mDuration; 113 private TimeInterpolator mInterpolator; 114 115 @Override 116 public void startTransition(LayoutTransition transition, ViewGroup container, 117 View view, int transitionType) { 118 if (view.getId() == R.id.back) { 119 mBackTransitioning = true; 120 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 121 mHomeAppearing = true; 122 mStartDelay = transition.getStartDelay(transitionType); 123 mDuration = transition.getDuration(transitionType); 124 mInterpolator = transition.getInterpolator(transitionType); 125 } 126 } 127 128 @Override 129 public void endTransition(LayoutTransition transition, ViewGroup container, 130 View view, int transitionType) { 131 if (view.getId() == R.id.back) { 132 mBackTransitioning = false; 133 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 134 mHomeAppearing = false; 135 } 136 } 137 138 public void onBackAltCleared() { 139 ButtonDispatcher backButton = getBackButton(); 140 141 // When dismissing ime during unlock, force the back button to run the same appearance 142 // animation as home (if we catch this condition early enough). 143 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE 144 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 145 getBackButton().setAlpha(0); 146 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1); 147 a.setStartDelay(mStartDelay); 148 a.setDuration(mDuration); 149 a.setInterpolator(mInterpolator); 150 a.start(); 151 } 152 } 153 } 154 155 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() { 156 @Override 157 public void onClick(View view) { 158 mContext.getSystemService(InputMethodManager.class) 159 .showInputMethodPicker(true /* showAuxiliarySubtypes */); 160 } 161 }; 162 163 private class H extends Handler { 164 public void handleMessage(Message m) { 165 switch (m.what) { 166 case MSG_CHECK_INVALID_LAYOUT: 167 final String how = "" + m.obj; 168 final int w = getWidth(); 169 final int h = getHeight(); 170 final int vw = getCurrentView().getWidth(); 171 final int vh = getCurrentView().getHeight(); 172 173 if (h != vh || w != vw) { 174 Log.w(TAG, String.format( 175 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)", 176 how, w, h, vw, vh)); 177 if (WORKAROUND_INVALID_LAYOUT) { 178 requestLayout(); 179 } 180 } 181 break; 182 } 183 } 184 } 185 186 public NavigationBarView(Context context, AttributeSet attrs) { 187 super(context, attrs); 188 189 mDisplay = ((WindowManager) context.getSystemService( 190 Context.WINDOW_SERVICE)).getDefaultDisplay(); 191 192 mVertical = false; 193 mShowMenu = false; 194 mGestureHelper = new NavigationBarGestureHelper(context); 195 196 mConfiguration = new Configuration(); 197 mConfiguration.updateFrom(context.getResources().getConfiguration()); 198 updateIcons(context, Configuration.EMPTY, mConfiguration); 199 200 mBarTransitions = new NavigationBarTransitions(this); 201 202 mButtonDisatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); 203 mButtonDisatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); 204 mButtonDisatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); 205 mButtonDisatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu)); 206 mButtonDisatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher)); 207 } 208 209 public BarTransitions getBarTransitions() { 210 return mBarTransitions; 211 } 212 213 public void setComponents(RecentsComponent recentsComponent, Divider divider) { 214 mGestureHelper.setComponents(recentsComponent, divider, this); 215 } 216 217 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 218 mOnVerticalChangedListener = onVerticalChangedListener; 219 notifyVerticalChangedListener(mVertical); 220 } 221 222 @Override 223 public boolean onTouchEvent(MotionEvent event) { 224 if (mGestureHelper.onTouchEvent(event)) { 225 return true; 226 } 227 if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) { 228 mDeadZone.poke(event); 229 } 230 return super.onTouchEvent(event); 231 } 232 233 @Override 234 public boolean onInterceptTouchEvent(MotionEvent event) { 235 return mGestureHelper.onInterceptTouchEvent(event); 236 } 237 238 public void abortCurrentGesture() { 239 getHomeButton().abortCurrentGesture(); 240 } 241 242 private H mHandler = new H(); 243 244 public View getCurrentView() { 245 return mCurrentView; 246 } 247 248 public View[] getAllViews() { 249 return mRotatedViews; 250 } 251 252 public ButtonDispatcher getRecentsButton() { 253 return mButtonDisatchers.get(R.id.recent_apps); 254 } 255 256 public ButtonDispatcher getMenuButton() { 257 return mButtonDisatchers.get(R.id.menu); 258 } 259 260 public ButtonDispatcher getBackButton() { 261 return mButtonDisatchers.get(R.id.back); 262 } 263 264 public ButtonDispatcher getHomeButton() { 265 return mButtonDisatchers.get(R.id.home); 266 } 267 268 public ButtonDispatcher getImeSwitchButton() { 269 return mButtonDisatchers.get(R.id.ime_switcher); 270 } 271 272 private void updateCarModeIcons(Context ctx) { 273 mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode); 274 mBackLandCarModeIcon = mBackCarModeIcon; 275 mBackAltCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime_carmode); 276 mBackAltLandCarModeIcon = mBackAltCarModeIcon; 277 mHomeCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_home_carmode); 278 } 279 280 private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) { 281 if (oldConfig.orientation != newConfig.orientation 282 || oldConfig.densityDpi != newConfig.densityDpi) { 283 mDockedIcon = ctx.getDrawable(R.drawable.ic_sysbar_docked); 284 } 285 if (oldConfig.densityDpi != newConfig.densityDpi) { 286 mBackIcon = ctx.getDrawable(R.drawable.ic_sysbar_back); 287 mBackLandIcon = mBackIcon; 288 mBackAltIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime); 289 mBackAltLandIcon = mBackAltIcon; 290 291 mHomeDefaultIcon = ctx.getDrawable(R.drawable.ic_sysbar_home); 292 mRecentIcon = ctx.getDrawable(R.drawable.ic_sysbar_recent); 293 mMenuIcon = ctx.getDrawable(R.drawable.ic_sysbar_menu); 294 mImeIcon = ctx.getDrawable(R.drawable.ic_ime_switcher_default); 295 296 if (ALTERNATE_CAR_MODE_UI) { 297 updateCarModeIcons(ctx); 298 } 299 } 300 } 301 302 @Override 303 public void setLayoutDirection(int layoutDirection) { 304 // Reload all the icons 305 updateIcons(getContext(), Configuration.EMPTY, mConfiguration); 306 307 super.setLayoutDirection(layoutDirection); 308 } 309 310 public void notifyScreenOn(boolean screenOn) { 311 mScreenOn = screenOn; 312 setDisabledFlags(mDisabledFlags, true); 313 } 314 315 public void setNavigationIconHints(int hints) { 316 setNavigationIconHints(hints, false); 317 } 318 319 private Drawable getBackIconWithAlt(boolean carMode, boolean landscape) { 320 return landscape 321 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon 322 : carMode ? mBackAltCarModeIcon : mBackAltIcon; 323 } 324 325 private Drawable getBackIcon(boolean carMode, boolean landscape) { 326 return landscape 327 ? carMode ? mBackLandCarModeIcon : mBackLandIcon 328 : carMode ? mBackCarModeIcon : mBackIcon; 329 } 330 331 public void setNavigationIconHints(int hints, boolean force) { 332 if (!force && hints == mNavigationIconHints) return; 333 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 334 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) { 335 mTransitionListener.onBackAltCleared(); 336 } 337 if (DEBUG) { 338 android.widget.Toast.makeText(getContext(), 339 "Navigation icon hints = " + hints, 340 500).show(); 341 } 342 343 mNavigationIconHints = hints; 344 345 // We have to replace or restore the back and home button icons when exiting or entering 346 // carmode, respectively. Recents are not available in CarMode in nav bar so change 347 // to recent icon is not required. 348 Drawable backIcon = (backAlt) 349 ? getBackIconWithAlt(mUseCarModeUi, mVertical) 350 : getBackIcon(mUseCarModeUi, mVertical); 351 352 getBackButton().setImageDrawable(backIcon); 353 354 updateRecentsIcon(); 355 356 if (mUseCarModeUi) { 357 getHomeButton().setImageDrawable(mHomeCarModeIcon); 358 } else { 359 getHomeButton().setImageDrawable(mHomeDefaultIcon); 360 } 361 362 final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0); 363 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE); 364 getImeSwitchButton().setImageDrawable(mImeIcon); 365 366 // Update menu button in case the IME state has changed. 367 setMenuVisibility(mShowMenu, true); 368 getMenuButton().setImageDrawable(mMenuIcon); 369 370 setDisabledFlags(mDisabledFlags, true); 371 } 372 373 public void setDisabledFlags(int disabledFlags) { 374 setDisabledFlags(disabledFlags, false); 375 } 376 377 public void setDisabledFlags(int disabledFlags, boolean force) { 378 if (!force && mDisabledFlags == disabledFlags) return; 379 380 mDisabledFlags = disabledFlags; 381 382 final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 383 384 // Always disable recents when alternate car mode UI is active. 385 boolean disableRecent = mUseCarModeUi 386 || ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0); 387 final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) 388 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0); 389 final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0); 390 391 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 392 if (navButtons != null) { 393 LayoutTransition lt = navButtons.getLayoutTransition(); 394 if (lt != null) { 395 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 396 lt.addTransitionListener(mTransitionListener); 397 } 398 } 399 } 400 if (inLockTask() && disableRecent && !disableHome) { 401 // Don't hide recents when in lock task, it is used for exiting. 402 // Unless home is hidden, then in DPM locked mode and no exit available. 403 disableRecent = false; 404 } 405 406 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 407 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 408 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 409 } 410 411 private boolean inLockTask() { 412 try { 413 return ActivityManagerNative.getDefault().isInLockTaskMode(); 414 } catch (RemoteException e) { 415 return false; 416 } 417 } 418 419 public void setLayoutTransitionsEnabled(boolean enabled) { 420 mLayoutTransitionsEnabled = enabled; 421 updateLayoutTransitionsEnabled(); 422 } 423 424 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 425 setUseFadingAnimations(wakeAndUnlocking); 426 mWakeAndUnlocking = wakeAndUnlocking; 427 updateLayoutTransitionsEnabled(); 428 } 429 430 private void updateLayoutTransitionsEnabled() { 431 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 432 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 433 LayoutTransition lt = navButtons.getLayoutTransition(); 434 if (lt != null) { 435 if (enabled) { 436 lt.enableTransitionType(LayoutTransition.APPEARING); 437 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 438 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 439 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 440 } else { 441 lt.disableTransitionType(LayoutTransition.APPEARING); 442 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 443 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 444 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 445 } 446 } 447 } 448 449 private void setUseFadingAnimations(boolean useFadingAnimations) { 450 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); 451 if (lp != null) { 452 boolean old = lp.windowAnimations != 0; 453 if (!old && useFadingAnimations) { 454 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 455 } else if (old && !useFadingAnimations) { 456 lp.windowAnimations = 0; 457 } else { 458 return; 459 } 460 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 461 wm.updateViewLayout(this, lp); 462 } 463 } 464 465 public void setMenuVisibility(final boolean show) { 466 setMenuVisibility(show, false); 467 } 468 469 public void setMenuVisibility(final boolean show, final boolean force) { 470 if (!force && mShowMenu == show) return; 471 472 mShowMenu = show; 473 474 // Only show Menu if IME switcher not shown. 475 final boolean shouldShow = mShowMenu && 476 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0); 477 478 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE); 479 } 480 481 @Override 482 public void onFinishInflate() { 483 mNavigationInflaterView = (NavigationBarInflaterView) findViewById( 484 R.id.navigation_inflater); 485 updateRotatedViews(); 486 mNavigationInflaterView.setButtonDispatchers(mButtonDisatchers); 487 488 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 489 490 try { 491 WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() { 492 @Override 493 public void onDividerVisibilityChanged(boolean visible) throws RemoteException { 494 } 495 496 @Override 497 public void onDockedStackExistsChanged(final boolean exists) throws RemoteException { 498 mHandler.post(new Runnable() { 499 @Override 500 public void run() { 501 mDockedStackExists = exists; 502 updateRecentsIcon(); 503 } 504 }); 505 } 506 507 @Override 508 public void onDockedStackMinimizedChanged(boolean minimized, long animDuration) 509 throws RemoteException { 510 } 511 512 @Override 513 public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) 514 throws RemoteException { 515 } 516 517 @Override 518 public void onDockSideChanged(int newDockSide) throws RemoteException { 519 } 520 }); 521 } catch (RemoteException e) { 522 Log.e(TAG, "Failed registering docked stack exists listener", e); 523 } 524 } 525 526 void updateRotatedViews() { 527 mRotatedViews[Surface.ROTATION_0] = 528 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); 529 mRotatedViews[Surface.ROTATION_270] = 530 mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90); 531 532 updateCurrentView(); 533 } 534 535 public boolean needsReorient(int rotation) { 536 return mCurrentRotation != rotation; 537 } 538 539 private void updateCurrentView() { 540 final int rot = mDisplay.getRotation(); 541 for (int i=0; i<4; i++) { 542 mRotatedViews[i].setVisibility(View.GONE); 543 } 544 mCurrentView = mRotatedViews[rot]; 545 mCurrentView.setVisibility(View.VISIBLE); 546 mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90); 547 for (int i = 0; i < mButtonDisatchers.size(); i++) { 548 mButtonDisatchers.valueAt(i).setCurrentView(mCurrentView); 549 } 550 updateLayoutTransitionsEnabled(); 551 mCurrentRotation = rot; 552 } 553 554 private void updateRecentsIcon() { 555 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 556 } 557 558 public boolean isVertical() { 559 return mVertical; 560 } 561 562 public void reorient() { 563 updateCurrentView(); 564 565 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 566 567 mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone); 568 569 // force the low profile & disabled states into compliance 570 mBarTransitions.init(); 571 setDisabledFlags(mDisabledFlags, true /* force */); 572 setMenuVisibility(mShowMenu, true /* force */); 573 574 if (DEBUG) { 575 Log.d(TAG, "reorient(): rot=" + mCurrentRotation); 576 } 577 578 updateTaskSwitchHelper(); 579 setNavigationIconHints(mNavigationIconHints, true); 580 } 581 582 private void updateTaskSwitchHelper() { 583 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 584 mGestureHelper.setBarState(mVertical, isRtl); 585 } 586 587 @Override 588 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 589 if (DEBUG) Log.d(TAG, String.format( 590 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh)); 591 592 final boolean newVertical = w > 0 && h > w; 593 if (newVertical != mVertical) { 594 mVertical = newVertical; 595 //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n")); 596 reorient(); 597 notifyVerticalChangedListener(newVertical); 598 } 599 600 postCheckForInvalidLayout("sizeChanged"); 601 super.onSizeChanged(w, h, oldw, oldh); 602 } 603 604 private void notifyVerticalChangedListener(boolean newVertical) { 605 if (mOnVerticalChangedListener != null) { 606 mOnVerticalChangedListener.onVerticalChanged(newVertical); 607 } 608 } 609 610 @Override 611 protected void onConfigurationChanged(Configuration newConfig) { 612 super.onConfigurationChanged(newConfig); 613 boolean uiCarModeChanged = updateCarMode(newConfig); 614 updateTaskSwitchHelper(); 615 updateIcons(getContext(), mConfiguration, newConfig); 616 updateRecentsIcon(); 617 if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi) { 618 // If car mode or density changes, we need to reset the icons. 619 setNavigationIconHints(mNavigationIconHints, true); 620 } 621 mConfiguration.updateFrom(newConfig); 622 } 623 624 /** 625 * If the configuration changed, update the carmode and return that it was updated. 626 */ 627 private boolean updateCarMode(Configuration newConfig) { 628 boolean uiCarModeChanged = false; 629 if (newConfig != null) { 630 int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK; 631 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 632 633 if (isCarMode != mInCarMode) { 634 mInCarMode = isCarMode; 635 getHomeButton().setCarMode(isCarMode); 636 637 if (ALTERNATE_CAR_MODE_UI) { 638 mUseCarModeUi = isCarMode; 639 uiCarModeChanged = true; 640 } else { 641 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 642 mUseCarModeUi = false; 643 } 644 } 645 } 646 return uiCarModeChanged; 647 } 648 649 /* 650 @Override 651 protected void onLayout (boolean changed, int left, int top, int right, int bottom) { 652 if (DEBUG) Log.d(TAG, String.format( 653 "onLayout: %s (%d,%d,%d,%d)", 654 changed?"changed":"notchanged", left, top, right, bottom)); 655 super.onLayout(changed, left, top, right, bottom); 656 } 657 658 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else 659 // fails, any touch on the display will fix the layout. 660 @Override 661 public boolean onInterceptTouchEvent(MotionEvent ev) { 662 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString()); 663 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 664 postCheckForInvalidLayout("touch"); 665 } 666 return super.onInterceptTouchEvent(ev); 667 } 668 */ 669 670 671 private String getResourceName(int resId) { 672 if (resId != 0) { 673 final android.content.res.Resources res = getContext().getResources(); 674 try { 675 return res.getResourceName(resId); 676 } catch (android.content.res.Resources.NotFoundException ex) { 677 return "(unknown)"; 678 } 679 } else { 680 return "(null)"; 681 } 682 } 683 684 private void postCheckForInvalidLayout(final String how) { 685 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget(); 686 } 687 688 private static String visibilityToString(int vis) { 689 switch (vis) { 690 case View.INVISIBLE: 691 return "INVISIBLE"; 692 case View.GONE: 693 return "GONE"; 694 default: 695 return "VISIBLE"; 696 } 697 } 698 699 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 700 pw.println("NavigationBarView {"); 701 final Rect r = new Rect(); 702 final Point size = new Point(); 703 mDisplay.getRealSize(size); 704 705 pw.println(String.format(" this: " + PhoneStatusBar.viewInfo(this) 706 + " " + visibilityToString(getVisibility()))); 707 708 getWindowVisibleDisplayFrame(r); 709 final boolean offscreen = r.right > size.x || r.bottom > size.y; 710 pw.println(" window: " 711 + r.toShortString() 712 + " " + visibilityToString(getWindowVisibility()) 713 + (offscreen ? " OFFSCREEN!" : "")); 714 715 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s", 716 getResourceName(getCurrentView().getId()), 717 getCurrentView().getWidth(), getCurrentView().getHeight(), 718 visibilityToString(getCurrentView().getVisibility()))); 719 720 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s", 721 mDisabledFlags, 722 mVertical ? "true" : "false", 723 mShowMenu ? "true" : "false")); 724 725 dumpButton(pw, "back", getBackButton()); 726 dumpButton(pw, "home", getHomeButton()); 727 dumpButton(pw, "rcnt", getRecentsButton()); 728 dumpButton(pw, "menu", getMenuButton()); 729 730 pw.println(" }"); 731 } 732 733 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 734 pw.print(" " + caption + ": "); 735 if (button == null) { 736 pw.print("null"); 737 } else { 738 pw.print(visibilityToString(button.getVisibility()) 739 + " alpha=" + button.getAlpha() 740 ); 741 } 742 pw.println(); 743 } 744 745 public interface OnVerticalChangedListener { 746 void onVerticalChanged(boolean isVertical); 747 } 748 749 } 750