NavigationBarView.java revision 5e25caae7a440116a3ff494c9ef8ce26ee938612
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
17package com.android.systemui.statusbar.phone;
18
19import android.animation.LayoutTransition;
20import android.animation.LayoutTransition.TransitionListener;
21import android.animation.ObjectAnimator;
22import android.animation.TimeInterpolator;
23import android.animation.ValueAnimator;
24import android.app.ActivityManagerNative;
25import android.app.StatusBarManager;
26import android.app.admin.DevicePolicyManager;
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.content.res.Resources;
32import android.graphics.Point;
33import android.graphics.Rect;
34import android.graphics.drawable.Drawable;
35import android.os.Handler;
36import android.os.Message;
37import android.os.PowerManager;
38import android.os.RemoteException;
39import android.os.UserHandle;
40import android.provider.MediaStore;
41import android.util.AttributeSet;
42import android.util.Log;
43import android.view.Display;
44import android.view.MotionEvent;
45import android.view.Surface;
46import android.view.View;
47import android.view.ViewConfiguration;
48import android.view.ViewGroup;
49import android.view.WindowManager;
50import android.view.accessibility.AccessibilityManager;
51import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
52import android.view.animation.AccelerateInterpolator;
53import android.view.animation.DecelerateInterpolator;
54import android.widget.ImageView;
55import android.widget.LinearLayout;
56
57import com.android.systemui.R;
58import com.android.systemui.statusbar.BaseStatusBar;
59import com.android.systemui.statusbar.DelegateViewHelper;
60import com.android.systemui.statusbar.policy.DeadZone;
61import com.android.systemui.statusbar.policy.KeyButtonView;
62
63import java.io.FileDescriptor;
64import java.io.PrintWriter;
65
66import static com.android.systemui.statusbar.phone.KeyguardTouchDelegate.OnKeyguardConnectionListener;
67
68public class NavigationBarView extends LinearLayout {
69    final static boolean DEBUG = false;
70    final static String TAG = "PhoneStatusBar/NavigationBarView";
71
72    final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
73
74    // slippery nav bar when everything is disabled, e.g. during setup
75    final static boolean SLIPPERY_WHEN_DISABLED = true;
76
77    final Display mDisplay;
78    View mCurrentView = null;
79    View[] mRotatedViews = new View[4];
80
81    int mBarSize;
82    boolean mVertical;
83    boolean mScreenOn;
84
85    boolean mShowMenu;
86    int mDisabledFlags = 0;
87    int mNavigationIconHints = 0;
88
89    private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
90    private Drawable mRecentIcon;
91    private Drawable mRecentLandIcon;
92
93    private DelegateViewHelper mDelegateHelper;
94    private DeadZone mDeadZone;
95    private final NavigationBarTransitions mBarTransitions;
96
97    // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
98    final static boolean WORKAROUND_INVALID_LAYOUT = true;
99    final static int MSG_CHECK_INVALID_LAYOUT = 8686;
100
101    private final float mCameraDragDistance;
102
103    // used to disable the camera icon in navbar when disabled by DPM
104    private boolean mCameraDisabledByDpm;
105
106    // performs manual animation in sync with layout transitions
107    private final NavTransitionListener mTransitionListener = new NavTransitionListener();
108
109    private final PowerManager mPowerManager;
110
111    private class NavTransitionListener implements TransitionListener {
112        private boolean mBackTransitioning;
113        private boolean mHomeAppearing;
114        private long mStartDelay;
115        private long mDuration;
116        private TimeInterpolator mInterpolator;
117
118        @Override
119        public void startTransition(LayoutTransition transition, ViewGroup container,
120                View view, int transitionType) {
121            if (view.getId() == R.id.back) {
122                mBackTransitioning = true;
123            } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
124                mHomeAppearing = true;
125                mStartDelay = transition.getStartDelay(transitionType);
126                mDuration = transition.getDuration(transitionType);
127                mInterpolator = transition.getInterpolator(transitionType);
128            }
129        }
130
131        @Override
132        public void endTransition(LayoutTransition transition, ViewGroup container,
133                View view, int transitionType) {
134            if (view.getId() == R.id.back) {
135                mBackTransitioning = false;
136            } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
137                mHomeAppearing = false;
138            }
139        }
140
141        public void onBackAltCleared() {
142            // When dismissing ime during unlock, force the back button to run the same appearance
143            // animation as home (if we catch this condition early enough).
144            if (!mBackTransitioning && getBackButton().getVisibility() == VISIBLE
145                    && mHomeAppearing && getHomeButton().getAlpha() == 0) {
146                getBackButton().setAlpha(0);
147                ValueAnimator a = ObjectAnimator.ofFloat(getBackButton(), "alpha", 0, 1);
148                a.setStartDelay(mStartDelay);
149                a.setDuration(mDuration);
150                a.setInterpolator(mInterpolator);
151                a.start();
152            }
153        }
154    }
155
156    // simplified click handler to be used when device is in accessibility mode
157    private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
158        @Override
159        public void onClick(View v) {
160            if (v.getId() == R.id.camera_button) {
161                KeyguardTouchDelegate.getInstance(getContext()).launchCamera();
162            } else if (v.getId() == R.id.search_light) {
163                KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
164            }
165        }
166    };
167
168    private final int mScaledTouchSlop;
169
170    private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
171        private float mStartX;
172        private boolean mTouchSlopReached;
173        private boolean mSkipCancelAnimation;
174
175        @Override
176        public boolean onTouch(final View cameraButtonView, MotionEvent event) {
177            float realX = event.getRawX();
178            switch (event.getAction()) {
179                case MotionEvent.ACTION_DOWN:
180                    // disable search gesture while interacting with camera
181                    mDelegateHelper.setDisabled(true);
182                    mBarTransitions.setContentVisible(false);
183                    mStartX = realX;
184                    mTouchSlopReached = false;
185                    mSkipCancelAnimation = false;
186                    break;
187                case MotionEvent.ACTION_MOVE:
188                    if (realX > mStartX) {
189                        realX = mStartX;
190                    }
191                    if (realX < mStartX - mCameraDragDistance) {
192                        ((KeyButtonView) cameraButtonView).setPressed(true);
193                        mPowerManager.userActivity(event.getEventTime(), false);
194                    } else {
195                        ((KeyButtonView) cameraButtonView).setPressed(false);
196                    }
197                    if (realX < mStartX - mScaledTouchSlop) {
198                        mTouchSlopReached = true;
199                    }
200                    cameraButtonView.setTranslationX(Math.max(realX - mStartX,
201                            -mCameraDragDistance));
202                    break;
203                case MotionEvent.ACTION_UP:
204                    if (realX < mStartX - mCameraDragDistance) {
205                        mContext.startActivityAsUser(
206                                new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
207                                UserHandle.CURRENT);
208                    }
209                    if (realX < mStartX - mScaledTouchSlop) {
210                        mTouchSlopReached = true;
211                    }
212                    if (!mTouchSlopReached) {
213                        mSkipCancelAnimation = true;
214                        cameraButtonView.animate().translationX(-mCameraDragDistance / 2).
215                                        setInterpolator(new DecelerateInterpolator()).withEndAction(
216                                new Runnable() {
217                                    @Override
218                                    public void run() {
219                                        cameraButtonView.animate().translationX(0).
220                                                setInterpolator(new AccelerateInterpolator());
221                                    }
222                                });
223                    }
224                case MotionEvent.ACTION_CANCEL:
225                    ((KeyButtonView) cameraButtonView).setPressed(false);
226                    mDelegateHelper.setDisabled(false);
227                    mBarTransitions.setContentVisible(true);
228                    if (!mSkipCancelAnimation) {
229                        cameraButtonView.animate().translationX(0)
230                                .setInterpolator(new AccelerateInterpolator(2f));
231                    }
232                    break;
233            }
234            return true;
235        }
236    };
237
238    private final OnKeyguardConnectionListener mKeyguardConnectionListener =
239            new OnKeyguardConnectionListener() {
240                @Override
241                public void onKeyguardServiceConnected(
242                        KeyguardTouchDelegate keyguardTouchDelegate) {
243                    post(new Runnable() {
244                        @Override
245                        public void run() {
246                            mCameraDisabledByDpm = isCameraDisabledByDpm();
247                        }
248                    });
249                }
250
251                @Override
252                public void onKeyguardServiceDisconnected(
253                        KeyguardTouchDelegate keyguardTouchDelegate) {
254                }
255            };
256
257    private class H extends Handler {
258        public void handleMessage(Message m) {
259            switch (m.what) {
260                case MSG_CHECK_INVALID_LAYOUT:
261                    final String how = "" + m.obj;
262                    final int w = getWidth();
263                    final int h = getHeight();
264                    final int vw = mCurrentView.getWidth();
265                    final int vh = mCurrentView.getHeight();
266
267                    if (h != vh || w != vw) {
268                        Log.w(TAG, String.format(
269                            "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
270                            how, w, h, vw, vh));
271                        if (WORKAROUND_INVALID_LAYOUT) {
272                            requestLayout();
273                        }
274                    }
275                    break;
276            }
277        }
278    }
279
280    public NavigationBarView(Context context, AttributeSet attrs) {
281        super(context, attrs);
282
283        mDisplay = ((WindowManager)context.getSystemService(
284                Context.WINDOW_SERVICE)).getDefaultDisplay();
285
286        final Resources res = getContext().getResources();
287        mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
288        mVertical = false;
289        mShowMenu = false;
290        mDelegateHelper = new DelegateViewHelper(this);
291
292        getIcons(res);
293
294        mBarTransitions = new NavigationBarTransitions(this);
295
296        KeyguardTouchDelegate.addListener(mKeyguardConnectionListener);
297        mCameraDisabledByDpm = isCameraDisabledByDpm();
298        watchForDevicePolicyChanges();
299        mCameraDragDistance = res.getDimension(R.dimen.camera_drag_distance);
300        mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
301        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
302    }
303
304    private void watchForDevicePolicyChanges() {
305        final IntentFilter filter = new IntentFilter();
306        filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
307        getContext().registerReceiver(new BroadcastReceiver() {
308            public void onReceive(Context context, Intent intent) {
309                post(new Runnable() {
310                    @Override
311                    public void run() {
312                        mCameraDisabledByDpm = isCameraDisabledByDpm();
313                    }
314                });
315            }
316        }, filter);
317    }
318
319    public BarTransitions getBarTransitions() {
320        return mBarTransitions;
321    }
322
323    public void setDelegateView(View view) {
324        mDelegateHelper.setDelegateView(view);
325    }
326
327    public void setBar(BaseStatusBar phoneStatusBar) {
328        mDelegateHelper.setBar(phoneStatusBar);
329    }
330
331    @Override
332    public boolean onTouchEvent(MotionEvent event) {
333        if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
334            mDeadZone.poke(event);
335        }
336        if (mDelegateHelper != null) {
337            boolean ret = mDelegateHelper.onInterceptTouchEvent(event);
338            if (ret) return true;
339        }
340        return super.onTouchEvent(event);
341    }
342
343    @Override
344    public boolean onInterceptTouchEvent(MotionEvent event) {
345        return mDelegateHelper.onInterceptTouchEvent(event);
346    }
347
348    private H mHandler = new H();
349
350    public View getCurrentView() {
351        return mCurrentView;
352    }
353
354    public View getRecentsButton() {
355        return mCurrentView.findViewById(R.id.recent_apps);
356    }
357
358    public View getMenuButton() {
359        return mCurrentView.findViewById(R.id.menu);
360    }
361
362    public View getBackButton() {
363        return mCurrentView.findViewById(R.id.back);
364    }
365
366    public View getHomeButton() {
367        return mCurrentView.findViewById(R.id.home);
368    }
369
370    // for when home is disabled, but search isn't
371    public View getSearchLight() {
372        return mCurrentView.findViewById(R.id.search_light);
373    }
374
375    // shown when keyguard is visible and camera is available
376    public View getCameraButton() {
377        return mCurrentView.findViewById(R.id.camera_button);
378    }
379
380    private void getIcons(Resources res) {
381        mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
382        mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
383        mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
384        mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
385        mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
386        mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land);
387    }
388
389    @Override
390    public void setLayoutDirection(int layoutDirection) {
391        getIcons(getContext().getResources());
392
393        super.setLayoutDirection(layoutDirection);
394    }
395
396    public void notifyScreenOn(boolean screenOn) {
397        mScreenOn = screenOn;
398        setDisabledFlags(mDisabledFlags, true);
399    }
400
401    public void setNavigationIconHints(int hints) {
402        setNavigationIconHints(hints, false);
403    }
404
405    public void setNavigationIconHints(int hints, boolean force) {
406        if (!force && hints == mNavigationIconHints) return;
407        final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
408        if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) {
409            mTransitionListener.onBackAltCleared();
410        }
411        if (DEBUG) {
412            android.widget.Toast.makeText(getContext(),
413                "Navigation icon hints = " + hints,
414                500).show();
415        }
416
417        mNavigationIconHints = hints;
418
419        ((ImageView)getBackButton()).setImageDrawable(backAlt
420                ? (mVertical ? mBackAltLandIcon : mBackAltIcon)
421                : (mVertical ? mBackLandIcon : mBackIcon));
422
423        ((ImageView)getRecentsButton()).setImageDrawable(mVertical ? mRecentLandIcon : mRecentIcon);
424
425        setDisabledFlags(mDisabledFlags, true);
426    }
427
428    public void setDisabledFlags(int disabledFlags) {
429        setDisabledFlags(disabledFlags, false);
430    }
431
432    public void setDisabledFlags(int disabledFlags, boolean force) {
433        if (!force && mDisabledFlags == disabledFlags) return;
434
435        mDisabledFlags = disabledFlags;
436
437        final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
438        final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
439        final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
440                && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
441        final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
442
443        if (SLIPPERY_WHEN_DISABLED) {
444            setSlippery(disableHome && disableRecent && disableBack && disableSearch);
445        }
446
447        ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
448        if (navButtons != null) {
449            LayoutTransition lt = navButtons.getLayoutTransition();
450            if (lt != null) {
451                if (!lt.getTransitionListeners().contains(mTransitionListener)) {
452                    lt.addTransitionListener(mTransitionListener);
453                }
454                if (!mScreenOn && mCurrentView != null) {
455                    lt.disableTransitionType(
456                            LayoutTransition.CHANGE_APPEARING |
457                            LayoutTransition.CHANGE_DISAPPEARING |
458                            LayoutTransition.APPEARING |
459                            LayoutTransition.DISAPPEARING);
460                }
461            }
462        }
463
464        getBackButton()   .setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
465        getHomeButton()   .setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
466        getRecentsButton().setVisibility(disableRecent     ? View.INVISIBLE : View.VISIBLE);
467
468        final boolean showSearch = disableHome && !disableSearch;
469        final boolean showCamera = showSearch && !mCameraDisabledByDpm;
470        setVisibleOrGone(getSearchLight(), showSearch);
471        setVisibleOrGone(getCameraButton(), showCamera);
472
473        mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
474    }
475
476    private void setVisibleOrGone(View view, boolean visible) {
477        if (view != null) {
478            view.setVisibility(visible ? VISIBLE : GONE);
479        }
480    }
481
482    private boolean isCameraDisabledByDpm() {
483        final DevicePolicyManager dpm =
484                (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
485        if (dpm != null) {
486            try {
487                final int userId = ActivityManagerNative.getDefault().getCurrentUser().id;
488                final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
489                final  boolean disabledBecauseKeyguardSecure =
490                        (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
491                        && KeyguardTouchDelegate.getInstance(getContext()).isSecure();
492                return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
493            } catch (RemoteException e) {
494                Log.e(TAG, "Can't get userId", e);
495            }
496        }
497        return false;
498    }
499
500    public void setSlippery(boolean newSlippery) {
501        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
502        if (lp != null) {
503            boolean oldSlippery = (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0;
504            if (!oldSlippery && newSlippery) {
505                lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
506            } else if (oldSlippery && !newSlippery) {
507                lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
508            } else {
509                return;
510            }
511            WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
512            wm.updateViewLayout(this, lp);
513        }
514    }
515
516    public void setMenuVisibility(final boolean show) {
517        setMenuVisibility(show, false);
518    }
519
520    public void setMenuVisibility(final boolean show, final boolean force) {
521        if (!force && mShowMenu == show) return;
522
523        mShowMenu = show;
524
525        getMenuButton().setVisibility(mShowMenu ? View.VISIBLE : View.INVISIBLE);
526    }
527
528    @Override
529    public void onFinishInflate() {
530        mRotatedViews[Surface.ROTATION_0] =
531        mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
532
533        mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
534
535        mRotatedViews[Surface.ROTATION_270] = NAVBAR_ALWAYS_AT_RIGHT
536                                                ? findViewById(R.id.rot90)
537                                                : findViewById(R.id.rot270);
538
539        mCurrentView = mRotatedViews[Surface.ROTATION_0];
540
541        watchForAccessibilityChanges();
542    }
543
544    private void watchForAccessibilityChanges() {
545        final AccessibilityManager am =
546                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
547
548        // Set the initial state
549        enableAccessibility(am.isTouchExplorationEnabled());
550
551        // Watch for changes
552        am.addTouchExplorationStateChangeListener(new TouchExplorationStateChangeListener() {
553            @Override
554            public void onTouchExplorationStateChanged(boolean enabled) {
555                enableAccessibility(enabled);
556            }
557        });
558    }
559
560    private void enableAccessibility(boolean touchEnabled) {
561        Log.v(TAG, "touchEnabled:"  + touchEnabled);
562
563        // Add a touch handler or accessibility click listener for camera and search buttons
564        // for all view orientations.
565        final OnClickListener onClickListener = touchEnabled ? mAccessibilityClickListener : null;
566        final OnTouchListener onTouchListener = touchEnabled ? null : mCameraTouchListener;
567        boolean hasCamera = false;
568        for (int i = 0; i < mRotatedViews.length; i++) {
569            final View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
570            final View searchLight = mRotatedViews[i].findViewById(R.id.search_light);
571            if (cameraButton != null) {
572                hasCamera = true;
573                cameraButton.setOnTouchListener(onTouchListener);
574                cameraButton.setOnClickListener(onClickListener);
575            }
576            if (searchLight != null) {
577                searchLight.setOnClickListener(onClickListener);
578            }
579        }
580        if (hasCamera) {
581            // Warm up KeyguardTouchDelegate so it's ready by the time the camera button is touched.
582            // This will connect to KeyguardService so that touch events are processed.
583            KeyguardTouchDelegate.getInstance(getContext());
584        }
585    }
586
587    public boolean isVertical() {
588        return mVertical;
589    }
590
591    public void reorient() {
592        final int rot = mDisplay.getRotation();
593        for (int i=0; i<4; i++) {
594            mRotatedViews[i].setVisibility(View.GONE);
595        }
596        mCurrentView = mRotatedViews[rot];
597        mCurrentView.setVisibility(View.VISIBLE);
598
599        mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
600
601        // force the low profile & disabled states into compliance
602        mBarTransitions.init(mVertical);
603        setDisabledFlags(mDisabledFlags, true /* force */);
604        setMenuVisibility(mShowMenu, true /* force */);
605
606        if (DEBUG) {
607            Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
608        }
609
610        // swap to x coordinate if orientation is not in vertical
611        if (mDelegateHelper != null) {
612            mDelegateHelper.setSwapXY(!mVertical);
613        }
614
615        setNavigationIconHints(mNavigationIconHints, true);
616    }
617
618    @Override
619    protected void onLayout(boolean changed, int l, int t, int r, int b) {
620        super.onLayout(changed, l, t, r, b);
621        mDelegateHelper.setInitialTouchRegion(getHomeButton(), getBackButton(), getRecentsButton());
622    }
623
624    @Override
625    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
626        if (DEBUG) Log.d(TAG, String.format(
627                    "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
628
629        final boolean newVertical = w > 0 && h > w;
630        if (newVertical != mVertical) {
631            mVertical = newVertical;
632            //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
633            reorient();
634        }
635
636        postCheckForInvalidLayout("sizeChanged");
637        super.onSizeChanged(w, h, oldw, oldh);
638    }
639
640    /*
641    @Override
642    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
643        if (DEBUG) Log.d(TAG, String.format(
644                    "onLayout: %s (%d,%d,%d,%d)",
645                    changed?"changed":"notchanged", left, top, right, bottom));
646        super.onLayout(changed, left, top, right, bottom);
647    }
648
649    // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
650    // fails, any touch on the display will fix the layout.
651    @Override
652    public boolean onInterceptTouchEvent(MotionEvent ev) {
653        if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
654        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
655            postCheckForInvalidLayout("touch");
656        }
657        return super.onInterceptTouchEvent(ev);
658    }
659    */
660
661
662    private String getResourceName(int resId) {
663        if (resId != 0) {
664            final android.content.res.Resources res = getContext().getResources();
665            try {
666                return res.getResourceName(resId);
667            } catch (android.content.res.Resources.NotFoundException ex) {
668                return "(unknown)";
669            }
670        } else {
671            return "(null)";
672        }
673    }
674
675    private void postCheckForInvalidLayout(final String how) {
676        mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
677    }
678
679    private static String visibilityToString(int vis) {
680        switch (vis) {
681            case View.INVISIBLE:
682                return "INVISIBLE";
683            case View.GONE:
684                return "GONE";
685            default:
686                return "VISIBLE";
687        }
688    }
689
690    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
691        pw.println("NavigationBarView {");
692        final Rect r = new Rect();
693        final Point size = new Point();
694        mDisplay.getRealSize(size);
695
696        pw.println(String.format("      this: " + PhoneStatusBar.viewInfo(this)
697                        + " " + visibilityToString(getVisibility())));
698
699        getWindowVisibleDisplayFrame(r);
700        final boolean offscreen = r.right > size.x || r.bottom > size.y;
701        pw.println("      window: "
702                + r.toShortString()
703                + " " + visibilityToString(getWindowVisibility())
704                + (offscreen ? " OFFSCREEN!" : ""));
705
706        pw.println(String.format("      mCurrentView: id=%s (%dx%d) %s",
707                        getResourceName(mCurrentView.getId()),
708                        mCurrentView.getWidth(), mCurrentView.getHeight(),
709                        visibilityToString(mCurrentView.getVisibility())));
710
711        pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s",
712                        mDisabledFlags,
713                        mVertical ? "true" : "false",
714                        mShowMenu ? "true" : "false"));
715
716        dumpButton(pw, "back", getBackButton());
717        dumpButton(pw, "home", getHomeButton());
718        dumpButton(pw, "rcnt", getRecentsButton());
719        dumpButton(pw, "menu", getMenuButton());
720        dumpButton(pw, "srch", getSearchLight());
721        dumpButton(pw, "cmra", getCameraButton());
722
723        pw.println("    }");
724    }
725
726    private static void dumpButton(PrintWriter pw, String caption, View button) {
727        pw.print("      " + caption + ": ");
728        if (button == null) {
729            pw.print("null");
730        } else {
731            pw.print(PhoneStatusBar.viewInfo(button)
732                    + " " + visibilityToString(button.getVisibility())
733                    + " alpha=" + button.getAlpha()
734                    );
735            if (button instanceof KeyButtonView) {
736                pw.print(" drawingAlpha=" + ((KeyButtonView)button).getDrawingAlpha());
737                pw.print(" quiescentAlpha=" + ((KeyButtonView)button).getQuiescentAlpha());
738            }
739        }
740        pw.println();
741    }
742
743}
744