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.app.ActivityManagerNative;
     21 import android.app.StatusBarManager;
     22 import android.app.admin.DevicePolicyManager;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     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.os.RemoteException;
     34 import android.util.AttributeSet;
     35 import android.util.Log;
     36 import android.view.Display;
     37 import android.view.MotionEvent;
     38 import android.view.Surface;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 import android.view.WindowManager;
     42 import android.view.accessibility.AccessibilityManager;
     43 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
     44 import android.widget.ImageView;
     45 import android.widget.LinearLayout;
     46 
     47 import com.android.systemui.R;
     48 import com.android.systemui.statusbar.BaseStatusBar;
     49 import com.android.systemui.statusbar.DelegateViewHelper;
     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     private static final int CAMERA_BUTTON_FADE_DURATION = 200;
     57     final static boolean DEBUG = false;
     58     final static String TAG = "PhoneStatusBar/NavigationBarView";
     59 
     60     final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
     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 DelegateViewHelper mDelegateHelper;
     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     // used to disable the camera icon in navbar when disabled by DPM
     90     private boolean mCameraDisabledByDpm;
     91 
     92     // simplified click handler to be used when device is in accessibility mode
     93     private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
     94         @Override
     95         public void onClick(View v) {
     96             if (v.getId() == R.id.camera_button) {
     97                 KeyguardTouchDelegate.getInstance(getContext()).launchCamera();
     98             } else if (v.getId() == R.id.search_light) {
     99                 KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
    100             }
    101         }
    102     };
    103 
    104     private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
    105         @Override
    106         public boolean onTouch(View cameraButtonView, MotionEvent event) {
    107             switch (event.getAction()) {
    108                 case MotionEvent.ACTION_DOWN:
    109                     // disable search gesture while interacting with camera
    110                     mDelegateHelper.setDisabled(true);
    111                     transitionCameraAndSearchButtonAlpha(0.0f);
    112                     break;
    113                 case MotionEvent.ACTION_UP:
    114                 case MotionEvent.ACTION_CANCEL:
    115                     mDelegateHelper.setDisabled(false);
    116                     transitionCameraAndSearchButtonAlpha(1.0f);
    117                     break;
    118             }
    119             return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event);
    120         }
    121     };
    122 
    123     private class H extends Handler {
    124         public void handleMessage(Message m) {
    125             switch (m.what) {
    126                 case MSG_CHECK_INVALID_LAYOUT:
    127                     final String how = "" + m.obj;
    128                     final int w = getWidth();
    129                     final int h = getHeight();
    130                     final int vw = mCurrentView.getWidth();
    131                     final int vh = mCurrentView.getHeight();
    132 
    133                     if (h != vh || w != vw) {
    134                         Log.w(TAG, String.format(
    135                             "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
    136                             how, w, h, vw, vh));
    137                         if (WORKAROUND_INVALID_LAYOUT) {
    138                             requestLayout();
    139                         }
    140                     }
    141                     break;
    142             }
    143         }
    144     }
    145 
    146     public NavigationBarView(Context context, AttributeSet attrs) {
    147         super(context, attrs);
    148 
    149         mDisplay = ((WindowManager)context.getSystemService(
    150                 Context.WINDOW_SERVICE)).getDefaultDisplay();
    151 
    152         final Resources res = mContext.getResources();
    153         mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
    154         mVertical = false;
    155         mShowMenu = false;
    156         mDelegateHelper = new DelegateViewHelper(this);
    157 
    158         getIcons(res);
    159 
    160         mBarTransitions = new NavigationBarTransitions(this);
    161 
    162         mCameraDisabledByDpm = isCameraDisabledByDpm();
    163         watchForDevicePolicyChanges();
    164     }
    165 
    166     protected void transitionCameraAndSearchButtonAlpha(float alpha) {
    167         View cameraButtonView = getCameraButton();
    168         if (cameraButtonView != null) {
    169             cameraButtonView.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
    170         }
    171         View searchLight = getSearchLight();
    172         if (searchLight != null) {
    173             searchLight.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
    174         }
    175     }
    176 
    177     private void watchForDevicePolicyChanges() {
    178         final IntentFilter filter = new IntentFilter();
    179         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
    180         mContext.registerReceiver(new BroadcastReceiver() {
    181             public void onReceive(Context context, Intent intent) {
    182                 post(new Runnable() {
    183                     @Override
    184                     public void run() {
    185                         mCameraDisabledByDpm = isCameraDisabledByDpm();
    186                     }
    187                 });
    188             }
    189         }, filter);
    190     }
    191 
    192     public BarTransitions getBarTransitions() {
    193         return mBarTransitions;
    194     }
    195 
    196     public void setDelegateView(View view) {
    197         mDelegateHelper.setDelegateView(view);
    198     }
    199 
    200     public void setBar(BaseStatusBar phoneStatusBar) {
    201         mDelegateHelper.setBar(phoneStatusBar);
    202     }
    203 
    204     @Override
    205     public boolean onTouchEvent(MotionEvent event) {
    206         if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
    207             mDeadZone.poke(event);
    208         }
    209         if (mDelegateHelper != null) {
    210             boolean ret = mDelegateHelper.onInterceptTouchEvent(event);
    211             if (ret) return true;
    212         }
    213         return super.onTouchEvent(event);
    214     }
    215 
    216     @Override
    217     public boolean onInterceptTouchEvent(MotionEvent event) {
    218         return mDelegateHelper.onInterceptTouchEvent(event);
    219     }
    220 
    221     private H mHandler = new H();
    222 
    223     public View getCurrentView() {
    224         return mCurrentView;
    225     }
    226 
    227     public View getRecentsButton() {
    228         return mCurrentView.findViewById(R.id.recent_apps);
    229     }
    230 
    231     public View getMenuButton() {
    232         return mCurrentView.findViewById(R.id.menu);
    233     }
    234 
    235     public View getBackButton() {
    236         return mCurrentView.findViewById(R.id.back);
    237     }
    238 
    239     public View getHomeButton() {
    240         return mCurrentView.findViewById(R.id.home);
    241     }
    242 
    243     // for when home is disabled, but search isn't
    244     public View getSearchLight() {
    245         return mCurrentView.findViewById(R.id.search_light);
    246     }
    247 
    248     // shown when keyguard is visible and camera is available
    249     public View getCameraButton() {
    250         return mCurrentView.findViewById(R.id.camera_button);
    251     }
    252 
    253     private void getIcons(Resources res) {
    254         mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
    255         mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
    256         mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
    257         mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
    258         mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
    259         mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land);
    260     }
    261 
    262     @Override
    263     public void setLayoutDirection(int layoutDirection) {
    264         getIcons(mContext.getResources());
    265 
    266         super.setLayoutDirection(layoutDirection);
    267     }
    268 
    269     public void notifyScreenOn(boolean screenOn) {
    270         mScreenOn = screenOn;
    271         setDisabledFlags(mDisabledFlags, true);
    272     }
    273 
    274     public void setNavigationIconHints(int hints) {
    275         setNavigationIconHints(hints, false);
    276     }
    277 
    278     public void setNavigationIconHints(int hints, boolean force) {
    279         if (!force && hints == mNavigationIconHints) return;
    280 
    281         if (DEBUG) {
    282             android.widget.Toast.makeText(mContext,
    283                 "Navigation icon hints = " + hints,
    284                 500).show();
    285         }
    286 
    287         mNavigationIconHints = hints;
    288 
    289         getBackButton().setAlpha(
    290             (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f);
    291         getHomeButton().setAlpha(
    292             (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f);
    293         getRecentsButton().setAlpha(
    294             (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f);
    295 
    296         ((ImageView)getBackButton()).setImageDrawable(
    297             (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT))
    298                 ? (mVertical ? mBackAltLandIcon : mBackAltIcon)
    299                 : (mVertical ? mBackLandIcon : mBackIcon));
    300 
    301         ((ImageView)getRecentsButton()).setImageDrawable(mVertical ? mRecentLandIcon : mRecentIcon);
    302 
    303         setDisabledFlags(mDisabledFlags, true);
    304     }
    305 
    306     public void setDisabledFlags(int disabledFlags) {
    307         setDisabledFlags(disabledFlags, false);
    308     }
    309 
    310     public void setDisabledFlags(int disabledFlags, boolean force) {
    311         if (!force && mDisabledFlags == disabledFlags) return;
    312 
    313         mDisabledFlags = disabledFlags;
    314 
    315         final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
    316         final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
    317         final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
    318                 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
    319         final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
    320 
    321         if (SLIPPERY_WHEN_DISABLED) {
    322             setSlippery(disableHome && disableRecent && disableBack && disableSearch);
    323         }
    324 
    325         if (!mScreenOn && mCurrentView != null) {
    326             ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
    327             LayoutTransition lt = navButtons == null ? null : navButtons.getLayoutTransition();
    328             if (lt != null) {
    329                 lt.disableTransitionType(
    330                         LayoutTransition.CHANGE_APPEARING | LayoutTransition.CHANGE_DISAPPEARING |
    331                         LayoutTransition.APPEARING | LayoutTransition.DISAPPEARING);
    332             }
    333         }
    334 
    335         getBackButton()   .setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
    336         getHomeButton()   .setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
    337         getRecentsButton().setVisibility(disableRecent     ? View.INVISIBLE : View.VISIBLE);
    338 
    339         final boolean shouldShowSearch = disableHome && !disableSearch;
    340         getSearchLight().setVisibility(shouldShowSearch ? View.VISIBLE : View.GONE);
    341         final View cameraButton = getCameraButton();
    342         if (cameraButton != null) {
    343             cameraButton.setVisibility(
    344                     shouldShowSearch && !mCameraDisabledByDpm ? View.VISIBLE : View.GONE);
    345         }
    346     }
    347 
    348     private boolean isCameraDisabledByDpm() {
    349         final DevicePolicyManager dpm =
    350                 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
    351         if (dpm != null) {
    352             try {
    353                 final int userId = ActivityManagerNative.getDefault().getCurrentUser().id;
    354                 final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
    355                 final  boolean disabledBecauseKeyguardSecure =
    356                         (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
    357                         && KeyguardTouchDelegate.getInstance(getContext()).isSecure();
    358                 return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
    359             } catch (RemoteException e) {
    360                 Log.e(TAG, "Can't get userId", e);
    361             }
    362         }
    363         return false;
    364     }
    365 
    366     public void setSlippery(boolean newSlippery) {
    367         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
    368         if (lp != null) {
    369             boolean oldSlippery = (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0;
    370             if (!oldSlippery && newSlippery) {
    371                 lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
    372             } else if (oldSlippery && !newSlippery) {
    373                 lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
    374             } else {
    375                 return;
    376             }
    377             WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
    378             wm.updateViewLayout(this, lp);
    379         }
    380     }
    381 
    382     public void setMenuVisibility(final boolean show) {
    383         setMenuVisibility(show, false);
    384     }
    385 
    386     public void setMenuVisibility(final boolean show, final boolean force) {
    387         if (!force && mShowMenu == show) return;
    388 
    389         mShowMenu = show;
    390 
    391         getMenuButton().setVisibility(mShowMenu ? View.VISIBLE : View.INVISIBLE);
    392     }
    393 
    394     @Override
    395     public void onFinishInflate() {
    396         mRotatedViews[Surface.ROTATION_0] =
    397         mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
    398 
    399         mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
    400 
    401         mRotatedViews[Surface.ROTATION_270] = NAVBAR_ALWAYS_AT_RIGHT
    402                                                 ? findViewById(R.id.rot90)
    403                                                 : findViewById(R.id.rot270);
    404 
    405         mCurrentView = mRotatedViews[Surface.ROTATION_0];
    406 
    407         watchForAccessibilityChanges();
    408     }
    409 
    410     private void watchForAccessibilityChanges() {
    411         final AccessibilityManager am =
    412                 (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
    413 
    414         // Set the initial state
    415         enableAccessibility(am.isTouchExplorationEnabled());
    416 
    417         // Watch for changes
    418         am.addTouchExplorationStateChangeListener(new TouchExplorationStateChangeListener() {
    419             @Override
    420             public void onTouchExplorationStateChanged(boolean enabled) {
    421                 enableAccessibility(enabled);
    422             }
    423         });
    424     }
    425 
    426     private void enableAccessibility(boolean touchEnabled) {
    427         Log.v(TAG, "touchEnabled:"  + touchEnabled);
    428 
    429         // Add a touch handler or accessibility click listener for camera and search buttons
    430         // for all view orientations.
    431         final OnClickListener onClickListener = touchEnabled ? mAccessibilityClickListener : null;
    432         final OnTouchListener onTouchListener = touchEnabled ? null : mCameraTouchListener;
    433         boolean hasCamera = false;
    434         for (int i = 0; i < mRotatedViews.length; i++) {
    435             final View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
    436             final View searchLight = mRotatedViews[i].findViewById(R.id.search_light);
    437             if (cameraButton != null) {
    438                 hasCamera = true;
    439                 cameraButton.setOnTouchListener(onTouchListener);
    440                 cameraButton.setOnClickListener(onClickListener);
    441             }
    442             if (searchLight != null) {
    443                 searchLight.setOnClickListener(onClickListener);
    444             }
    445         }
    446         if (hasCamera) {
    447             // Warm up KeyguardTouchDelegate so it's ready by the time the camera button is touched.
    448             // This will connect to KeyguardService so that touch events are processed.
    449             KeyguardTouchDelegate.getInstance(mContext);
    450         }
    451     }
    452 
    453     public boolean isVertical() {
    454         return mVertical;
    455     }
    456 
    457     public void reorient() {
    458         final int rot = mDisplay.getRotation();
    459         for (int i=0; i<4; i++) {
    460             mRotatedViews[i].setVisibility(View.GONE);
    461         }
    462         mCurrentView = mRotatedViews[rot];
    463         mCurrentView.setVisibility(View.VISIBLE);
    464 
    465         mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
    466 
    467         // force the low profile & disabled states into compliance
    468         mBarTransitions.init(mVertical);
    469         setDisabledFlags(mDisabledFlags, true /* force */);
    470         setMenuVisibility(mShowMenu, true /* force */);
    471 
    472         if (DEBUG) {
    473             Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
    474         }
    475 
    476         setNavigationIconHints(mNavigationIconHints, true);
    477     }
    478 
    479     @Override
    480     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    481         super.onLayout(changed, l, t, r, b);
    482         mDelegateHelper.setInitialTouchRegion(getHomeButton(), getBackButton(), getRecentsButton());
    483     }
    484 
    485     @Override
    486     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    487         if (DEBUG) Log.d(TAG, String.format(
    488                     "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
    489 
    490         final boolean newVertical = w > 0 && h > w;
    491         if (newVertical != mVertical) {
    492             mVertical = newVertical;
    493             //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
    494             reorient();
    495         }
    496 
    497         postCheckForInvalidLayout("sizeChanged");
    498         super.onSizeChanged(w, h, oldw, oldh);
    499     }
    500 
    501     /*
    502     @Override
    503     protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
    504         if (DEBUG) Log.d(TAG, String.format(
    505                     "onLayout: %s (%d,%d,%d,%d)",
    506                     changed?"changed":"notchanged", left, top, right, bottom));
    507         super.onLayout(changed, left, top, right, bottom);
    508     }
    509 
    510     // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
    511     // fails, any touch on the display will fix the layout.
    512     @Override
    513     public boolean onInterceptTouchEvent(MotionEvent ev) {
    514         if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
    515         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    516             postCheckForInvalidLayout("touch");
    517         }
    518         return super.onInterceptTouchEvent(ev);
    519     }
    520     */
    521 
    522 
    523     private String getResourceName(int resId) {
    524         if (resId != 0) {
    525             final android.content.res.Resources res = mContext.getResources();
    526             try {
    527                 return res.getResourceName(resId);
    528             } catch (android.content.res.Resources.NotFoundException ex) {
    529                 return "(unknown)";
    530             }
    531         } else {
    532             return "(null)";
    533         }
    534     }
    535 
    536     private void postCheckForInvalidLayout(final String how) {
    537         mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
    538     }
    539 
    540     private static String visibilityToString(int vis) {
    541         switch (vis) {
    542             case View.INVISIBLE:
    543                 return "INVISIBLE";
    544             case View.GONE:
    545                 return "GONE";
    546             default:
    547                 return "VISIBLE";
    548         }
    549     }
    550 
    551     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    552         pw.println("NavigationBarView {");
    553         final Rect r = new Rect();
    554         final Point size = new Point();
    555         mDisplay.getRealSize(size);
    556 
    557         pw.println(String.format("      this: " + PhoneStatusBar.viewInfo(this)
    558                         + " " + visibilityToString(getVisibility())));
    559 
    560         getWindowVisibleDisplayFrame(r);
    561         final boolean offscreen = r.right > size.x || r.bottom > size.y;
    562         pw.println("      window: "
    563                 + r.toShortString()
    564                 + " " + visibilityToString(getWindowVisibility())
    565                 + (offscreen ? " OFFSCREEN!" : ""));
    566 
    567         pw.println(String.format("      mCurrentView: id=%s (%dx%d) %s",
    568                         getResourceName(mCurrentView.getId()),
    569                         mCurrentView.getWidth(), mCurrentView.getHeight(),
    570                         visibilityToString(mCurrentView.getVisibility())));
    571 
    572         pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s",
    573                         mDisabledFlags,
    574                         mVertical ? "true" : "false",
    575                         mShowMenu ? "true" : "false"));
    576 
    577         final View back = getBackButton();
    578         final View home = getHomeButton();
    579         final View recent = getRecentsButton();
    580         final View menu = getMenuButton();
    581 
    582         pw.println("      back: "
    583                 + PhoneStatusBar.viewInfo(back)
    584                 + " " + visibilityToString(back.getVisibility())
    585                 );
    586         pw.println("      home: "
    587                 + PhoneStatusBar.viewInfo(home)
    588                 + " " + visibilityToString(home.getVisibility())
    589                 );
    590         pw.println("      rcnt: "
    591                 + PhoneStatusBar.viewInfo(recent)
    592                 + " " + visibilityToString(recent.getVisibility())
    593                 );
    594         pw.println("      menu: "
    595                 + PhoneStatusBar.viewInfo(menu)
    596                 + " " + visibilityToString(menu.getVisibility())
    597                 );
    598         pw.println("    }");
    599     }
    600 
    601 }
    602