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