PhoneStatusBar.java revision 4f329cb77df5c53a677e5f2bcf695f6cdbe06658
1/*
2 * Copyright (C) 2010 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.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.Dialog;
26import android.app.KeyguardManager;
27import android.app.Notification;
28import android.app.PendingIntent;
29import android.app.StatusBarManager;
30import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.res.Configuration;
35import android.content.res.Resources;
36import android.graphics.Canvas;
37import android.graphics.ColorFilter;
38import android.graphics.PixelFormat;
39import android.graphics.PorterDuff;
40import android.graphics.Rect;
41import android.graphics.drawable.Drawable;
42import android.graphics.drawable.NinePatchDrawable;
43import android.inputmethodservice.InputMethodService;
44import android.os.IBinder;
45import android.os.Message;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.SystemClock;
49import android.provider.Settings;
50import android.util.DisplayMetrics;
51import android.util.Log;
52import android.util.Slog;
53import android.view.Choreographer;
54import android.view.Display;
55import android.view.Gravity;
56import android.view.IWindowManager;
57import android.view.KeyEvent;
58import android.view.MotionEvent;
59import android.view.VelocityTracker;
60import android.view.View;
61import android.view.ViewGroup;
62import android.view.ViewGroup.LayoutParams;
63import android.view.WindowManager;
64import android.view.WindowManagerImpl;
65import android.view.animation.AccelerateInterpolator;
66import android.view.animation.Animation;
67import android.view.animation.AnimationUtils;
68import android.view.animation.DecelerateInterpolator;
69import android.widget.FrameLayout;
70import android.widget.ImageView;
71import android.widget.LinearLayout;
72import android.widget.ScrollView;
73import android.widget.TextView;
74
75import com.android.internal.statusbar.StatusBarIcon;
76import com.android.internal.statusbar.StatusBarNotification;
77import com.android.systemui.R;
78import com.android.systemui.UniverseBackground;
79import com.android.systemui.recent.RecentTasksLoader;
80import com.android.systemui.statusbar.BaseStatusBar;
81import com.android.systemui.statusbar.CommandQueue;
82import com.android.systemui.statusbar.NotificationData;
83import com.android.systemui.statusbar.NotificationData.Entry;
84import com.android.systemui.statusbar.RotationToggle;
85import com.android.systemui.statusbar.SignalClusterView;
86import com.android.systemui.statusbar.StatusBarIconView;
87import com.android.systemui.statusbar.policy.BatteryController;
88import com.android.systemui.statusbar.policy.DateView;
89import com.android.systemui.statusbar.policy.IntruderAlertView;
90import com.android.systemui.statusbar.policy.LocationController;
91import com.android.systemui.statusbar.policy.NetworkController;
92import com.android.systemui.statusbar.policy.NotificationRowLayout;
93import com.android.systemui.statusbar.policy.OnSizeChangedListener;
94
95import java.io.FileDescriptor;
96import java.io.PrintWriter;
97import java.util.ArrayList;
98
99public class PhoneStatusBar extends BaseStatusBar {
100    static final String TAG = "PhoneStatusBar";
101    public static final boolean DEBUG = false;
102    public static final boolean SPEW = DEBUG;
103    public static final boolean DUMPTRUCK = true; // extra dumpsys info
104
105    // additional instrumentation for testing purposes; intended to be left on during development
106    public static final boolean CHATTY = DEBUG;
107
108    public static final String ACTION_STATUSBAR_START
109            = "com.android.internal.policy.statusbar.START";
110
111    private static final boolean DIM_BEHIND_EXPANDED_PANEL = true;
112    private static final boolean SHOW_CARRIER_LABEL = true;
113
114    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
115    private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
116    // 1020-1030 reserved for BaseStatusBar
117
118    // will likely move to a resource or other tunable param at some point
119    private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000;
120
121    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
122
123    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
124    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
125
126    // fling gesture tuning parameters, scaled to display density
127    private float mSelfExpandVelocityPx; // classic value: 2000px/s
128    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
129    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
130    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
131    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
132    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
133    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
134
135    private float mExpandAccelPx; // classic value: 2000px/s/s
136    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
137
138    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
139                                                    // faster than mSelfCollapseVelocityPx)
140
141    PhoneStatusBarPolicy mIconPolicy;
142
143    // These are no longer handled by the policy, because we need custom strategies for them
144    BatteryController mBatteryController;
145    LocationController mLocationController;
146    NetworkController mNetworkController;
147
148    int mNaturalBarHeight = -1;
149    int mIconSize = -1;
150    int mIconHPadding = -1;
151    Display mDisplay;
152
153    IWindowManager mWindowManager;
154
155    StatusBarWindowView mStatusBarWindow;
156    PhoneStatusBarView mStatusBarView;
157
158    UniverseBackground mUniverseBackground;
159
160    int mPixelFormat;
161    Object mQueueLock = new Object();
162
163    // icons
164    LinearLayout mIcons;
165    IconMerger mNotificationIcons;
166    View mMoreIcon;
167    LinearLayout mStatusIcons;
168
169    // expanded notifications
170    View mNotificationPanel; // the sliding/resizing panel within the notification window
171    ScrollView mScrollView;
172    View mExpandedContents;
173    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginLeftPx;
174    final Rect mNotificationPanelBackgroundPadding = new Rect();
175    int mNotificationPanelGravity;
176    int mNotificationPanelMinHeight;
177    boolean mNotificationPanelIsFullScreenWidth;
178
179    // top bar
180    View mClearButton;
181    View mSettingsButton;
182    RotationToggle mRotationButton;
183
184    // carrier/wifi label
185    private TextView mCarrierLabel;
186    private boolean mCarrierLabelVisible = false;
187    private int mCarrierLabelHeight;
188
189    // drag bar
190    CloseDragHandle mCloseView;
191    private int mCloseViewHeight;
192
193    // position
194    int[] mPositionTmp = new int[2];
195    boolean mExpanded;
196    boolean mExpandedVisible;
197
198    // the date view
199    DateView mDateView;
200
201    // for immersive activities
202    private IntruderAlertView mIntruderAlertView;
203
204    // on-screen navigation buttons
205    private NavigationBarView mNavigationBarView = null;
206
207    // the tracker view
208    int mTrackingPosition; // the position of the top of the tracking view.
209
210    // ticker
211    private Ticker mTicker;
212    private View mTickerView;
213    private boolean mTicking;
214
215    // Tracking finger for opening/closing.
216    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
217    boolean mTracking;
218    VelocityTracker mVelocityTracker;
219
220    Choreographer mChoreographer;
221    boolean mAnimating;
222    boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
223    float mAnimY;
224    float mAnimVel;
225    float mAnimAccel;
226    long mAnimLastTimeNanos;
227    boolean mAnimatingReveal = false;
228    int mViewDelta;
229    float mFlingVelocity;
230    int mFlingY;
231    int[] mAbsPos = new int[2];
232    Runnable mPostCollapseCleanup = null;
233
234    private AnimatorSet mLightsOutAnimation;
235    private AnimatorSet mLightsOnAnimation;
236
237    // for disabling the status bar
238    int mDisabled = 0;
239
240    // tracking calls to View.setSystemUiVisibility()
241    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
242
243    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
244
245    private int mNavigationIconHints = 0;
246    private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
247        @Override
248        public void onAnimationEnd(Animator animation) {
249            // double-check to avoid races
250            if (mIcons.getAlpha() == 0) {
251                Slog.d(TAG, "makeIconsInvisible");
252                mIcons.setVisibility(View.INVISIBLE);
253            }
254        }
255    };
256
257    private final Runnable mStartRevealAnimation = new Runnable() {
258        @Override
259        public void run() {
260            mAnimAccel = mExpandAccelPx;
261            mAnimVel = mFlingExpandMinVelocityPx;
262            mAnimY = getStatusBarHeight();
263            updateExpandedViewPos((int)mAnimY);
264
265            mAnimating = true;
266            mAnimatingReveal = true;
267            resetLastAnimTime();
268            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
269                mAnimationCallback, null);
270            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
271                mRevealAnimationCallback, null);
272            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
273                mRevealAnimationCallback, null);
274        }
275    };
276
277    private final Runnable mPerformSelfExpandFling = new Runnable() {
278        @Override
279        public void run() {
280            performFling(0, mSelfExpandVelocityPx, true);
281        }
282    };
283
284    private final Runnable mPerformFling = new Runnable() {
285        @Override
286        public void run() {
287            performFling(mFlingY + mViewDelta, mFlingVelocity, false);
288        }
289    };
290
291    @Override
292    public void start() {
293        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
294                .getDefaultDisplay();
295
296        mWindowManager = IWindowManager.Stub.asInterface(
297                ServiceManager.getService(Context.WINDOW_SERVICE));
298
299        super.start(); // calls createAndAddWindows()
300
301        addNavigationBar();
302
303        if (ENABLE_INTRUDERS) addIntruderView();
304
305        mUniverseBackground = new UniverseBackground(mContext);
306        mUniverseBackground.setVisibility(View.GONE);
307        WindowManagerImpl.getDefault().addView(mUniverseBackground,
308                mUniverseBackground.getLayoutParams(mDisplay));
309
310        // Lastly, call to the icon policy to install/update all the icons.
311        mIconPolicy = new PhoneStatusBarPolicy(mContext);
312    }
313
314    // ================================================================================
315    // Constructing the view
316    // ================================================================================
317    protected PhoneStatusBarView makeStatusBarView() {
318        final Context context = mContext;
319
320        Resources res = context.getResources();
321
322        updateDisplaySize(); // populates mDisplayMetrics
323        loadDimens();
324
325        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
326
327        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
328                R.layout.super_status_bar, null);
329        if (DEBUG) {
330            mStatusBarWindow.setBackgroundColor(0x6000FF80);
331        }
332        mStatusBarWindow.mService = this;
333        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
334            @Override
335            public boolean onTouch(View v, MotionEvent event) {
336                if (event.getAction() == MotionEvent.ACTION_DOWN) {
337                    if (mExpanded && !mAnimating) {
338                        animateCollapse();
339                    }
340                }
341                return mStatusBarWindow.onTouchEvent(event);
342            }});
343
344        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
345        mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
346        // don't allow clicks on the panel to pass through to the background where they will cause the panel to close
347        mNotificationPanel.setOnTouchListener(new View.OnTouchListener() {
348            @Override
349            public boolean onTouch(View v, MotionEvent event) {
350                return true;
351            }
352        });
353        mNotificationPanelIsFullScreenWidth =
354            (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
355        mNotificationPanel.setSystemUiVisibility(
356                  View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
357                | (mNotificationPanelIsFullScreenWidth ? 0 : View.STATUS_BAR_DISABLE_SYSTEM_INFO));
358
359        if (!ActivityManager.isHighEndGfx(mDisplay)) {
360            mStatusBarWindow.setBackground(null);
361            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
362                    R.color.notification_panel_solid_background)));
363        }
364        if (ENABLE_INTRUDERS) {
365            mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
366            mIntruderAlertView.setVisibility(View.GONE);
367            mIntruderAlertView.setBar(this);
368        }
369
370        updateShowSearchHoldoff();
371
372        mStatusBarView.mService = this;
373
374        mChoreographer = Choreographer.getInstance();
375
376        try {
377            boolean showNav = mWindowManager.hasNavigationBar();
378            if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
379            if (showNav) {
380                mNavigationBarView =
381                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
382
383                mNavigationBarView.setDisabledFlags(mDisabled);
384                mNavigationBarView.setBar(this);
385            }
386        } catch (RemoteException ex) {
387            // no window manager? good luck with that
388        }
389
390        // figure out which pixel-format to use for the status bar.
391        mPixelFormat = PixelFormat.OPAQUE;
392        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
393        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
394        mNotificationIcons.setOverflowIndicator(mMoreIcon);
395        mIcons = (LinearLayout)mStatusBarView.findViewById(R.id.icons);
396        mTickerView = mStatusBarView.findViewById(R.id.ticker);
397
398        mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);
399        mPile.setLayoutTransitionsEnabled(false);
400        mPile.setLongPressListener(getNotificationLongClicker());
401        if (SHOW_CARRIER_LABEL) {
402            mPile.setOnSizeChangedListener(new OnSizeChangedListener() {
403                @Override
404                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
405                    updateCarrierLabelVisibility(false);
406                }
407            });
408        }
409        mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
410
411        mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
412        mClearButton.setOnClickListener(mClearButtonListener);
413        mClearButton.setAlpha(0f);
414        mClearButton.setVisibility(View.INVISIBLE);
415        mClearButton.setEnabled(false);
416        mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
417        mSettingsButton = mStatusBarWindow.findViewById(R.id.settings_button);
418        mSettingsButton.setOnClickListener(mSettingsButtonListener);
419        mRotationButton = (RotationToggle) mStatusBarWindow.findViewById(R.id.rotation_lock_button);
420
421        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
422        mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
423
424        mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);
425        mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
426
427        mTicker = new MyTicker(context, mStatusBarView);
428
429        TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
430        tickerView.mTicker = mTicker;
431
432        mCloseView = (CloseDragHandle)mStatusBarWindow.findViewById(R.id.close);
433        mCloseView.mService = this;
434        mCloseViewHeight = res.getDimensionPixelSize(R.dimen.close_handle_height);
435
436        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
437
438        // set the inital view visibility
439        setAreThereNotifications();
440
441        // Other icons
442        mLocationController = new LocationController(mContext); // will post a notification
443        mBatteryController = new BatteryController(mContext);
444        mBatteryController.addIconView((ImageView)mStatusBarView.findViewById(R.id.battery));
445        mNetworkController = new NetworkController(mContext);
446        final SignalClusterView signalCluster =
447                (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
448
449        mNetworkController.addSignalCluster(signalCluster);
450        signalCluster.setNetworkController(mNetworkController);
451
452        if (SHOW_CARRIER_LABEL) {
453            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
454            // for other devices, we show whatever network is connected
455            if (mNetworkController.hasMobileDataFeature()) {
456                mNetworkController.addMobileLabelView(mCarrierLabel);
457            } else {
458                mNetworkController.addCombinedLabelView(mCarrierLabel);
459            }
460        }
461
462//        final ImageView wimaxRSSI =
463//                (ImageView)sb.findViewById(R.id.wimax_signal);
464//        if (wimaxRSSI != null) {
465//            mNetworkController.addWimaxIconView(wimaxRSSI);
466//        }
467        // Recents Panel
468        mRecentTasksLoader = new RecentTasksLoader(context);
469        updateRecentsPanel();
470
471        // receive broadcasts
472        IntentFilter filter = new IntentFilter();
473        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
474        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
475        filter.addAction(Intent.ACTION_SCREEN_OFF);
476        context.registerReceiver(mBroadcastReceiver, filter);
477
478        return mStatusBarView;
479    }
480
481    @Override
482    protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
483        boolean opaque = false;
484        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
485                layoutParams.width,
486                layoutParams.height,
487                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
488                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
489                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
490                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
491                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
492        if (ActivityManager.isHighEndGfx(mDisplay)) {
493            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
494        } else {
495            lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
496            lp.dimAmount = 0.75f;
497        }
498        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
499        lp.setTitle("RecentsPanel");
500        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
501        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
502        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
503        return lp;
504    }
505
506    @Override
507    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
508        boolean opaque = false;
509        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
510                LayoutParams.MATCH_PARENT,
511                LayoutParams.MATCH_PARENT,
512                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
513                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
514                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
515                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
516                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
517        if (ActivityManager.isHighEndGfx(mDisplay)) {
518            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
519        }
520        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
521        lp.setTitle("SearchPanel");
522        // TODO: Define custom animation for Search panel
523        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
524        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
525        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
526        return lp;
527    }
528
529    protected void updateRecentsPanel() {
530        super.updateRecentsPanel(R.layout.status_bar_recent_panel);
531        // Make .03 alpha the minimum so you always see the item a bit-- slightly below
532        // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
533        // a bit jarring
534        mRecentsPanel.setMinSwipeAlpha(0.03f);
535        if (mNavigationBarView != null) {
536            mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
537        }
538    }
539
540    @Override
541    protected void updateSearchPanel() {
542        super.updateSearchPanel();
543        mSearchPanelView.setStatusBarView(mNavigationBarView);
544        mNavigationBarView.setDelegateView(mSearchPanelView);
545    }
546
547    @Override
548    public void showSearchPanel() {
549        super.showSearchPanel();
550        WindowManager.LayoutParams lp =
551            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
552        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
553        WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp);
554    }
555
556    @Override
557    public void hideSearchPanel() {
558        super.hideSearchPanel();
559        WindowManager.LayoutParams lp =
560            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
561        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
562        WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp);
563    }
564
565    protected int getStatusBarGravity() {
566        return Gravity.TOP | Gravity.FILL_HORIZONTAL;
567    }
568
569    public int getStatusBarHeight() {
570        if (mNaturalBarHeight < 0) {
571            final Resources res = mContext.getResources();
572            mNaturalBarHeight =
573                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
574        }
575        return mNaturalBarHeight;
576    }
577
578    private int getCloseViewHeight() {
579        return mCloseViewHeight;
580    }
581
582    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
583        public void onClick(View v) {
584            toggleRecentApps();
585        }
586    };
587
588    private int mShowSearchHoldoff = 0;
589    private Runnable mShowSearchPanel = new Runnable() {
590        public void run() {
591            showSearchPanel();
592        }
593    };
594
595    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
596        public boolean onTouch(View v, MotionEvent event) {
597            switch(event.getAction()) {
598            case MotionEvent.ACTION_DOWN:
599                if (!shouldDisableNavbarGestures() && !inKeyguardRestrictedInputMode()) {
600                    mHandler.removeCallbacks(mShowSearchPanel);
601                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
602                }
603            break;
604
605            case MotionEvent.ACTION_UP:
606            case MotionEvent.ACTION_CANCEL:
607                mHandler.removeCallbacks(mShowSearchPanel);
608            break;
609        }
610        return false;
611        }
612    };
613
614    private void prepareNavigationBarView() {
615        mNavigationBarView.reorient();
616
617        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
618        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
619        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
620        updateSearchPanel();
621    }
622
623    // For small-screen devices (read: phones) that lack hardware navigation buttons
624    private void addNavigationBar() {
625        if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
626        if (mNavigationBarView == null) return;
627
628        prepareNavigationBarView();
629
630        WindowManagerImpl.getDefault().addView(
631                mNavigationBarView, getNavigationBarLayoutParams());
632    }
633
634    private void repositionNavigationBar() {
635        if (mNavigationBarView == null) return;
636
637        prepareNavigationBarView();
638
639        WindowManagerImpl.getDefault().updateViewLayout(
640                mNavigationBarView, getNavigationBarLayoutParams());
641    }
642
643    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
644        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
645                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
646                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
647                    0
648                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
649                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
650                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
651                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
652                PixelFormat.OPAQUE);
653        // this will allow the navbar to run in an overlay on devices that support this
654        if (ActivityManager.isHighEndGfx(mDisplay)) {
655            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
656        }
657
658        lp.setTitle("NavigationBar");
659        lp.windowAnimations = 0;
660        return lp;
661    }
662
663    private void addIntruderView() {
664        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
665                ViewGroup.LayoutParams.MATCH_PARENT,
666                ViewGroup.LayoutParams.WRAP_CONTENT,
667                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
668                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
669                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
670                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
671                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
672                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
673                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
674                PixelFormat.TRANSLUCENT);
675        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
676        //lp.y += height * 1.5; // FIXME
677        lp.setTitle("IntruderAlert");
678        lp.packageName = mContext.getPackageName();
679        lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert;
680
681        WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
682    }
683
684    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
685        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
686                + " icon=" + icon);
687        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
688        view.set(icon);
689        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
690    }
691
692    public void updateIcon(String slot, int index, int viewIndex,
693            StatusBarIcon old, StatusBarIcon icon) {
694        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
695                + " old=" + old + " icon=" + icon);
696        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
697        view.set(icon);
698    }
699
700    public void removeIcon(String slot, int index, int viewIndex) {
701        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
702        mStatusIcons.removeViewAt(viewIndex);
703    }
704
705    public void addNotification(IBinder key, StatusBarNotification notification) {
706        /* if (DEBUG) */ Slog.d(TAG, "addNotification score=" + notification.score);
707        StatusBarIconView iconView = addNotificationViews(key, notification);
708        if (iconView == null) return;
709
710        boolean immersive = false;
711        try {
712            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
713            if (DEBUG) {
714                Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
715            }
716        } catch (RemoteException ex) {
717        }
718
719        /*
720         * DISABLED due to missing API
721        if (ENABLE_INTRUDERS && (
722                   // TODO(dsandler): Only if the screen is on
723                notification.notification.intruderView != null)) {
724            Slog.d(TAG, "Presenting high-priority notification");
725            // special new transient ticker mode
726            // 1. Populate mIntruderAlertView
727
728            if (notification.notification.intruderView == null) {
729                Slog.e(TAG, notification.notification.toString() + " wanted to intrude but intruderView was null");
730                return;
731            }
732
733            // bind the click event to the content area
734            PendingIntent contentIntent = notification.notification.contentIntent;
735            final View.OnClickListener listener = (contentIntent != null)
736                    ? new NotificationClicker(contentIntent,
737                            notification.pkg, notification.tag, notification.id)
738                    : null;
739
740            mIntruderAlertView.applyIntruderContent(notification.notification.intruderView, listener);
741
742            mCurrentlyIntrudingNotification = notification;
743
744            // 2. Animate mIntruderAlertView in
745            mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
746
747            // 3. Set alarm to age the notification off (TODO)
748            mHandler.removeMessages(MSG_HIDE_INTRUDER);
749            if (INTRUDER_ALERT_DECAY_MS > 0) {
750                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
751            }
752        } else
753         */
754
755        if (notification.notification.fullScreenIntent != null) {
756            // not immersive & a full-screen alert should be shown
757            Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
758            try {
759                notification.notification.fullScreenIntent.send();
760            } catch (PendingIntent.CanceledException e) {
761            }
762        } else {
763            // usual case: status bar visible & not immersive
764
765            // show the ticker if there isn't an intruder too
766            if (mCurrentlyIntrudingNotification == null) {
767                tick(null, notification, true);
768            }
769        }
770
771        // Recalculate the position of the sliding windows and the titles.
772        setAreThereNotifications();
773        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
774    }
775
776    public void removeNotification(IBinder key) {
777        StatusBarNotification old = removeNotificationViews(key);
778        if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
779
780        if (old != null) {
781            // Cancel the ticker if it's still running
782            mTicker.removeEntry(old);
783
784            // Recalculate the position of the sliding windows and the titles.
785            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
786
787            if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
788                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
789            }
790
791            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mAnimating) {
792                animateCollapse();
793            }
794        }
795
796        setAreThereNotifications();
797    }
798
799    @Override
800    protected void onConfigurationChanged(Configuration newConfig) {
801        updateRecentsPanel();
802        updateShowSearchHoldoff();
803    }
804
805    private void updateShowSearchHoldoff() {
806        mShowSearchHoldoff = mContext.getResources().getInteger(
807            R.integer.config_show_search_delay);
808    }
809
810    private void loadNotificationShade() {
811        if (mPile == null) return;
812
813        int N = mNotificationData.size();
814
815        ArrayList<View> toShow = new ArrayList<View>();
816
817        final boolean provisioned = isDeviceProvisioned();
818        // If the device hasn't been through Setup, we only show system notifications
819        for (int i=0; i<N; i++) {
820            Entry ent = mNotificationData.get(N-i-1);
821            if (provisioned || showNotificationEvenIfUnprovisioned(ent.notification)) {
822                toShow.add(ent.row);
823            }
824        }
825
826        ArrayList<View> toRemove = new ArrayList<View>();
827        for (int i=0; i<mPile.getChildCount(); i++) {
828            View child = mPile.getChildAt(i);
829            if (!toShow.contains(child)) {
830                toRemove.add(child);
831            }
832        }
833
834        for (View remove : toRemove) {
835            mPile.removeView(remove);
836        }
837
838        for (int i=0; i<toShow.size(); i++) {
839            View v = toShow.get(i);
840            if (v.getParent() == null) {
841                mPile.addView(v, i);
842            }
843        }
844
845        mSettingsButton.setEnabled(isDeviceProvisioned());
846    }
847
848    @Override
849    protected void updateNotificationIcons() {
850        if (mNotificationIcons == null) return;
851
852        loadNotificationShade();
853
854        final LinearLayout.LayoutParams params
855            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
856
857        int N = mNotificationData.size();
858
859        if (DEBUG) {
860            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
861        }
862
863        ArrayList<View> toShow = new ArrayList<View>();
864
865        final boolean provisioned = isDeviceProvisioned();
866        // If the device hasn't been through Setup, we only show system notifications
867        for (int i=0; i<N; i++) {
868            Entry ent = mNotificationData.get(N-i-1);
869            if ((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
870                    || showNotificationEvenIfUnprovisioned(ent.notification)) {
871                toShow.add(ent.icon);
872            }
873        }
874
875        ArrayList<View> toRemove = new ArrayList<View>();
876        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
877            View child = mNotificationIcons.getChildAt(i);
878            if (!toShow.contains(child)) {
879                toRemove.add(child);
880            }
881        }
882
883        for (View remove : toRemove) {
884            mNotificationIcons.removeView(remove);
885        }
886
887        for (int i=0; i<toShow.size(); i++) {
888            View v = toShow.get(i);
889            if (v.getParent() == null) {
890                mNotificationIcons.addView(v, i, params);
891            }
892        }
893    }
894
895    protected void updateCarrierLabelVisibility(boolean force) {
896        if (!SHOW_CARRIER_LABEL) return;
897        // The idea here is to only show the carrier label when there is enough room to see it,
898        // i.e. when there aren't enough notifications to fill the panel.
899        if (DEBUG) {
900            Slog.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
901                    mPile.getHeight(), mScrollView.getHeight(), mCarrierLabelHeight));
902        }
903
904        final boolean makeVisible =
905            mPile.getHeight() < (mScrollView.getHeight() - mCarrierLabelHeight);
906
907        if (force || mCarrierLabelVisible != makeVisible) {
908            mCarrierLabelVisible = makeVisible;
909            if (DEBUG) {
910                Slog.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
911            }
912            mCarrierLabel.animate().cancel();
913            if (makeVisible) {
914                mCarrierLabel.setVisibility(View.VISIBLE);
915            }
916            mCarrierLabel.animate()
917                .alpha(makeVisible ? 1f : 0f)
918                //.setStartDelay(makeVisible ? 500 : 0)
919                //.setDuration(makeVisible ? 750 : 100)
920                .setDuration(150)
921                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
922                    @Override
923                    public void onAnimationEnd(Animator animation) {
924                        if (!mCarrierLabelVisible) { // race
925                            mCarrierLabel.setVisibility(View.INVISIBLE);
926                            mCarrierLabel.setAlpha(0f);
927                        }
928                    }
929                })
930                .start();
931        }
932    }
933
934    @Override
935    protected void setAreThereNotifications() {
936        final boolean any = mNotificationData.size() > 0;
937
938        final boolean clearable = any && mNotificationData.hasClearableItems();
939
940        if (DEBUG) {
941            Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
942                    + " any=" + any + " clearable=" + clearable);
943        }
944
945        if (mClearButton.isShown()) {
946            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
947                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
948                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
949                clearAnimation.addListener(new AnimatorListenerAdapter() {
950                    @Override
951                    public void onAnimationEnd(Animator animation) {
952                        if (mClearButton.getAlpha() <= 0.0f) {
953                            mClearButton.setVisibility(View.INVISIBLE);
954                        }
955                    }
956
957                    @Override
958                    public void onAnimationStart(Animator animation) {
959                        if (mClearButton.getAlpha() <= 0.0f) {
960                            mClearButton.setVisibility(View.VISIBLE);
961                        }
962                    }
963                });
964                clearAnimation.start();
965            }
966        } else {
967            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
968            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
969        }
970        mClearButton.setEnabled(clearable);
971
972        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
973        final boolean showDot = (any&&!areLightsOn());
974        if (showDot != (nlo.getAlpha() == 1.0f)) {
975            if (showDot) {
976                nlo.setAlpha(0f);
977                nlo.setVisibility(View.VISIBLE);
978            }
979            nlo.animate()
980                .alpha(showDot?1:0)
981                .setDuration(showDot?750:250)
982                .setInterpolator(new AccelerateInterpolator(2.0f))
983                .setListener(showDot ? null : new AnimatorListenerAdapter() {
984                    @Override
985                    public void onAnimationEnd(Animator _a) {
986                        nlo.setVisibility(View.GONE);
987                    }
988                })
989                .start();
990        }
991
992        updateCarrierLabelVisibility(false);
993    }
994
995    public void showClock(boolean show) {
996        if (mStatusBarView == null) return;
997        View clock = mStatusBarView.findViewById(R.id.clock);
998        if (clock != null) {
999            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1000        }
1001    }
1002
1003    /**
1004     * State is one or more of the DISABLE constants from StatusBarManager.
1005     */
1006    public void disable(int state) {
1007        final int old = mDisabled;
1008        final int diff = state ^ old;
1009        mDisabled = state;
1010
1011        if (DEBUG) {
1012            Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1013                old, state, diff));
1014        }
1015
1016        StringBuilder flagdbg = new StringBuilder();
1017        flagdbg.append("disable: < ");
1018        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1019        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1020        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1021        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1022        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1023        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1024        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
1025        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
1026        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1027        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1028        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1029        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1030        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1031        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1032        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1033        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1034        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1035        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1036        flagdbg.append(">");
1037        Slog.d(TAG, flagdbg.toString());
1038
1039        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1040            mIcons.animate().cancel();
1041            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1042                if (mTicking) {
1043                    mTicker.halt();
1044                }
1045                mIcons.animate()
1046                    .alpha(0f)
1047                    .translationY(mNaturalBarHeight*0.5f)
1048                    //.setStartDelay(100)
1049                    .setDuration(175)
1050                    .setInterpolator(new DecelerateInterpolator(1.5f))
1051                    .setListener(mMakeIconsInvisible)
1052                    .start();
1053            } else {
1054                mIcons.setVisibility(View.VISIBLE);
1055                mIcons.animate()
1056                    .alpha(1f)
1057                    .translationY(0)
1058                    .setStartDelay(0)
1059                    .setInterpolator(new DecelerateInterpolator(1.5f))
1060                    .setDuration(175)
1061                    .start();
1062            }
1063        }
1064
1065        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1066            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1067            showClock(show);
1068        }
1069        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1070            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1071                animateCollapse();
1072            }
1073        }
1074
1075        if ((diff & (StatusBarManager.DISABLE_HOME
1076                        | StatusBarManager.DISABLE_RECENT
1077                        | StatusBarManager.DISABLE_BACK)) != 0) {
1078            // the nav bar will take care of these
1079            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1080
1081            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1082                // close recents if it's visible
1083                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1084                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1085            }
1086        }
1087
1088        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1089            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1090                if (mTicking) {
1091                    mTicker.halt();
1092                } else {
1093                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1094                }
1095            } else {
1096                if (!mExpandedVisible) {
1097                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1098                }
1099            }
1100        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1101            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1102                mTicker.halt();
1103            }
1104        }
1105    }
1106
1107    @Override
1108    protected BaseStatusBar.H createHandler() {
1109        return new PhoneStatusBar.H();
1110    }
1111
1112    /**
1113     * All changes to the status bar and notifications funnel through here and are batched.
1114     */
1115    private class H extends BaseStatusBar.H {
1116        public void handleMessage(Message m) {
1117            super.handleMessage(m);
1118            switch (m.what) {
1119                case MSG_OPEN_NOTIFICATION_PANEL:
1120                    animateExpand();
1121                    break;
1122                case MSG_CLOSE_NOTIFICATION_PANEL:
1123                    animateCollapse();
1124                    break;
1125                case MSG_SHOW_INTRUDER:
1126                    setIntruderAlertVisibility(true);
1127                    break;
1128                case MSG_HIDE_INTRUDER:
1129                    setIntruderAlertVisibility(false);
1130                    mCurrentlyIntrudingNotification = null;
1131                    break;
1132            }
1133        }
1134    }
1135
1136    final Runnable mAnimationCallback = new Runnable() {
1137        @Override
1138        public void run() {
1139            doAnimation(mChoreographer.getFrameTimeNanos());
1140        }
1141    };
1142
1143    final Runnable mRevealAnimationCallback = new Runnable() {
1144        @Override
1145        public void run() {
1146            doRevealAnimation(mChoreographer.getFrameTimeNanos());
1147        }
1148    };
1149
1150    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1151        public void onFocusChange(View v, boolean hasFocus) {
1152            // Because 'v' is a ViewGroup, all its children will be (un)selected
1153            // too, which allows marqueeing to work.
1154            v.setSelected(hasFocus);
1155        }
1156    };
1157
1158    private void makeExpandedVisible(boolean revealAfterDraw) {
1159        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1160        if (mExpandedVisible) {
1161            return;
1162        }
1163
1164        mExpandedVisible = true;
1165        mPile.setLayoutTransitionsEnabled(true);
1166        if (mNavigationBarView != null)
1167            mNavigationBarView.setSlippery(true);
1168
1169        updateCarrierLabelVisibility(true);
1170
1171        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1172
1173        // Expand the window to encompass the full screen in anticipation of the drag.
1174        // This is only possible to do atomically because the status bar is at the top of the screen!
1175        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1176        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1177        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1178        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
1179        final WindowManager wm = WindowManagerImpl.getDefault();
1180        wm.updateViewLayout(mStatusBarWindow, lp);
1181
1182        // Updating the window layout will force an expensive traversal/redraw.
1183        // Kick off the reveal animation after this is complete to avoid animation latency.
1184        if (revealAfterDraw) {
1185            mHandler.post(mStartRevealAnimation);
1186        }
1187
1188        visibilityChanged(true);
1189    }
1190
1191    public void animateExpand() {
1192        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
1193        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1194            return ;
1195        }
1196        if (mExpanded) {
1197            return;
1198        }
1199
1200        prepareTracking(0, true);
1201        mHandler.post(mPerformSelfExpandFling);
1202    }
1203
1204    public void animateCollapse() {
1205        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1206    }
1207
1208    public void animateCollapse(int flags) {
1209        animateCollapse(flags, 1.0f);
1210    }
1211
1212    public void animateCollapse(int flags, float velocityMultiplier) {
1213        if (SPEW) {
1214            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
1215                    + " mExpandedVisible=" + mExpandedVisible
1216                    + " mExpanded=" + mExpanded
1217                    + " mAnimating=" + mAnimating
1218                    + " mAnimY=" + mAnimY
1219                    + " mAnimVel=" + mAnimVel
1220                    + " flags=" + flags);
1221        }
1222
1223        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1224            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1225            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1226        }
1227
1228        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1229            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1230            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1231        }
1232
1233        if (!mExpandedVisible) {
1234            return;
1235        }
1236
1237        int y;
1238        if (mAnimating) {
1239            y = (int)mAnimY;
1240        } else {
1241            y = getExpandedViewMaxHeight()-1;
1242        }
1243        // Let the fling think that we're open so it goes in the right direction
1244        // and doesn't try to re-open the windowshade.
1245        mExpanded = true;
1246        prepareTracking(y, false);
1247        performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
1248    }
1249
1250    void performExpand() {
1251        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
1252        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1253            return ;
1254        }
1255        if (mExpanded) {
1256            return;
1257        }
1258
1259        mExpanded = true;
1260        makeExpandedVisible(false);
1261        updateExpandedViewPos(EXPANDED_FULL_OPEN);
1262
1263        if (false) postStartTracing();
1264    }
1265
1266    void performCollapse() {
1267        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
1268                + " mExpandedVisible=" + mExpandedVisible);
1269
1270        if (!mExpandedVisible) {
1271            return;
1272        }
1273        mExpandedVisible = false;
1274        mPile.setLayoutTransitionsEnabled(false);
1275        if (mNavigationBarView != null)
1276            mNavigationBarView.setSlippery(false);
1277        visibilityChanged(false);
1278
1279        // Shrink the window to the size of the status bar only
1280        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1281        lp.height = getStatusBarHeight();
1282        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1283        lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1284        final WindowManager wm = WindowManagerImpl.getDefault();
1285        wm.updateViewLayout(mStatusBarWindow, lp);
1286
1287        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1288            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1289        }
1290
1291        if (!mExpanded) {
1292            return;
1293        }
1294        mExpanded = false;
1295
1296        // Close any "App info" popups that might have snuck on-screen
1297        dismissPopups();
1298
1299        if (mPostCollapseCleanup != null) {
1300            mPostCollapseCleanup.run();
1301            mPostCollapseCleanup = null;
1302        }
1303    }
1304
1305    void resetLastAnimTime() {
1306        mAnimLastTimeNanos = System.nanoTime();
1307        if (SPEW) {
1308            Throwable t = new Throwable();
1309            t.fillInStackTrace();
1310            Slog.d(TAG, "resetting last anim time=" + mAnimLastTimeNanos, t);
1311        }
1312    }
1313
1314    void doAnimation(long frameTimeNanos) {
1315        if (mAnimating) {
1316            if (SPEW) Slog.d(TAG, "doAnimation dt=" + (frameTimeNanos - mAnimLastTimeNanos));
1317            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
1318            incrementAnim(frameTimeNanos);
1319            if (SPEW) {
1320                Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
1321                Slog.d(TAG, "doAnimation expandedViewMax=" + getExpandedViewMaxHeight());
1322            }
1323
1324            if (mAnimY >= getExpandedViewMaxHeight()-1 && !mClosing) {
1325                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
1326                mAnimating = false;
1327                updateExpandedViewPos(EXPANDED_FULL_OPEN);
1328                performExpand();
1329                return;
1330            }
1331
1332            if (mAnimY == 0 && mAnimAccel == 0 && mClosing) {
1333                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
1334                mAnimating = false;
1335                performCollapse();
1336                return;
1337            }
1338
1339            if (mAnimY < getStatusBarHeight() && mClosing) {
1340                // Draw one more frame with the bar positioned at the top of the screen
1341                // before ending the animation so that the user sees the bar in
1342                // its final position.  The call to performCollapse() causes a window
1343                // relayout which takes time and might cause the animation to skip
1344                // on the very last frame before the bar disappears if we did it now.
1345                mAnimY = 0;
1346                mAnimAccel = 0;
1347                mAnimVel = 0;
1348            }
1349
1350            updateExpandedViewPos((int)mAnimY);
1351            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
1352                    mAnimationCallback, null);
1353        }
1354    }
1355
1356    void stopTracking() {
1357        if (!mTracking)
1358            return;
1359        mTracking = false;
1360        setPileLayers(View.LAYER_TYPE_NONE);
1361        mVelocityTracker.recycle();
1362        mVelocityTracker = null;
1363        mCloseView.setPressed(false);
1364    }
1365
1366    /**
1367     * Enables or disables layers on the children of the notifications pile.
1368     *
1369     * When layers are enabled, this method attempts to enable layers for the minimal
1370     * number of children. Only children visible when the notification area is fully
1371     * expanded will receive a layer. The technique used in this method might cause
1372     * more children than necessary to get a layer (at most one extra child with the
1373     * current UI.)
1374     *
1375     * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
1376     */
1377    private void setPileLayers(int layerType) {
1378        final int count = mPile.getChildCount();
1379
1380        switch (layerType) {
1381            case View.LAYER_TYPE_NONE:
1382                for (int i = 0; i < count; i++) {
1383                    mPile.getChildAt(i).setLayerType(layerType, null);
1384                }
1385                break;
1386            case View.LAYER_TYPE_HARDWARE:
1387                final int[] location = new int[2];
1388                mNotificationPanel.getLocationInWindow(location);
1389
1390                final int left = location[0];
1391                final int top = location[1];
1392                final int right = left + mNotificationPanel.getWidth();
1393                final int bottom = top + getExpandedViewMaxHeight();
1394
1395                final Rect childBounds = new Rect();
1396
1397                for (int i = 0; i < count; i++) {
1398                    final View view = mPile.getChildAt(i);
1399                    view.getLocationInWindow(location);
1400
1401                    childBounds.set(location[0], location[1],
1402                            location[0] + view.getWidth(), location[1] + view.getHeight());
1403
1404                    if (childBounds.intersects(left, top, right, bottom)) {
1405                        view.setLayerType(layerType, null);
1406                    }
1407                }
1408
1409                break;
1410        }
1411    }
1412
1413    void incrementAnim(long frameTimeNanos) {
1414        final long deltaNanos = Math.max(frameTimeNanos - mAnimLastTimeNanos, 0);
1415        final float t = deltaNanos * 0.000000001f;                  // ns -> s
1416        final float y = mAnimY;
1417        final float v = mAnimVel;                                   // px/s
1418        final float a = mAnimAccel;                                 // px/s/s
1419        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
1420        mAnimVel = v + (a*t);                                       // px/s
1421        mAnimLastTimeNanos = frameTimeNanos;                        // ns
1422        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
1423        //        + " mAnimAccel=" + mAnimAccel);
1424    }
1425
1426    void doRevealAnimation(long frameTimeNanos) {
1427        if (SPEW) {
1428            Slog.d(TAG, "doRevealAnimation: dt=" + (frameTimeNanos - mAnimLastTimeNanos));
1429        }
1430        final int h = mNotificationPanelMinHeight;
1431        if (mAnimatingReveal && mAnimating && mAnimY < h) {
1432            incrementAnim(frameTimeNanos);
1433            if (mAnimY >= h) {
1434                mAnimY = h;
1435                updateExpandedViewPos((int)mAnimY);
1436            } else {
1437                updateExpandedViewPos((int)mAnimY);
1438                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
1439                        mRevealAnimationCallback, null);
1440            }
1441        }
1442    }
1443
1444    void prepareTracking(int y, boolean opening) {
1445        if (CHATTY) {
1446            Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
1447        }
1448
1449        mCloseView.setPressed(true);
1450
1451        mTracking = true;
1452        setPileLayers(View.LAYER_TYPE_HARDWARE);
1453        mVelocityTracker = VelocityTracker.obtain();
1454        if (opening) {
1455            makeExpandedVisible(true);
1456        } else {
1457            // it's open, close it?
1458            if (mAnimating) {
1459                mAnimating = false;
1460                mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
1461                        mAnimationCallback, null);
1462            }
1463            updateExpandedViewPos(y + mViewDelta);
1464        }
1465    }
1466
1467    void performFling(int y, float vel, boolean always) {
1468        if (CHATTY) {
1469            Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel + " mExpanded=" + mExpanded);
1470        }
1471
1472        mAnimatingReveal = false;
1473
1474        mAnimY = y;
1475        mAnimVel = vel;
1476
1477        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
1478
1479        if (mExpanded) {
1480            if (!always && (
1481                    vel > mFlingCollapseMinVelocityPx
1482                    || (y > (getExpandedViewMaxHeight()*(1f-mCollapseMinDisplayFraction)) &&
1483                        vel > -mFlingExpandMinVelocityPx))) {
1484                // We are expanded, but they didn't move sufficiently to cause
1485                // us to retract.  Animate back to the expanded position.
1486                mAnimAccel = mExpandAccelPx;
1487                if (vel < 0) {
1488                    mAnimVel = 0;
1489                }
1490            }
1491            else {
1492                // We are expanded and are now going to animate away.
1493                mAnimAccel = -mCollapseAccelPx;
1494                if (vel > 0) {
1495                    mAnimVel = 0;
1496                }
1497            }
1498        } else {
1499            if (always || (
1500                    vel > mFlingExpandMinVelocityPx
1501                    || (y > (getExpandedViewMaxHeight()*(1f-mExpandMinDisplayFraction)) &&
1502                        vel > -mFlingCollapseMinVelocityPx))) {
1503                // We are collapsed, and they moved enough to allow us to
1504                // expand.  Animate in the notifications.
1505                mAnimAccel = mExpandAccelPx;
1506                if (vel < 0) {
1507                    mAnimVel = 0;
1508                }
1509            }
1510            else {
1511                // We are collapsed, but they didn't move sufficiently to cause
1512                // us to retract.  Animate back to the collapsed position.
1513                mAnimAccel = -mCollapseAccelPx;
1514                if (vel > 0) {
1515                    mAnimVel = 0;
1516                }
1517            }
1518        }
1519        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
1520        //        + " mAnimAccel=" + mAnimAccel);
1521
1522        resetLastAnimTime();
1523        mAnimating = true;
1524        mClosing = mAnimAccel < 0;
1525
1526        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
1527                mAnimationCallback, null);
1528        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
1529                mRevealAnimationCallback, null);
1530        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
1531                mAnimationCallback, null);
1532        stopTracking();
1533    }
1534
1535    boolean handleUniverseEvent(MotionEvent event) {
1536        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1537            return false;
1538        }
1539        if (mExpanded) {
1540            return false;
1541        }
1542        if (mUniverseBackground.consumeEvent(event)) {
1543            if (mTracking) {
1544                // fling back to the top, starting from the last tracked position.
1545                mFlingY = mTrackingPosition;
1546                mViewDelta = 0;
1547                mFlingVelocity = -1;
1548                mHandler.post(mPerformFling);
1549            }
1550            return true;
1551        }
1552        return false;
1553    }
1554
1555    boolean interceptTouchEvent(MotionEvent event) {
1556        if (SPEW) {
1557            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1558                + mDisabled + " mTracking=" + mTracking);
1559        } else if (CHATTY) {
1560            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1561                Slog.d(TAG, String.format(
1562                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1563                            MotionEvent.actionToString(event.getAction()),
1564                            event.getRawX(), event.getRawY(), mDisabled));
1565            }
1566        }
1567
1568        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1569            return false;
1570        }
1571
1572        final int y = (int)event.getRawY();
1573        final int action = event.getAction();
1574        final int statusBarSize = getStatusBarHeight();
1575        final int hitSize = statusBarSize*2;
1576        if (action == MotionEvent.ACTION_DOWN) {
1577            if (!areLightsOn()) {
1578                setLightsOn(true);
1579            }
1580
1581            if (!mExpanded) {
1582                mViewDelta = statusBarSize - y;
1583            } else {
1584                mCloseView.getLocationOnScreen(mAbsPos);
1585                mViewDelta = mAbsPos[1]
1586                           + getCloseViewHeight() // XXX: not closeViewHeight, but paddingBottom from the 9patch
1587                           + mNotificationPanelBackgroundPadding.top
1588                           + mNotificationPanelBackgroundPadding.bottom
1589                           - y;
1590            }
1591            if ((!mExpanded && y < hitSize) ||
1592                    // @@ add taps outside the panel if it's not full-screen
1593                    (mExpanded && y > (getExpandedViewMaxHeight()-hitSize))) {
1594                // We drop events at the edge of the screen to make the windowshade come
1595                // down by accident less, especially when pushing open a device with a keyboard
1596                // that rotates (like g1 and droid)
1597                int x = (int)event.getRawX();
1598                final int edgeBorder = mEdgeBorder;
1599                if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) {
1600                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
1601                    trackMovement(event);
1602                }
1603            }
1604        } else if (mTracking) {
1605            trackMovement(event);
1606            if (action == MotionEvent.ACTION_MOVE) {
1607                if (mAnimatingReveal && (y + mViewDelta) < mNotificationPanelMinHeight) {
1608                    // nothing
1609                } else  {
1610                    mAnimatingReveal = false;
1611                    updateExpandedViewPos(y + mViewDelta);
1612                }
1613            } else if (action == MotionEvent.ACTION_UP
1614                    || action == MotionEvent.ACTION_CANCEL) {
1615                mVelocityTracker.computeCurrentVelocity(1000);
1616
1617                float yVel = mVelocityTracker.getYVelocity();
1618                boolean negative = yVel < 0;
1619
1620                float xVel = mVelocityTracker.getXVelocity();
1621                if (xVel < 0) {
1622                    xVel = -xVel;
1623                }
1624                if (xVel > mFlingGestureMaxXVelocityPx) {
1625                    xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
1626                }
1627
1628                float vel = (float)Math.hypot(yVel, xVel);
1629                if (vel > mFlingGestureMaxOutputVelocityPx) {
1630                    vel = mFlingGestureMaxOutputVelocityPx;
1631                }
1632                if (negative) {
1633                    vel = -vel;
1634                }
1635
1636                if (CHATTY) {
1637                    Slog.d(TAG, String.format("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
1638                        mVelocityTracker.getXVelocity(),
1639                        mVelocityTracker.getYVelocity(),
1640                        xVel, yVel,
1641                        vel));
1642                }
1643
1644                if (mTrackingPosition == mNotificationPanelMinHeight) {
1645                    // start the fling from the tracking position, ignore y and view delta
1646                    mFlingY = mTrackingPosition;
1647                    mViewDelta = 0;
1648                } else {
1649                    mFlingY = y;
1650                }
1651                mFlingVelocity = vel;
1652                mHandler.post(mPerformFling);
1653            }
1654
1655        }
1656        return false;
1657    }
1658
1659    private void trackMovement(MotionEvent event) {
1660        // Add movement to velocity tracker using raw screen X and Y coordinates instead
1661        // of window coordinates because the window frame may be moving at the same time.
1662        float deltaX = event.getRawX() - event.getX();
1663        float deltaY = event.getRawY() - event.getY();
1664        event.offsetLocation(deltaX, deltaY);
1665        mVelocityTracker.addMovement(event);
1666        event.offsetLocation(-deltaX, -deltaY);
1667    }
1668
1669    @Override // CommandQueue
1670    public void setNavigationIconHints(int hints) {
1671        if (hints == mNavigationIconHints) return;
1672
1673        mNavigationIconHints = hints;
1674
1675        if (mNavigationBarView != null) {
1676            mNavigationBarView.setNavigationIconHints(hints);
1677        }
1678    }
1679
1680    @Override // CommandQueue
1681    public void setSystemUiVisibility(int vis, int mask) {
1682        final int oldVal = mSystemUiVisibility;
1683        final int newVal = (oldVal&~mask) | (vis&mask);
1684        final int diff = newVal ^ oldVal;
1685
1686        if (diff != 0) {
1687            mSystemUiVisibility = newVal;
1688
1689            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1690                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1691                if (lightsOut) {
1692                    animateCollapse();
1693                    if (mTicking) {
1694                        mTicker.halt();
1695                    }
1696                }
1697
1698                if (mNavigationBarView != null) {
1699                    mNavigationBarView.setLowProfile(lightsOut);
1700                }
1701
1702                setStatusBarLowProfile(lightsOut);
1703            }
1704
1705            notifyUiVisibilityChanged();
1706        }
1707    }
1708
1709    private void setStatusBarLowProfile(boolean lightsOut) {
1710        if (mLightsOutAnimation == null) {
1711            final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
1712            final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons);
1713            final View signal = mStatusBarView.findViewById(R.id.signal_cluster);
1714            final View battery = mStatusBarView.findViewById(R.id.battery);
1715            final View clock = mStatusBarView.findViewById(R.id.clock);
1716
1717            mLightsOutAnimation = new AnimatorSet();
1718            mLightsOutAnimation.playTogether(
1719                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 0),
1720                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 0),
1721                    ObjectAnimator.ofFloat(signal, View.ALPHA, 0),
1722                    ObjectAnimator.ofFloat(battery, View.ALPHA, 0.5f),
1723                    ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
1724                );
1725            mLightsOutAnimation.setDuration(750);
1726
1727            mLightsOnAnimation = new AnimatorSet();
1728            mLightsOnAnimation.playTogether(
1729                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
1730                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 1),
1731                    ObjectAnimator.ofFloat(signal, View.ALPHA, 1),
1732                    ObjectAnimator.ofFloat(battery, View.ALPHA, 1),
1733                    ObjectAnimator.ofFloat(clock, View.ALPHA, 1)
1734                );
1735            mLightsOnAnimation.setDuration(250);
1736        }
1737
1738        mLightsOutAnimation.cancel();
1739        mLightsOnAnimation.cancel();
1740
1741        final Animator a = lightsOut ? mLightsOutAnimation : mLightsOnAnimation;
1742        a.start();
1743
1744        setAreThereNotifications();
1745    }
1746
1747    private boolean areLightsOn() {
1748        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
1749    }
1750
1751    public void setLightsOn(boolean on) {
1752        Log.v(TAG, "setLightsOn(" + on + ")");
1753        if (on) {
1754            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1755        } else {
1756            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1757        }
1758    }
1759
1760    private void notifyUiVisibilityChanged() {
1761        try {
1762            mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
1763        } catch (RemoteException ex) {
1764        }
1765    }
1766
1767    public void topAppWindowChanged(boolean showMenu) {
1768        if (DEBUG) {
1769            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1770        }
1771        if (mNavigationBarView != null) {
1772            mNavigationBarView.setMenuVisibility(showMenu);
1773        }
1774
1775        // See above re: lights-out policy for legacy apps.
1776        if (showMenu) setLightsOn(true);
1777    }
1778
1779    @Override
1780    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1781        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1782            || ((vis & InputMethodService.IME_VISIBLE) != 0);
1783
1784        mCommandQueue.setNavigationIconHints(
1785                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1786                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1787    }
1788
1789    @Override
1790    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
1791
1792    @Override
1793    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
1794        // no ticking in lights-out mode
1795        if (!areLightsOn()) return;
1796
1797        // no ticking in Setup
1798        if (!isDeviceProvisioned()) return;
1799
1800        // Show the ticker if one is requested. Also don't do this
1801        // until status bar window is attached to the window manager,
1802        // because...  well, what's the point otherwise?  And trying to
1803        // run a ticker without being attached will crash!
1804        if (n.notification.tickerText != null && mStatusBarWindow.getWindowToken() != null) {
1805            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1806                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1807                mTicker.addEntry(n);
1808            }
1809        }
1810    }
1811
1812    private class MyTicker extends Ticker {
1813        MyTicker(Context context, View sb) {
1814            super(context, sb);
1815        }
1816
1817        @Override
1818        public void tickerStarting() {
1819            mTicking = true;
1820            mIcons.setVisibility(View.GONE);
1821            mTickerView.setVisibility(View.VISIBLE);
1822            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1823            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1824        }
1825
1826        @Override
1827        public void tickerDone() {
1828            mIcons.setVisibility(View.VISIBLE);
1829            mTickerView.setVisibility(View.GONE);
1830            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1831            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1832                        mTickingDoneListener));
1833        }
1834
1835        public void tickerHalting() {
1836            mIcons.setVisibility(View.VISIBLE);
1837            mTickerView.setVisibility(View.GONE);
1838            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1839            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1840                        mTickingDoneListener));
1841        }
1842    }
1843
1844    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1845        public void onAnimationEnd(Animation animation) {
1846            mTicking = false;
1847        }
1848        public void onAnimationRepeat(Animation animation) {
1849        }
1850        public void onAnimationStart(Animation animation) {
1851        }
1852    };
1853
1854    private Animation loadAnim(int id, Animation.AnimationListener listener) {
1855        Animation anim = AnimationUtils.loadAnimation(mContext, id);
1856        if (listener != null) {
1857            anim.setAnimationListener(listener);
1858        }
1859        return anim;
1860    }
1861
1862    public static String viewInfo(View v) {
1863        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1864                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
1865    }
1866
1867    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1868        synchronized (mQueueLock) {
1869            pw.println("Current Status Bar state:");
1870            pw.println("  mExpanded=" + mExpanded
1871                    + ", mExpandedVisible=" + mExpandedVisible);
1872            pw.println("  mTicking=" + mTicking);
1873            pw.println("  mTracking=" + mTracking);
1874            pw.println("  mAnimating=" + mAnimating
1875                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1876                    + ", mAnimAccel=" + mAnimAccel);
1877            pw.println("  mAnimLastTimeNanos=" + mAnimLastTimeNanos);
1878            pw.println("  mAnimatingReveal=" + mAnimatingReveal
1879                    + " mViewDelta=" + mViewDelta);
1880            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
1881            pw.println("  mPile: " + viewInfo(mPile));
1882            pw.println("  mCloseView: " + viewInfo(mCloseView));
1883            pw.println("  mTickerView: " + viewInfo(mTickerView));
1884            pw.println("  mScrollView: " + viewInfo(mScrollView)
1885                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1886        }
1887
1888        pw.print("  mNavigationBarView=");
1889        if (mNavigationBarView == null) {
1890            pw.println("null");
1891        } else {
1892            mNavigationBarView.dump(fd, pw, args);
1893        }
1894
1895        if (DUMPTRUCK) {
1896            synchronized (mNotificationData) {
1897                int N = mNotificationData.size();
1898                pw.println("  notification icons: " + N);
1899                for (int i=0; i<N; i++) {
1900                    NotificationData.Entry e = mNotificationData.get(i);
1901                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
1902                    StatusBarNotification n = e.notification;
1903                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " score=" + n.score);
1904                    pw.println("         notification=" + n.notification);
1905                    pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
1906                }
1907            }
1908
1909            int N = mStatusIcons.getChildCount();
1910            pw.println("  system icons: " + N);
1911            for (int i=0; i<N; i++) {
1912                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
1913                pw.println("    [" + i + "] icon=" + ic);
1914            }
1915
1916            if (false) {
1917                pw.println("see the logcat for a dump of the views we have created.");
1918                // must happen on ui thread
1919                mHandler.post(new Runnable() {
1920                        public void run() {
1921                            mStatusBarView.getLocationOnScreen(mAbsPos);
1922                            Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1923                                    + ") " + mStatusBarView.getWidth() + "x"
1924                                    + getStatusBarHeight());
1925                            mStatusBarView.debug();
1926                        }
1927                    });
1928            }
1929        }
1930
1931        mNetworkController.dump(fd, pw, args);
1932    }
1933
1934    @Override
1935    public void createAndAddWindows() {
1936        addStatusBarWindow();
1937    }
1938
1939    private void addStatusBarWindow() {
1940        // Put up the view
1941        final int height = getStatusBarHeight();
1942
1943        // Now that the status bar window encompasses the sliding panel and its
1944        // translucent backdrop, the entire thing is made TRANSLUCENT and is
1945        // hardware-accelerated.
1946        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1947                ViewGroup.LayoutParams.MATCH_PARENT,
1948                height,
1949                WindowManager.LayoutParams.TYPE_STATUS_BAR,
1950                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1951                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1952                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1953                PixelFormat.TRANSLUCENT);
1954
1955        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1956
1957        lp.gravity = getStatusBarGravity();
1958        lp.setTitle("StatusBar");
1959        lp.packageName = mContext.getPackageName();
1960
1961        makeStatusBarView();
1962        WindowManagerImpl.getDefault().addView(mStatusBarWindow, lp);
1963    }
1964
1965    void setNotificationIconVisibility(boolean visible, int anim) {
1966        int old = mNotificationIcons.getVisibility();
1967        int v = visible ? View.VISIBLE : View.INVISIBLE;
1968        if (old != v) {
1969            mNotificationIcons.setVisibility(v);
1970            mNotificationIcons.startAnimation(loadAnim(anim, null));
1971        }
1972    }
1973
1974    void updateExpandedInvisiblePosition() {
1975        mTrackingPosition = -mDisplayMetrics.heightPixels;
1976    }
1977
1978    static final float saturate(float a) {
1979        return a < 0f ? 0f : (a > 1f ? 1f : a);
1980    }
1981
1982    @Override
1983    protected int getExpandedViewMaxHeight() {
1984        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
1985    }
1986
1987    @Override
1988    protected void updateExpandedViewPos(int expandedPosition) {
1989        if (SPEW) {
1990            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
1991                    //+ " mTrackingParams.y=" + ((mTrackingParams == null) ? "?" : mTrackingParams.y)
1992                    + " mTrackingPosition=" + mTrackingPosition
1993                    + " gravity=" + mNotificationPanelGravity);
1994        }
1995        int panelh = 0;
1996        final int disph = getExpandedViewMaxHeight();
1997
1998        // If the expanded view is not visible, make sure they're still off screen.
1999        // Maybe the view was resized.
2000        if (!mExpandedVisible) {
2001            updateExpandedInvisiblePosition();
2002            return;
2003        }
2004
2005        // tracking view...
2006        if (expandedPosition == EXPANDED_FULL_OPEN) {
2007            panelh = disph;
2008        }
2009        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
2010            panelh = mTrackingPosition;
2011        }
2012        else {
2013            if (expandedPosition <= disph) {
2014                panelh = expandedPosition;
2015            } else {
2016                panelh = disph;
2017            }
2018        }
2019
2020        // catch orientation changes and other peculiar cases
2021        if (panelh > disph || (panelh < disph && !mTracking && !mAnimating)) {
2022            panelh = disph;
2023        } else if (panelh < 0) {
2024            panelh = 0;
2025        }
2026
2027        if (panelh == mTrackingPosition) return;
2028
2029        mTrackingPosition = panelh;
2030
2031        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2032        lp.height = panelh;
2033        lp.gravity = mNotificationPanelGravity;
2034        lp.leftMargin = mNotificationPanelMarginLeftPx;
2035        if (SPEW) {
2036            Slog.v(TAG, "updated cropView height=" + panelh + " grav=" + lp.gravity);
2037        }
2038        mNotificationPanel.setLayoutParams(lp);
2039
2040        final int barh = getCloseViewHeight() + getStatusBarHeight();
2041        final float frac = saturate((float)(panelh - barh) / (disph - barh));
2042
2043        if (DIM_BEHIND_EXPANDED_PANEL && ActivityManager.isHighEndGfx(mDisplay)) {
2044            // woo, special effects
2045            final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2.2f))));
2046            final int color = ((int)(0xB0 * k)) << 24;
2047            mStatusBarWindow.setBackgroundColor(color);
2048        }
2049
2050        updateCarrierLabelVisibility(false);
2051    }
2052
2053    void updateDisplaySize() {
2054        mDisplay.getMetrics(mDisplayMetrics);
2055    }
2056
2057    void performDisableActions(int net) {
2058        int old = mDisabled;
2059        int diff = net ^ old;
2060        mDisabled = net;
2061
2062        // act accordingly
2063        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
2064            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
2065                Slog.d(TAG, "DISABLE_EXPAND: yes");
2066                animateCollapse();
2067            }
2068        }
2069        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2070            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2071                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
2072                if (mTicking) {
2073                    mNotificationIcons.setVisibility(View.INVISIBLE);
2074                    mTicker.halt();
2075                } else {
2076                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
2077                }
2078            } else {
2079                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
2080                if (!mExpandedVisible) {
2081                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
2082                }
2083            }
2084        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
2085            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
2086                mTicker.halt();
2087            }
2088        }
2089    }
2090
2091    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2092        public void onClick(View v) {
2093            synchronized (mNotificationData) {
2094                // animate-swipe all dismissable notifications, then animate the shade closed
2095                int numChildren = mPile.getChildCount();
2096
2097                int scrollTop = mScrollView.getScrollY();
2098                int scrollBottom = scrollTop + mScrollView.getHeight();
2099                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
2100                for (int i=0; i<numChildren; i++) {
2101                    final View child = mPile.getChildAt(i);
2102                    if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
2103                            child.getTop() < scrollBottom) {
2104                        snapshot.add(child);
2105                    }
2106                }
2107                if (snapshot.isEmpty()) {
2108                    animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
2109                    return;
2110                }
2111                new Thread(new Runnable() {
2112                    @Override
2113                    public void run() {
2114                        // Decrease the delay for every row we animate to give the sense of
2115                        // accelerating the swipes
2116                        final int ROW_DELAY_DECREMENT = 10;
2117                        int currentDelay = 140;
2118                        int totalDelay = 0;
2119
2120                        // Set the shade-animating state to avoid doing other work during
2121                        // all of these animations. In particular, avoid layout and
2122                        // redrawing when collapsing the shade.
2123                        mPile.setViewRemoval(false);
2124
2125                        mPostCollapseCleanup = new Runnable() {
2126                            @Override
2127                            public void run() {
2128                                try {
2129                                    mPile.setViewRemoval(true);
2130                                    mBarService.onClearAllNotifications();
2131                                } catch (Exception ex) { }
2132                            }
2133                        };
2134
2135                        View sampleView = snapshot.get(0);
2136                        int width = sampleView.getWidth();
2137                        final int velocity = width * 8; // 1000/8 = 125 ms duration
2138                        for (final View _v : snapshot) {
2139                            mHandler.postDelayed(new Runnable() {
2140                                @Override
2141                                public void run() {
2142                                    mPile.dismissRowAnimated(_v, velocity);
2143                                }
2144                            }, totalDelay);
2145                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
2146                            totalDelay += currentDelay;
2147                        }
2148                        // Delay the collapse animation until after all swipe animations have
2149                        // finished. Provide some buffer because there may be some extra delay
2150                        // before actually starting each swipe animation. Ideally, we'd
2151                        // synchronize the end of those animations with the start of the collaps
2152                        // exactly.
2153                        mHandler.postDelayed(new Runnable() {
2154                            @Override
2155                            public void run() {
2156                                animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
2157                            }
2158                        }, totalDelay + 225);
2159                    }
2160                }).start();
2161            }
2162        }
2163    };
2164
2165    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
2166        public void onClick(View v) {
2167            // We take this as a good indicator that Setup is running and we shouldn't
2168            // allow you to go somewhere else
2169            if (!isDeviceProvisioned()) return;
2170            try {
2171                // Dismiss the lock screen when Settings starts.
2172                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2173            } catch (RemoteException e) {
2174            }
2175            v.getContext().startActivity(new Intent(Settings.ACTION_SETTINGS)
2176                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
2177            animateCollapse();
2178        }
2179    };
2180
2181    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2182        public void onReceive(Context context, Intent intent) {
2183            String action = intent.getAction();
2184            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
2185                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
2186                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2187                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2188                    String reason = intent.getStringExtra("reason");
2189                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2190                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2191                    }
2192                }
2193                animateCollapse(flags);
2194            }
2195            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
2196                updateResources();
2197                repositionNavigationBar();
2198                updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2199            }
2200        }
2201    };
2202
2203    private void setIntruderAlertVisibility(boolean vis) {
2204        if (!ENABLE_INTRUDERS) return;
2205        if (DEBUG) {
2206            Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
2207        }
2208        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
2209    }
2210
2211    public void dismissIntruder() {
2212        if (mCurrentlyIntrudingNotification == null) return;
2213
2214        try {
2215            mBarService.onNotificationClear(
2216                    mCurrentlyIntrudingNotification.pkg,
2217                    mCurrentlyIntrudingNotification.tag,
2218                    mCurrentlyIntrudingNotification.id);
2219        } catch (android.os.RemoteException ex) {
2220            // oh well
2221        }
2222    }
2223
2224    /**
2225     * Reload some of our resources when the configuration changes.
2226     *
2227     * We don't reload everything when the configuration changes -- we probably
2228     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2229     * meantime, just update the things that we know change.
2230     */
2231    void updateResources() {
2232        final Context context = mContext;
2233        final Resources res = context.getResources();
2234
2235        if (mClearButton instanceof TextView) {
2236            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
2237        }
2238        loadDimens();
2239    }
2240
2241    protected void loadDimens() {
2242        final Resources res = mContext.getResources();
2243
2244        mNaturalBarHeight = res.getDimensionPixelSize(
2245                com.android.internal.R.dimen.status_bar_height);
2246
2247        int newIconSize = res.getDimensionPixelSize(
2248            com.android.internal.R.dimen.status_bar_icon_size);
2249        int newIconHPadding = res.getDimensionPixelSize(
2250            R.dimen.status_bar_icon_padding);
2251
2252        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2253//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2254            mIconHPadding = newIconHPadding;
2255            mIconSize = newIconSize;
2256            //reloadAllNotificationIcons(); // reload the tray
2257        }
2258
2259        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2260
2261        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2262        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2263        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2264        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2265
2266        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2267        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2268
2269        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2270        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2271
2272        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2273
2274        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
2275
2276        mNotificationPanelMarginBottomPx
2277            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
2278        mNotificationPanelMarginLeftPx
2279            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
2280        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
2281        if (mNotificationPanelGravity <= 0) {
2282            mNotificationPanelGravity = Gravity.CENTER_VERTICAL | Gravity.TOP;
2283        }
2284        getNinePatchPadding(res.getDrawable(R.drawable.notification_panel_bg), mNotificationPanelBackgroundPadding);
2285        final int notificationPanelDecorationHeight =
2286              res.getDimensionPixelSize(R.dimen.notification_panel_padding_top)
2287            + res.getDimensionPixelSize(R.dimen.notification_panel_header_height)
2288            + mNotificationPanelBackgroundPadding.top
2289            + mNotificationPanelBackgroundPadding.bottom;
2290        mNotificationPanelMinHeight =
2291              notificationPanelDecorationHeight
2292            + res.getDimensionPixelSize(R.dimen.close_handle_underlap);
2293
2294        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
2295
2296        if (false) Slog.v(TAG, "updateResources");
2297    }
2298
2299    private static void getNinePatchPadding(Drawable d, Rect outPadding) {
2300        if (d instanceof NinePatchDrawable) {
2301            NinePatchDrawable ninePatch = (NinePatchDrawable) d;
2302            ninePatch.getPadding(outPadding);
2303        }
2304    }
2305
2306    //
2307    // tracing
2308    //
2309
2310    void postStartTracing() {
2311        mHandler.postDelayed(mStartTracing, 3000);
2312    }
2313
2314    void vibrate() {
2315        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2316                Context.VIBRATOR_SERVICE);
2317        vib.vibrate(250);
2318    }
2319
2320    Runnable mStartTracing = new Runnable() {
2321        public void run() {
2322            vibrate();
2323            SystemClock.sleep(250);
2324            Slog.d(TAG, "startTracing");
2325            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2326            mHandler.postDelayed(mStopTracing, 10000);
2327        }
2328    };
2329
2330    Runnable mStopTracing = new Runnable() {
2331        public void run() {
2332            android.os.Debug.stopMethodTracing();
2333            Slog.d(TAG, "stopTracing");
2334            vibrate();
2335        }
2336    };
2337
2338    @Override
2339    protected void haltTicker() {
2340        mTicker.halt();
2341    }
2342
2343    @Override
2344    protected boolean shouldDisableNavbarGestures() {
2345        return mExpanded || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
2346    }
2347
2348    private static class FastColorDrawable extends Drawable {
2349        private final int mColor;
2350
2351        public FastColorDrawable(int color) {
2352            mColor = 0xff000000 | color;
2353        }
2354
2355        @Override
2356        public void draw(Canvas canvas) {
2357            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
2358        }
2359
2360        @Override
2361        public void setAlpha(int alpha) {
2362        }
2363
2364        @Override
2365        public void setColorFilter(ColorFilter cf) {
2366        }
2367
2368        @Override
2369        public int getOpacity() {
2370            return PixelFormat.OPAQUE;
2371        }
2372
2373        @Override
2374        public void setBounds(int left, int top, int right, int bottom) {
2375        }
2376
2377        @Override
2378        public void setBounds(Rect bounds) {
2379        }
2380    }
2381}
2382