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