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.content.res.Resources; 29 import android.graphics.Point; 30 import android.graphics.Rect; 31 import android.graphics.drawable.Drawable; 32 import android.os.Handler; 33 import android.os.Message; 34 import android.os.RemoteException; 35 import android.util.AttributeSet; 36 import android.util.Log; 37 import android.view.Display; 38 import android.view.Gravity; 39 import android.view.MotionEvent; 40 import android.view.Surface; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.ViewRootImpl; 44 import android.view.WindowManager; 45 import android.view.inputmethod.InputMethodManager; 46 import android.widget.FrameLayout; 47 import android.widget.ImageView; 48 import android.widget.LinearLayout; 49 50 import com.android.systemui.R; 51 import com.android.systemui.statusbar.policy.DeadZone; 52 import com.android.systemui.statusbar.policy.KeyButtonView; 53 54 import java.io.FileDescriptor; 55 import java.io.PrintWriter; 56 import java.util.ArrayList; 57 58 public class NavigationBarView extends LinearLayout { 59 final static boolean DEBUG = false; 60 final static String TAG = "PhoneStatusBar/NavigationBarView"; 61 62 // slippery nav bar when everything is disabled, e.g. during setup 63 final static boolean SLIPPERY_WHEN_DISABLED = true; 64 65 final Display mDisplay; 66 View mCurrentView = null; 67 View[] mRotatedViews = new View[4]; 68 69 int mBarSize; 70 boolean mVertical; 71 boolean mScreenOn; 72 73 boolean mShowMenu; 74 int mDisabledFlags = 0; 75 int mNavigationIconHints = 0; 76 77 private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon; 78 private Drawable mRecentIcon; 79 private Drawable mRecentLandIcon; 80 81 private NavigationBarViewTaskSwitchHelper mTaskSwitchHelper; 82 private DeadZone mDeadZone; 83 private final NavigationBarTransitions mBarTransitions; 84 85 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288) 86 final static boolean WORKAROUND_INVALID_LAYOUT = true; 87 final static int MSG_CHECK_INVALID_LAYOUT = 8686; 88 89 // performs manual animation in sync with layout transitions 90 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 91 92 private OnVerticalChangedListener mOnVerticalChangedListener; 93 private boolean mIsLayoutRtl; 94 private boolean mLayoutTransitionsEnabled; 95 96 private class NavTransitionListener implements TransitionListener { 97 private boolean mBackTransitioning; 98 private boolean mHomeAppearing; 99 private long mStartDelay; 100 private long mDuration; 101 private TimeInterpolator mInterpolator; 102 103 @Override startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)104 public void startTransition(LayoutTransition transition, ViewGroup container, 105 View view, int transitionType) { 106 if (view.getId() == R.id.back) { 107 mBackTransitioning = true; 108 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 109 mHomeAppearing = true; 110 mStartDelay = transition.getStartDelay(transitionType); 111 mDuration = transition.getDuration(transitionType); 112 mInterpolator = transition.getInterpolator(transitionType); 113 } 114 } 115 116 @Override endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)117 public void endTransition(LayoutTransition transition, ViewGroup container, 118 View view, int transitionType) { 119 if (view.getId() == R.id.back) { 120 mBackTransitioning = false; 121 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 122 mHomeAppearing = false; 123 } 124 } 125 onBackAltCleared()126 public void onBackAltCleared() { 127 // When dismissing ime during unlock, force the back button to run the same appearance 128 // animation as home (if we catch this condition early enough). 129 if (!mBackTransitioning && getBackButton().getVisibility() == VISIBLE 130 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 131 getBackButton().setAlpha(0); 132 ValueAnimator a = ObjectAnimator.ofFloat(getBackButton(), "alpha", 0, 1); 133 a.setStartDelay(mStartDelay); 134 a.setDuration(mDuration); 135 a.setInterpolator(mInterpolator); 136 a.start(); 137 } 138 } 139 } 140 141 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() { 142 @Override 143 public void onClick(View view) { 144 ((InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE)) 145 .showInputMethodPicker(true /* showAuxiliarySubtypes */); 146 } 147 }; 148 149 private class H extends Handler { handleMessage(Message m)150 public void handleMessage(Message m) { 151 switch (m.what) { 152 case MSG_CHECK_INVALID_LAYOUT: 153 final String how = "" + m.obj; 154 final int w = getWidth(); 155 final int h = getHeight(); 156 final int vw = mCurrentView.getWidth(); 157 final int vh = mCurrentView.getHeight(); 158 159 if (h != vh || w != vw) { 160 Log.w(TAG, String.format( 161 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)", 162 how, w, h, vw, vh)); 163 if (WORKAROUND_INVALID_LAYOUT) { 164 requestLayout(); 165 } 166 } 167 break; 168 } 169 } 170 } 171 NavigationBarView(Context context, AttributeSet attrs)172 public NavigationBarView(Context context, AttributeSet attrs) { 173 super(context, attrs); 174 175 mDisplay = ((WindowManager)context.getSystemService( 176 Context.WINDOW_SERVICE)).getDefaultDisplay(); 177 178 final Resources res = getContext().getResources(); 179 mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size); 180 mVertical = false; 181 mShowMenu = false; 182 mTaskSwitchHelper = new NavigationBarViewTaskSwitchHelper(context); 183 184 getIcons(res); 185 186 mBarTransitions = new NavigationBarTransitions(this); 187 } 188 189 @Override onAttachedToWindow()190 protected void onAttachedToWindow() { 191 super.onAttachedToWindow(); 192 ViewRootImpl root = getViewRootImpl(); 193 if (root != null) { 194 root.setDrawDuringWindowsAnimating(true); 195 } 196 } 197 getBarTransitions()198 public BarTransitions getBarTransitions() { 199 return mBarTransitions; 200 } 201 setBar(PhoneStatusBar phoneStatusBar)202 public void setBar(PhoneStatusBar phoneStatusBar) { 203 mTaskSwitchHelper.setBar(phoneStatusBar); 204 } 205 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)206 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 207 mOnVerticalChangedListener = onVerticalChangedListener; 208 notifyVerticalChangedListener(mVertical); 209 } 210 211 @Override onTouchEvent(MotionEvent event)212 public boolean onTouchEvent(MotionEvent event) { 213 if (mTaskSwitchHelper.onTouchEvent(event)) { 214 return true; 215 } 216 if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) { 217 mDeadZone.poke(event); 218 } 219 return super.onTouchEvent(event); 220 } 221 222 @Override onInterceptTouchEvent(MotionEvent event)223 public boolean onInterceptTouchEvent(MotionEvent event) { 224 return mTaskSwitchHelper.onInterceptTouchEvent(event); 225 } 226 abortCurrentGesture()227 public void abortCurrentGesture() { 228 getHomeButton().abortCurrentGesture(); 229 } 230 231 private H mHandler = new H(); 232 getCurrentView()233 public View getCurrentView() { 234 return mCurrentView; 235 } 236 getRecentsButton()237 public View getRecentsButton() { 238 return mCurrentView.findViewById(R.id.recent_apps); 239 } 240 getMenuButton()241 public View getMenuButton() { 242 return mCurrentView.findViewById(R.id.menu); 243 } 244 getBackButton()245 public View getBackButton() { 246 return mCurrentView.findViewById(R.id.back); 247 } 248 getHomeButton()249 public KeyButtonView getHomeButton() { 250 return (KeyButtonView) mCurrentView.findViewById(R.id.home); 251 } 252 getImeSwitchButton()253 public View getImeSwitchButton() { 254 return mCurrentView.findViewById(R.id.ime_switcher); 255 } 256 getIcons(Resources res)257 private void getIcons(Resources res) { 258 mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back); 259 mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land); 260 mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime); 261 mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime_land); 262 mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent); 263 mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land); 264 } 265 266 @Override setLayoutDirection(int layoutDirection)267 public void setLayoutDirection(int layoutDirection) { 268 getIcons(getContext().getResources()); 269 270 super.setLayoutDirection(layoutDirection); 271 } 272 notifyScreenOn(boolean screenOn)273 public void notifyScreenOn(boolean screenOn) { 274 mScreenOn = screenOn; 275 setDisabledFlags(mDisabledFlags, true); 276 } 277 setNavigationIconHints(int hints)278 public void setNavigationIconHints(int hints) { 279 setNavigationIconHints(hints, false); 280 } 281 setNavigationIconHints(int hints, boolean force)282 public void setNavigationIconHints(int hints, boolean force) { 283 if (!force && hints == mNavigationIconHints) return; 284 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 285 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) { 286 mTransitionListener.onBackAltCleared(); 287 } 288 if (DEBUG) { 289 android.widget.Toast.makeText(getContext(), 290 "Navigation icon hints = " + hints, 291 500).show(); 292 } 293 294 mNavigationIconHints = hints; 295 296 ((ImageView)getBackButton()).setImageDrawable(backAlt 297 ? (mVertical ? mBackAltLandIcon : mBackAltIcon) 298 : (mVertical ? mBackLandIcon : mBackIcon)); 299 300 ((ImageView)getRecentsButton()).setImageDrawable(mVertical ? mRecentLandIcon : mRecentIcon); 301 302 final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0); 303 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE); 304 // Update menu button in case the IME state has changed. 305 setMenuVisibility(mShowMenu, true); 306 307 308 setDisabledFlags(mDisabledFlags, true); 309 } 310 setDisabledFlags(int disabledFlags)311 public void setDisabledFlags(int disabledFlags) { 312 setDisabledFlags(disabledFlags, false); 313 } 314 setDisabledFlags(int disabledFlags, boolean force)315 public void setDisabledFlags(int disabledFlags, boolean force) { 316 if (!force && mDisabledFlags == disabledFlags) return; 317 318 mDisabledFlags = disabledFlags; 319 320 final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 321 boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0); 322 final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) 323 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0); 324 final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0); 325 326 if (SLIPPERY_WHEN_DISABLED) { 327 setSlippery(disableHome && disableRecent && disableBack && disableSearch); 328 } 329 330 ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons); 331 if (navButtons != null) { 332 LayoutTransition lt = navButtons.getLayoutTransition(); 333 if (lt != null) { 334 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 335 lt.addTransitionListener(mTransitionListener); 336 } 337 } 338 } 339 if (inLockTask() && disableRecent && !disableHome) { 340 // Don't hide recents when in lock task, it is used for exiting. 341 // Unless home is hidden, then in DPM locked mode and no exit available. 342 disableRecent = false; 343 } 344 345 getBackButton() .setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 346 getHomeButton() .setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 347 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 348 } 349 inLockTask()350 private boolean inLockTask() { 351 try { 352 return ActivityManagerNative.getDefault().isInLockTaskMode(); 353 } catch (RemoteException e) { 354 return false; 355 } 356 } 357 setVisibleOrGone(View view, boolean visible)358 private void setVisibleOrGone(View view, boolean visible) { 359 if (view != null) { 360 view.setVisibility(visible ? VISIBLE : GONE); 361 } 362 } 363 setWakeAndUnlocking(boolean wakeAndUnlocking)364 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 365 setUseFadingAnimations(wakeAndUnlocking); 366 setLayoutTransitionsEnabled(!wakeAndUnlocking); 367 } 368 setLayoutTransitionsEnabled(boolean enabled)369 private void setLayoutTransitionsEnabled(boolean enabled) { 370 mLayoutTransitionsEnabled = enabled; 371 ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons); 372 LayoutTransition lt = navButtons.getLayoutTransition(); 373 if (lt != null) { 374 if (enabled) { 375 lt.enableTransitionType(LayoutTransition.APPEARING); 376 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 377 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 378 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 379 } else { 380 lt.disableTransitionType(LayoutTransition.APPEARING); 381 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 382 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 383 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 384 } 385 } 386 } 387 setUseFadingAnimations(boolean useFadingAnimations)388 private void setUseFadingAnimations(boolean useFadingAnimations) { 389 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); 390 if (lp != null) { 391 boolean old = lp.windowAnimations != 0; 392 if (!old && useFadingAnimations) { 393 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 394 } else if (old && !useFadingAnimations) { 395 lp.windowAnimations = 0; 396 } else { 397 return; 398 } 399 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 400 wm.updateViewLayout(this, lp); 401 } 402 } 403 setSlippery(boolean newSlippery)404 public void setSlippery(boolean newSlippery) { 405 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); 406 if (lp != null) { 407 boolean oldSlippery = (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0; 408 if (!oldSlippery && newSlippery) { 409 lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; 410 } else if (oldSlippery && !newSlippery) { 411 lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; 412 } else { 413 return; 414 } 415 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 416 wm.updateViewLayout(this, lp); 417 } 418 } 419 setMenuVisibility(final boolean show)420 public void setMenuVisibility(final boolean show) { 421 setMenuVisibility(show, false); 422 } 423 setMenuVisibility(final boolean show, final boolean force)424 public void setMenuVisibility(final boolean show, final boolean force) { 425 if (!force && mShowMenu == show) return; 426 427 mShowMenu = show; 428 429 // Only show Menu if IME switcher not shown. 430 final boolean shouldShow = mShowMenu && 431 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0); 432 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE); 433 } 434 435 @Override onFinishInflate()436 public void onFinishInflate() { 437 mRotatedViews[Surface.ROTATION_0] = 438 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); 439 440 mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90); 441 442 mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90]; 443 444 mCurrentView = mRotatedViews[Surface.ROTATION_0]; 445 446 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 447 448 updateRTLOrder(); 449 } 450 isVertical()451 public boolean isVertical() { 452 return mVertical; 453 } 454 reorient()455 public void reorient() { 456 final int rot = mDisplay.getRotation(); 457 for (int i=0; i<4; i++) { 458 mRotatedViews[i].setVisibility(View.GONE); 459 } 460 mCurrentView = mRotatedViews[rot]; 461 mCurrentView.setVisibility(View.VISIBLE); 462 setLayoutTransitionsEnabled(mLayoutTransitionsEnabled); 463 464 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 465 466 mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone); 467 468 // force the low profile & disabled states into compliance 469 mBarTransitions.init(); 470 setDisabledFlags(mDisabledFlags, true /* force */); 471 setMenuVisibility(mShowMenu, true /* force */); 472 473 if (DEBUG) { 474 Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation()); 475 } 476 477 updateTaskSwitchHelper(); 478 479 setNavigationIconHints(mNavigationIconHints, true); 480 } 481 updateTaskSwitchHelper()482 private void updateTaskSwitchHelper() { 483 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 484 mTaskSwitchHelper.setBarState(mVertical, isRtl); 485 } 486 487 @Override onSizeChanged(int w, int h, int oldw, int oldh)488 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 489 if (DEBUG) Log.d(TAG, String.format( 490 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh)); 491 492 final boolean newVertical = w > 0 && h > w; 493 if (newVertical != mVertical) { 494 mVertical = newVertical; 495 //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n")); 496 reorient(); 497 notifyVerticalChangedListener(newVertical); 498 } 499 500 postCheckForInvalidLayout("sizeChanged"); 501 super.onSizeChanged(w, h, oldw, oldh); 502 } 503 notifyVerticalChangedListener(boolean newVertical)504 private void notifyVerticalChangedListener(boolean newVertical) { 505 if (mOnVerticalChangedListener != null) { 506 mOnVerticalChangedListener.onVerticalChanged(newVertical); 507 } 508 } 509 510 @Override onConfigurationChanged(Configuration newConfig)511 protected void onConfigurationChanged(Configuration newConfig) { 512 super.onConfigurationChanged(newConfig); 513 updateRTLOrder(); 514 updateTaskSwitchHelper(); 515 } 516 517 /** 518 * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we 519 * have to do it manually 520 */ updateRTLOrder()521 private void updateRTLOrder() { 522 boolean isLayoutRtl = getResources().getConfiguration() 523 .getLayoutDirection() == LAYOUT_DIRECTION_RTL; 524 if (mIsLayoutRtl != isLayoutRtl) { 525 526 // We swap all children of the 90 and 270 degree layouts, since they are vertical 527 View rotation90 = mRotatedViews[Surface.ROTATION_90]; 528 swapChildrenOrderIfVertical(rotation90.findViewById(R.id.nav_buttons)); 529 adjustExtraKeyGravity(rotation90, isLayoutRtl); 530 531 View rotation270 = mRotatedViews[Surface.ROTATION_270]; 532 if (rotation90 != rotation270) { 533 swapChildrenOrderIfVertical(rotation270.findViewById(R.id.nav_buttons)); 534 adjustExtraKeyGravity(rotation270, isLayoutRtl); 535 } 536 mIsLayoutRtl = isLayoutRtl; 537 } 538 } 539 adjustExtraKeyGravity(View navBar, boolean isLayoutRtl)540 private void adjustExtraKeyGravity(View navBar, boolean isLayoutRtl) { 541 View menu = navBar.findViewById(R.id.menu); 542 View imeSwitcher = navBar.findViewById(R.id.ime_switcher); 543 if (menu != null) { 544 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) menu.getLayoutParams(); 545 lp.gravity = isLayoutRtl ? Gravity.BOTTOM : Gravity.TOP; 546 menu.setLayoutParams(lp); 547 } 548 if (imeSwitcher != null) { 549 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) imeSwitcher.getLayoutParams(); 550 lp.gravity = isLayoutRtl ? Gravity.BOTTOM : Gravity.TOP; 551 imeSwitcher.setLayoutParams(lp); 552 } 553 } 554 555 /** 556 * Swaps the children order of a LinearLayout if it's orientation is Vertical 557 * 558 * @param group The LinearLayout to swap the children from. 559 */ swapChildrenOrderIfVertical(View group)560 private void swapChildrenOrderIfVertical(View group) { 561 if (group instanceof LinearLayout) { 562 LinearLayout linearLayout = (LinearLayout) group; 563 if (linearLayout.getOrientation() == VERTICAL) { 564 int childCount = linearLayout.getChildCount(); 565 ArrayList<View> childList = new ArrayList<>(childCount); 566 for (int i = 0; i < childCount; i++) { 567 childList.add(linearLayout.getChildAt(i)); 568 } 569 linearLayout.removeAllViews(); 570 for (int i = childCount - 1; i >= 0; i--) { 571 linearLayout.addView(childList.get(i)); 572 } 573 } 574 } 575 } 576 577 /* 578 @Override 579 protected void onLayout (boolean changed, int left, int top, int right, int bottom) { 580 if (DEBUG) Log.d(TAG, String.format( 581 "onLayout: %s (%d,%d,%d,%d)", 582 changed?"changed":"notchanged", left, top, right, bottom)); 583 super.onLayout(changed, left, top, right, bottom); 584 } 585 586 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else 587 // fails, any touch on the display will fix the layout. 588 @Override 589 public boolean onInterceptTouchEvent(MotionEvent ev) { 590 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString()); 591 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 592 postCheckForInvalidLayout("touch"); 593 } 594 return super.onInterceptTouchEvent(ev); 595 } 596 */ 597 598 getResourceName(int resId)599 private String getResourceName(int resId) { 600 if (resId != 0) { 601 final android.content.res.Resources res = getContext().getResources(); 602 try { 603 return res.getResourceName(resId); 604 } catch (android.content.res.Resources.NotFoundException ex) { 605 return "(unknown)"; 606 } 607 } else { 608 return "(null)"; 609 } 610 } 611 postCheckForInvalidLayout(final String how)612 private void postCheckForInvalidLayout(final String how) { 613 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget(); 614 } 615 visibilityToString(int vis)616 private static String visibilityToString(int vis) { 617 switch (vis) { 618 case View.INVISIBLE: 619 return "INVISIBLE"; 620 case View.GONE: 621 return "GONE"; 622 default: 623 return "VISIBLE"; 624 } 625 } 626 dump(FileDescriptor fd, PrintWriter pw, String[] args)627 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 628 pw.println("NavigationBarView {"); 629 final Rect r = new Rect(); 630 final Point size = new Point(); 631 mDisplay.getRealSize(size); 632 633 pw.println(String.format(" this: " + PhoneStatusBar.viewInfo(this) 634 + " " + visibilityToString(getVisibility()))); 635 636 getWindowVisibleDisplayFrame(r); 637 final boolean offscreen = r.right > size.x || r.bottom > size.y; 638 pw.println(" window: " 639 + r.toShortString() 640 + " " + visibilityToString(getWindowVisibility()) 641 + (offscreen ? " OFFSCREEN!" : "")); 642 643 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s", 644 getResourceName(mCurrentView.getId()), 645 mCurrentView.getWidth(), mCurrentView.getHeight(), 646 visibilityToString(mCurrentView.getVisibility()))); 647 648 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s", 649 mDisabledFlags, 650 mVertical ? "true" : "false", 651 mShowMenu ? "true" : "false")); 652 653 dumpButton(pw, "back", getBackButton()); 654 dumpButton(pw, "home", getHomeButton()); 655 dumpButton(pw, "rcnt", getRecentsButton()); 656 dumpButton(pw, "menu", getMenuButton()); 657 658 pw.println(" }"); 659 } 660 dumpButton(PrintWriter pw, String caption, View button)661 private static void dumpButton(PrintWriter pw, String caption, View button) { 662 pw.print(" " + caption + ": "); 663 if (button == null) { 664 pw.print("null"); 665 } else { 666 pw.print(PhoneStatusBar.viewInfo(button) 667 + " " + visibilityToString(button.getVisibility()) 668 + " alpha=" + button.getAlpha() 669 ); 670 } 671 pw.println(); 672 } 673 674 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)675 void onVerticalChanged(boolean isVertical); 676 } 677 } 678