PhoneStatusBar.java revision 9a0a4c9d7e39e9a91dc35cec3501206914198df6
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
19
20import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
21import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
22import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
23import static android.app.StatusBarManager.windowStateToString;
24import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
25import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
28import static com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
29
30import android.animation.Animator;
31import android.animation.AnimatorListenerAdapter;
32import android.animation.ObjectAnimator;
33import android.animation.TimeInterpolator;
34import android.app.ActivityManager;
35import android.app.ActivityManagerNative;
36import android.app.Notification;
37import android.app.PendingIntent;
38import android.app.StatusBarManager;
39import android.content.BroadcastReceiver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.res.Configuration;
44import android.content.res.Resources;
45import android.database.ContentObserver;
46import android.graphics.Canvas;
47import android.graphics.ColorFilter;
48import android.graphics.PixelFormat;
49import android.graphics.Point;
50import android.graphics.PorterDuff;
51import android.graphics.Rect;
52import android.graphics.drawable.Drawable;
53import android.inputmethodservice.InputMethodService;
54import android.media.AudioManager;
55import android.os.Bundle;
56import android.os.Handler;
57import android.os.IBinder;
58import android.os.Message;
59import android.os.PowerManager;
60import android.os.RemoteException;
61import android.os.SystemClock;
62import android.os.UserHandle;
63import android.provider.Settings;
64import android.service.notification.StatusBarNotification;
65import android.util.DisplayMetrics;
66import android.util.EventLog;
67import android.util.Log;
68import android.view.Display;
69import android.view.Gravity;
70import android.view.MotionEvent;
71import android.view.VelocityTracker;
72import android.view.View;
73import android.view.ViewGroup;
74import android.view.ViewGroup.LayoutParams;
75import android.view.ViewPropertyAnimator;
76import android.view.ViewStub;
77import android.view.WindowManager;
78import android.view.animation.AccelerateInterpolator;
79import android.view.animation.Animation;
80import android.view.animation.AnimationUtils;
81import android.view.animation.DecelerateInterpolator;
82import android.widget.FrameLayout;
83import android.widget.ImageView;
84import android.widget.LinearLayout;
85import android.widget.TextView;
86
87import com.android.internal.statusbar.StatusBarIcon;
88import com.android.keyguard.ViewMediatorCallback;
89import com.android.systemui.DemoMode;
90import com.android.systemui.EventLogTags;
91import com.android.systemui.R;
92import com.android.systemui.keyguard.KeyguardViewMediator;
93import com.android.systemui.statusbar.BaseStatusBar;
94import com.android.systemui.statusbar.CommandQueue;
95import com.android.systemui.statusbar.GestureRecorder;
96import com.android.systemui.statusbar.NotificationData;
97import com.android.systemui.statusbar.NotificationData.Entry;
98import com.android.systemui.statusbar.SignalClusterView;
99import com.android.systemui.statusbar.StatusBarIconView;
100import com.android.systemui.statusbar.policy.BatteryController;
101import com.android.systemui.statusbar.policy.BluetoothController;
102import com.android.systemui.statusbar.policy.DateView;
103import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
104import com.android.systemui.statusbar.policy.LocationController;
105import com.android.systemui.statusbar.policy.NetworkController;
106import com.android.systemui.statusbar.policy.RotationLockController;
107import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
108
109import java.io.FileDescriptor;
110import java.io.PrintWriter;
111import java.util.ArrayList;
112
113public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
114    static final String TAG = "PhoneStatusBar";
115    public static final boolean DEBUG = BaseStatusBar.DEBUG;
116    public static final boolean SPEW = false;
117    public static final boolean DUMPTRUCK = true; // extra dumpsys info
118    public static final boolean DEBUG_GESTURES = false;
119
120    public static final boolean DEBUG_WINDOW_STATE = false;
121
122    public static final boolean SETTINGS_DRAG_SHORTCUT = true;
123
124    // additional instrumentation for testing purposes; intended to be left on during development
125    public static final boolean CHATTY = DEBUG;
126
127    public static final String ACTION_STATUSBAR_START
128            = "com.android.internal.policy.statusbar.START";
129
130    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
131    private static final int MSG_CLOSE_PANELS = 1001;
132    private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
133    // 1020-1030 reserved for BaseStatusBar
134
135    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
136
137    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
138    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
139
140    private static final int STATUS_OR_NAV_TRANSIENT =
141            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
142    private static final long AUTOHIDE_TIMEOUT_MS = 3000;
143
144    // fling gesture tuning parameters, scaled to display density
145    private float mSelfExpandVelocityPx; // classic value: 2000px/s
146    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
147    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
148    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
149    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
150    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
151    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
152
153    private float mExpandAccelPx; // classic value: 2000px/s/s
154    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
155
156    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
157                                                    // faster than mSelfCollapseVelocityPx)
158
159    PhoneStatusBarPolicy mIconPolicy;
160
161    // These are no longer handled by the policy, because we need custom strategies for them
162    BluetoothController mBluetoothController;
163    BatteryController mBatteryController;
164    LocationController mLocationController;
165    NetworkController mNetworkController;
166    RotationLockController mRotationLockController;
167
168    int mNaturalBarHeight = -1;
169    int mIconSize = -1;
170    int mIconHPadding = -1;
171    Display mDisplay;
172    Point mCurrentDisplaySize = new Point();
173    private float mHeadsUpVerticalOffset;
174    private int[] mStackScrollerPosition = new int[2];
175
176    StatusBarWindowView mStatusBarWindow;
177    PhoneStatusBarView mStatusBarView;
178    private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
179    private StatusBarWindowManager mStatusBarWindowManager;
180
181    int mPixelFormat;
182    Object mQueueLock = new Object();
183
184    // viewgroup containing the normal contents of the statusbar
185    LinearLayout mStatusBarContents;
186
187    // right-hand icons
188    LinearLayout mSystemIconArea;
189
190    // left-hand icons
191    LinearLayout mStatusIcons;
192    // the icons themselves
193    IconMerger mNotificationIcons;
194    // [+>
195    View mMoreIcon;
196    // mode indicator icon
197    ImageView mModeIcon;
198
199    // expanded notifications
200    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
201    View mExpandedContents;
202    int mNotificationPanelGravity;
203    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
204    float mNotificationPanelMinHeightFrac;
205    boolean mNotificationPanelIsFullScreenWidth;
206    TextView mNotificationPanelDebugText;
207
208    // settings
209    QuickSettings mQS;
210    boolean mHasSettingsPanel, mHasFlipSettings;
211    SettingsPanelView mSettingsPanel;
212    View mFlipSettingsView;
213    QuickSettingsContainerView mSettingsContainer;
214    int mSettingsPanelGravity;
215
216    // top bar
217    View mNotificationPanelHeader;
218    View mKeyguardStatusView;
219    View mDateTimeView;
220    View mClearButton;
221    ImageView mSettingsButton, mNotificationButton;
222    View mKeyguardSettingsFlipButton;
223
224    // carrier/wifi label
225    private TextView mCarrierLabel;
226    private boolean mCarrierLabelVisible = false;
227    private int mCarrierLabelHeight;
228    private TextView mEmergencyCallLabel;
229    private int mNotificationHeaderHeight;
230
231    private boolean mShowCarrierInPanel = false;
232
233    // position
234    int[] mPositionTmp = new int[2];
235    boolean mExpandedVisible;
236
237    // the date view
238    DateView mDateView;
239
240    // for heads up notifications
241    private HeadsUpNotificationView mHeadsUpNotificationView;
242    private int mHeadsUpNotificationDecay;
243
244    // on-screen navigation buttons
245    private NavigationBarView mNavigationBarView = null;
246    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
247
248    // the tracker view
249    int mTrackingPosition; // the position of the top of the tracking view.
250
251    // ticker
252    private Ticker mTicker;
253    private View mTickerView;
254    private boolean mTicking;
255
256    // Tracking finger for opening/closing.
257    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
258    boolean mTracking;
259    VelocityTracker mVelocityTracker;
260
261    int[] mAbsPos = new int[2];
262    Runnable mPostCollapseCleanup = null;
263
264    // for disabling the status bar
265    int mDisabled = 0;
266
267    // tracking calls to View.setSystemUiVisibility()
268    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
269
270    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
271
272    // XXX: gesture research
273    private final GestureRecorder mGestureRec = DEBUG_GESTURES
274        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
275        : null;
276
277    private int mNavigationIconHints = 0;
278    private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
279        @Override
280        public void onAnimationEnd(Animator animation) {
281            // double-check to avoid races
282            if (mStatusBarContents.getAlpha() == 0) {
283                if (DEBUG) Log.d(TAG, "makeIconsInvisible");
284                mStatusBarContents.setVisibility(View.INVISIBLE);
285            }
286        }
287    };
288
289    // ensure quick settings is disabled until the current user makes it through the setup wizard
290    private boolean mUserSetup = false;
291    private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
292        @Override
293        public void onChange(boolean selfChange) {
294            final boolean userSetup = 0 != Settings.Secure.getIntForUser(
295                    mContext.getContentResolver(),
296                    Settings.Secure.USER_SETUP_COMPLETE,
297                    0 /*default */,
298                    mCurrentUserId);
299            if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
300                    "selfChange=%s userSetup=%s mUserSetup=%s",
301                    selfChange, userSetup, mUserSetup));
302            if (mSettingsButton != null && mHasFlipSettings) {
303                mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE);
304            }
305            if (mSettingsPanel != null) {
306                mSettingsPanel.setEnabled(userSetup);
307            }
308            if (userSetup != mUserSetup) {
309                mUserSetup = userSetup;
310                if (!mUserSetup && mStatusBarView != null)
311                    animateCollapseQuickSettings();
312            }
313        }
314    };
315
316    final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
317        @Override
318        public void onChange(boolean selfChange) {
319            boolean wasUsing = mUseHeadsUp;
320            mUseHeadsUp = ENABLE_HEADS_UP && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
321                    mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
322                    Settings.Global.HEADS_UP_OFF);
323            mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
324                    mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
325            Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
326            if (wasUsing != mUseHeadsUp) {
327                if (!mUseHeadsUp) {
328                    Log.d(TAG, "dismissing any existing heads up notification on disable event");
329                    setHeadsUpVisibility(false);
330                    mHeadsUpNotificationView.setNotification(null);
331                    removeHeadsUpView();
332                } else {
333                    addHeadsUpView();
334                }
335            }
336        }
337    };
338
339    private int mInteractingWindows;
340    private boolean mAutohideSuspended;
341    private int mStatusBarMode;
342    private int mNavigationBarMode;
343    private Boolean mScreenOn;
344
345    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
346    private ViewMediatorCallback mKeyguardViewMediatorCallback;
347
348    private final Runnable mAutohide = new Runnable() {
349        @Override
350        public void run() {
351            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
352            if (mSystemUiVisibility != requested) {
353                notifyUiVisibilityChanged(requested);
354            }
355        }};
356
357    private Runnable mOnFlipRunnable;
358
359    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
360            new OnChildLocationsChangedListener() {
361        @Override
362        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
363            userActivity();
364        }
365    };
366
367    public void setOnFlipRunnable(Runnable onFlipRunnable) {
368        mOnFlipRunnable = onFlipRunnable;
369    }
370
371    @Override
372    public void setZenMode(int mode) {
373        super.setZenMode(mode);
374        if (mModeIcon == null) return;
375        if (!isDeviceProvisioned()) return;
376        mModeIcon.setVisibility(mode != Settings.Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE);
377    }
378
379    @Override
380    public void start() {
381        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
382                .getDefaultDisplay();
383        updateDisplaySize();
384
385        super.start(); // calls createAndAddWindows()
386
387        addNavigationBar();
388
389        // Lastly, call to the icon policy to install/update all the icons.
390        mIconPolicy = new PhoneStatusBarPolicy(mContext);
391        mSettingsObserver.onChange(false); // set up
392
393        mHeadsUpObserver.onChange(true); // set up
394        if (ENABLE_HEADS_UP) {
395            mContext.getContentResolver().registerContentObserver(
396                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
397                    mHeadsUpObserver);
398            mContext.getContentResolver().registerContentObserver(
399                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
400                    mHeadsUpObserver);
401        }
402        startKeyguard();
403    }
404
405    // ================================================================================
406    // Constructing the view
407    // ================================================================================
408    protected PhoneStatusBarView makeStatusBarView() {
409        final Context context = mContext;
410
411        Resources res = context.getResources();
412
413        updateDisplaySize(); // populates mDisplayMetrics
414        loadDimens();
415
416        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
417
418        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
419                R.layout.super_status_bar, null);
420        mStatusBarWindow.mService = this;
421        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
422            @Override
423            public boolean onTouch(View v, MotionEvent event) {
424                checkUserAutohide(v, event);
425                if (event.getAction() == MotionEvent.ACTION_DOWN) {
426                    if (mExpandedVisible) {
427                        animateCollapsePanels();
428                    }
429                }
430                return mStatusBarWindow.onTouchEvent(event);
431            }});
432
433        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
434        mStatusBarView.setBar(this);
435
436        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
437        mStatusBarView.setPanelHolder(holder);
438
439        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
440                R.id.notification_panel);
441        mNotificationPanel.setStatusBar(this);
442        mNotificationPanelIsFullScreenWidth =
443            (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
444
445        // make the header non-responsive to clicks
446        mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
447                new View.OnTouchListener() {
448                    @Override
449                    public boolean onTouch(View v, MotionEvent event) {
450                        return true; // e eats everything
451                    }
452                });
453
454        if (!ActivityManager.isHighEndGfx()) {
455            mStatusBarWindow.setBackground(null);
456            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
457                    R.color.notification_panel_solid_background)));
458        }
459        if (ENABLE_HEADS_UP) {
460            mHeadsUpNotificationView =
461                    (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
462            mHeadsUpNotificationView.setVisibility(View.GONE);
463            mHeadsUpNotificationView.setBar(this);
464        }
465        if (MULTIUSER_DEBUG) {
466            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
467                    R.id.header_debug_info);
468            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
469        }
470
471        updateShowSearchHoldoff();
472
473        try {
474            boolean showNav = mWindowManagerService.hasNavigationBar();
475            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
476            if (showNav) {
477                mNavigationBarView =
478                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
479
480                mNavigationBarView.setDisabledFlags(mDisabled);
481                mNavigationBarView.setBar(this);
482                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
483                    @Override
484                    public boolean onTouch(View v, MotionEvent event) {
485                        checkUserAutohide(v, event);
486                        return false;
487                    }});
488            }
489        } catch (RemoteException ex) {
490            // no window manager? good luck with that
491        }
492
493        // figure out which pixel-format to use for the status bar.
494        mPixelFormat = PixelFormat.OPAQUE;
495
496        mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
497        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
498        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
499        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
500        mNotificationIcons.setOverflowIndicator(mMoreIcon);
501        mModeIcon = (ImageView)mStatusBarView.findViewById(R.id.modeIcon);
502        mModeIcon.setImageResource(R.drawable.stat_sys_zen_limited);
503        mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
504        mTickerView = mStatusBarView.findViewById(R.id.ticker);
505
506        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
507                R.id.notification_stack_scroller);
508        mStackScroller.setLongPressListener(getNotificationLongClicker());
509        mStackScroller.setChildLocationsChangedListener(mOnChildLocationsChangedListener);
510
511        mExpandedContents = mStackScroller;
512
513        mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
514        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
515
516        mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
517        mClearButton.setOnClickListener(mClearButtonListener);
518        mClearButton.setAlpha(0f);
519        mClearButton.setVisibility(View.INVISIBLE);
520        mClearButton.setEnabled(false);
521        mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
522
523        mHasSettingsPanel = res.getBoolean(R.bool.config_hasSettingsPanel);
524        mHasFlipSettings = res.getBoolean(R.bool.config_hasFlipSettingsPanel);
525
526        mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
527        if (mDateTimeView != null) {
528            mDateTimeView.setOnClickListener(mClockClickListener);
529            mDateTimeView.setEnabled(true);
530        }
531
532        mSettingsButton = (ImageView) mStatusBarWindow.findViewById(R.id.settings_button);
533        if (mSettingsButton != null) {
534            mSettingsButton.setOnClickListener(mSettingsButtonListener);
535            if (mHasSettingsPanel) {
536                if (mStatusBarView.hasFullWidthNotifications()) {
537                    // the settings panel is hiding behind this button
538                    mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
539                    mSettingsButton.setVisibility(View.VISIBLE);
540                } else {
541                    // there is a settings panel, but it's on the other side of the (large) screen
542                    final View buttonHolder = mStatusBarWindow.findViewById(
543                            R.id.settings_button_holder);
544                    if (buttonHolder != null) {
545                        buttonHolder.setVisibility(View.GONE);
546                    }
547                }
548            } else {
549                // no settings panel, go straight to settings
550                mSettingsButton.setVisibility(View.VISIBLE);
551                mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
552            }
553        }
554        if (mHasFlipSettings) {
555            mNotificationButton = (ImageView) mStatusBarWindow.findViewById(
556                    R.id.notification_button);
557            if (mNotificationButton != null) {
558                mNotificationButton.setOnClickListener(mNotificationButtonListener);
559            }
560        }
561
562        if (!mNotificationPanelIsFullScreenWidth) {
563            mNotificationPanel.setSystemUiVisibility(
564                    View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
565                    View.STATUS_BAR_DISABLE_CLOCK);
566        }
567
568        mTicker = new MyTicker(context, mStatusBarView);
569
570        TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
571        tickerView.mTicker = mTicker;
572
573        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
574
575        // set the inital view visibility
576        setAreThereNotifications();
577
578        // Other icons
579        mLocationController = new LocationController(mContext); // will post a notification
580        mBatteryController = new BatteryController(mContext);
581        mNetworkController = new NetworkController(mContext);
582        mBluetoothController = new BluetoothController(mContext);
583        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)
584                || QuickSettings.DEBUG_GONE_TILES) {
585            mRotationLockController = new RotationLockController(mContext);
586        }
587        final SignalClusterView signalCluster =
588                (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
589
590
591        mNetworkController.addSignalCluster(signalCluster);
592        signalCluster.setNetworkController(mNetworkController);
593
594        final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
595        if (isAPhone) {
596            mEmergencyCallLabel =
597                    (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only);
598            // TODO: Uncomment when correctly positioned
599//            if (mEmergencyCallLabel != null) {
600//                mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
601//                mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
602//                    public void onClick(View v) { }});
603//                mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
604//                    @Override
605//                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
606//                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
607//                        updateCarrierLabelVisibility(false);
608//                    }});
609//            }
610        }
611
612        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
613        mShowCarrierInPanel = (mCarrierLabel != null);
614        if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
615        if (mShowCarrierInPanel) {
616            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
617
618            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
619            // for other devices, we show whatever network is connected
620            if (mNetworkController.hasMobileDataFeature()) {
621                mNetworkController.addMobileLabelView(mCarrierLabel);
622            } else {
623                mNetworkController.addCombinedLabelView(mCarrierLabel);
624            }
625
626            // set up the dynamic hide/show of the label
627            // TODO: uncomment, handle this for the Stack scroller aswell
628//                ((NotificationRowLayout) mStackScroller)
629// .setOnSizeChangedListener(new OnSizeChangedListener() {
630//                @Override
631//                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
632//                    updateCarrierLabelVisibility(false);
633        }
634
635        // Quick Settings (where available, some restrictions apply)
636        if (mHasSettingsPanel) {
637            // first, figure out where quick settings should be inflated
638            final View settings_stub;
639            if (mHasFlipSettings) {
640                // a version of quick settings that flips around behind the notifications
641                settings_stub = mStatusBarWindow.findViewById(R.id.flip_settings_stub);
642                if (settings_stub != null) {
643                    mFlipSettingsView = ((ViewStub)settings_stub).inflate();
644                    mFlipSettingsView.setVisibility(View.GONE);
645                    mFlipSettingsView.setVerticalScrollBarEnabled(false);
646                }
647            } else {
648                // full quick settings panel
649                settings_stub = mStatusBarWindow.findViewById(R.id.quick_settings_stub);
650                if (settings_stub != null) {
651                    mSettingsPanel = (SettingsPanelView) ((ViewStub)settings_stub).inflate();
652                } else {
653                    mSettingsPanel = (SettingsPanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
654                }
655
656                if (mSettingsPanel != null) {
657                    if (!ActivityManager.isHighEndGfx()) {
658                        mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
659                                R.color.notification_panel_solid_background)));
660                    }
661                }
662            }
663
664            // wherever you find it, Quick Settings needs a container to survive
665            mSettingsContainer = (QuickSettingsContainerView)
666                    mStatusBarWindow.findViewById(R.id.quick_settings_container);
667            if (mSettingsContainer != null) {
668                mQS = new QuickSettings(mContext, mSettingsContainer);
669                if (!mNotificationPanelIsFullScreenWidth) {
670                    mSettingsContainer.setSystemUiVisibility(
671                            View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
672                            | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
673                }
674                if (mSettingsPanel != null) {
675                    mSettingsPanel.setQuickSettings(mQS);
676                }
677                mQS.setService(this);
678                mQS.setBar(mStatusBarView);
679                mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
680                        mLocationController, mRotationLockController);
681            } else {
682                mQS = null; // fly away, be free
683            }
684        }
685
686        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
687        mBroadcastReceiver.onReceive(mContext,
688                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
689
690        // receive broadcasts
691        IntentFilter filter = new IntentFilter();
692        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
693        filter.addAction(Intent.ACTION_SCREEN_OFF);
694        filter.addAction(Intent.ACTION_SCREEN_ON);
695        filter.addAction(ACTION_DEMO);
696        context.registerReceiver(mBroadcastReceiver, filter);
697
698        // listen for USER_SETUP_COMPLETE setting (per-user)
699        resetUserSetupObserver();
700
701        return mStatusBarView;
702    }
703
704    private void startKeyguard() {
705        KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
706        mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
707                mStatusBarWindow, mStatusBarWindowManager);
708        mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
709    }
710
711    @Override
712    protected void onShowSearchPanel() {
713        if (mNavigationBarView != null) {
714            mNavigationBarView.getBarTransitions().setContentVisible(false);
715        }
716    }
717
718    @Override
719    protected void onHideSearchPanel() {
720        if (mNavigationBarView != null) {
721            mNavigationBarView.getBarTransitions().setContentVisible(true);
722        }
723    }
724
725    @Override
726    protected View getStatusBarView() {
727        return mStatusBarView;
728    }
729
730    @Override
731    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
732        boolean opaque = false;
733        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
734                LayoutParams.MATCH_PARENT,
735                LayoutParams.MATCH_PARENT,
736                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
737                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
738                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
739                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
740                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
741        if (ActivityManager.isHighEndGfx()) {
742            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
743        }
744        lp.gravity = Gravity.BOTTOM | Gravity.START;
745        lp.setTitle("SearchPanel");
746        // TODO: Define custom animation for Search panel
747        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
748        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
749        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
750        return lp;
751    }
752
753    @Override
754    protected void updateSearchPanel() {
755        super.updateSearchPanel();
756        if (mNavigationBarView != null) {
757            mNavigationBarView.setDelegateView(mSearchPanelView);
758        }
759    }
760
761    @Override
762    public void showSearchPanel() {
763        super.showSearchPanel();
764        mHandler.removeCallbacks(mShowSearchPanel);
765
766        // we want to freeze the sysui state wherever it is
767        mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
768
769        if (mNavigationBarView != null) {
770            WindowManager.LayoutParams lp =
771                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
772            lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
773            mWindowManager.updateViewLayout(mNavigationBarView, lp);
774        }
775    }
776
777    @Override
778    public void hideSearchPanel() {
779        super.hideSearchPanel();
780        if (mNavigationBarView != null) {
781            WindowManager.LayoutParams lp =
782                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
783            lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
784            mWindowManager.updateViewLayout(mNavigationBarView, lp);
785        }
786    }
787
788    public int getStatusBarHeight() {
789        if (mNaturalBarHeight < 0) {
790            final Resources res = mContext.getResources();
791            mNaturalBarHeight =
792                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
793        }
794        return mNaturalBarHeight;
795    }
796
797    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
798        public void onClick(View v) {
799            awakenDreams();
800            toggleRecentApps();
801        }
802    };
803
804    private int mShowSearchHoldoff = 0;
805    private Runnable mShowSearchPanel = new Runnable() {
806        public void run() {
807            showSearchPanel();
808            awakenDreams();
809        }
810    };
811
812    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
813        public boolean onTouch(View v, MotionEvent event) {
814            switch(event.getAction()) {
815            case MotionEvent.ACTION_DOWN:
816                if (!shouldDisableNavbarGestures()) {
817                    mHandler.removeCallbacks(mShowSearchPanel);
818                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
819                }
820            break;
821
822            case MotionEvent.ACTION_UP:
823            case MotionEvent.ACTION_CANCEL:
824                mHandler.removeCallbacks(mShowSearchPanel);
825                awakenDreams();
826            break;
827        }
828        return false;
829        }
830    };
831
832    private void awakenDreams() {
833        if (mDreamManager != null) {
834            try {
835                mDreamManager.awaken();
836            } catch (RemoteException e) {
837                // fine, stay asleep then
838            }
839        }
840    }
841
842    private void prepareNavigationBarView() {
843        mNavigationBarView.reorient();
844
845        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
846        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
847        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
848        mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
849        updateSearchPanel();
850    }
851
852    // For small-screen devices (read: phones) that lack hardware navigation buttons
853    private void addNavigationBar() {
854        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
855        if (mNavigationBarView == null) return;
856
857        prepareNavigationBarView();
858
859        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
860    }
861
862    private void repositionNavigationBar() {
863        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
864
865        prepareNavigationBarView();
866
867        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
868    }
869
870    private void notifyNavigationBarScreenOn(boolean screenOn) {
871        if (mNavigationBarView == null) return;
872        mNavigationBarView.notifyScreenOn(screenOn);
873    }
874
875    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
876        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
877                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
878                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
879                    0
880                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
881                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
882                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
883                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
884                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
885                PixelFormat.TRANSLUCENT);
886        // this will allow the navbar to run in an overlay on devices that support this
887        if (ActivityManager.isHighEndGfx()) {
888            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
889        }
890
891        lp.setTitle("NavigationBar");
892        lp.windowAnimations = 0;
893        return lp;
894    }
895
896    private void addHeadsUpView() {
897        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
898                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
899                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
900                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
901                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
902                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
903                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
904                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
905                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
906                PixelFormat.TRANSLUCENT);
907        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
908        lp.gravity = Gravity.TOP;
909        lp.setTitle("Heads Up");
910        lp.packageName = mContext.getPackageName();
911        lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
912
913        mWindowManager.addView(mHeadsUpNotificationView, lp);
914    }
915
916    private void removeHeadsUpView() {
917        mWindowManager.removeView(mHeadsUpNotificationView);
918    }
919
920    public void refreshAllStatusBarIcons() {
921        refreshAllIconsForLayout(mStatusIcons);
922        refreshAllIconsForLayout(mNotificationIcons);
923    }
924
925    private void refreshAllIconsForLayout(LinearLayout ll) {
926        final int count = ll.getChildCount();
927        for (int n = 0; n < count; n++) {
928            View child = ll.getChildAt(n);
929            if (child instanceof StatusBarIconView) {
930                ((StatusBarIconView) child).updateDrawable();
931            }
932        }
933    }
934
935    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
936        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
937                + " icon=" + icon);
938        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
939        view.set(icon);
940        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
941    }
942
943    public void updateIcon(String slot, int index, int viewIndex,
944            StatusBarIcon old, StatusBarIcon icon) {
945        if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
946                + " old=" + old + " icon=" + icon);
947        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
948        view.set(icon);
949    }
950
951    public void removeIcon(String slot, int index, int viewIndex) {
952        if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
953        mStatusIcons.removeViewAt(viewIndex);
954    }
955
956    public void addNotification(IBinder key, StatusBarNotification notification) {
957        if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
958        Entry shadeEntry = createNotificationViews(key, notification);
959        if (shadeEntry == null) {
960            return;
961        }
962        if (!shouldIntercept(notification.getNotification())) {
963            if (mUseHeadsUp && shouldInterrupt(notification)) {
964                if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
965                Entry interruptionCandidate = new Entry(key, notification, null);
966                ViewGroup holder = mHeadsUpNotificationView.getHolder();
967                if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
968                    mInterruptingNotificationTime = System.currentTimeMillis();
969                    mInterruptingNotificationEntry = interruptionCandidate;
970                    shadeEntry.setInterruption();
971
972                    // 1. Populate mHeadsUpNotificationView
973                    mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
974
975                    // 2. Animate mHeadsUpNotificationView in
976                    mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
977
978                    // 3. Set alarm to age the notification off
979                    resetHeadsUpDecayTimer();
980                }
981            } else if (notification.getNotification().fullScreenIntent != null) {
982                // Stop screensaver if the notification has a full-screen intent.
983                // (like an incoming phone call)
984                awakenDreams();
985
986                // not immersive & a full-screen alert should be shown
987                if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
988                try {
989                    notification.getNotification().fullScreenIntent.send();
990                } catch (PendingIntent.CanceledException e) {
991                }
992            } else {
993                // usual case: status bar visible & not immersive
994
995                // show the ticker if there isn't already a heads up
996                if (mInterruptingNotificationEntry == null) {
997                    tick(null, notification, true);
998                }
999            }
1000        }
1001        addNotificationViews(shadeEntry);
1002        // Recalculate the position of the sliding windows and the titles.
1003        setAreThereNotifications();
1004        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1005    }
1006
1007    @Override
1008    public void resetHeadsUpDecayTimer() {
1009        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
1010        if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
1011                && mHeadsUpNotificationView.isClearable()) {
1012            mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay);
1013        }
1014    }
1015
1016    public void removeNotification(IBinder key) {
1017        StatusBarNotification old = removeNotificationViews(key);
1018        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1019
1020        if (old != null) {
1021            // Cancel the ticker if it's still running
1022            mTicker.removeEntry(old);
1023
1024            // Recalculate the position of the sliding windows and the titles.
1025            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1026
1027            if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
1028                    && old == mInterruptingNotificationEntry.notification) {
1029                mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1030            }
1031
1032            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0
1033                    && !mNotificationPanel.isTracking()) {
1034                animateCollapsePanels();
1035            }
1036        }
1037
1038        setAreThereNotifications();
1039    }
1040
1041    @Override
1042    protected void refreshLayout(int layoutDirection) {
1043        if (mNavigationBarView != null) {
1044            mNavigationBarView.setLayoutDirection(layoutDirection);
1045        }
1046
1047        if (mClearButton != null && mClearButton instanceof ImageView) {
1048            // Force asset reloading
1049            ((ImageView)mClearButton).setImageDrawable(null);
1050            ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear);
1051        }
1052
1053        if (mSettingsButton != null) {
1054            // Force asset reloading
1055            mSettingsButton.setImageDrawable(null);
1056            mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
1057        }
1058
1059        if (mNotificationButton != null) {
1060            // Force asset reloading
1061            mNotificationButton.setImageDrawable(null);
1062            mNotificationButton.setImageResource(R.drawable.ic_notifications);
1063        }
1064
1065        refreshAllStatusBarIcons();
1066    }
1067
1068    private void updateShowSearchHoldoff() {
1069        mShowSearchHoldoff = mContext.getResources().getInteger(
1070            R.integer.config_show_search_delay);
1071    }
1072
1073    private void loadNotificationShade() {
1074        if (mStackScroller == null) return;
1075
1076        int N = mNotificationData.size();
1077
1078        ArrayList<View> toShow = new ArrayList<View>();
1079
1080        final boolean provisioned = isDeviceProvisioned();
1081        // If the device hasn't been through Setup, we only show system notifications
1082        for (int i=0; i<N; i++) {
1083            Entry ent = mNotificationData.get(N-i-1);
1084            if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
1085
1086            // TODO How do we want to badge notifcations from profiles.
1087            if (!notificationIsForCurrentProfiles(ent.notification)) continue;
1088
1089            final int vis = ent.notification.getNotification().visibility;
1090            if (vis != Notification.VISIBILITY_SECRET) {
1091                // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications
1092                ent.row.setShowingPublic(isLockscreenPublicMode()
1093                        && vis == Notification.VISIBILITY_PRIVATE
1094                        && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId()));
1095                toShow.add(ent.row);
1096            }
1097        }
1098
1099        ArrayList<View> toRemove = new ArrayList<View>();
1100        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1101            View child = mStackScroller.getChildAt(i);
1102            if (!toShow.contains(child)) {
1103                toRemove.add(child);
1104            }
1105        }
1106
1107        for (View remove : toRemove) {
1108            mStackScroller.removeView(remove);
1109        }
1110
1111        for (int i=0; i<toShow.size(); i++) {
1112            View v = toShow.get(i);
1113            if (v.getParent() == null) {
1114                mStackScroller.addView(v, i);
1115            }
1116        }
1117
1118        if (mSettingsButton != null) {
1119            mSettingsButton.setEnabled(isDeviceProvisioned());
1120        }
1121    }
1122
1123    @Override
1124    protected void updateNotificationIcons() {
1125        if (mNotificationIcons == null) return;
1126
1127        loadNotificationShade();
1128
1129        final LinearLayout.LayoutParams params
1130            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1131
1132        int N = mNotificationData.size();
1133
1134        if (DEBUG) {
1135            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
1136        }
1137
1138        ArrayList<View> toShow = new ArrayList<View>();
1139
1140        final boolean provisioned = isDeviceProvisioned();
1141        // If the device hasn't been through Setup, we only show system notifications
1142        for (int i=0; i<N; i++) {
1143            Entry ent = mNotificationData.get(N-i-1);
1144            if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
1145                    || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
1146            if (!notificationIsForCurrentProfiles(ent.notification)) continue;
1147            if (isLockscreenPublicMode()
1148                    && ent.notification.getNotification().visibility
1149                            == Notification.VISIBILITY_SECRET
1150                    && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())) {
1151                // in "public" mode (atop a secure keyguard), secret notifs are totally hidden
1152                continue;
1153            }
1154            if (shouldIntercept(ent.notification.getNotification())) {
1155                continue;
1156            }
1157            toShow.add(ent.icon);
1158        }
1159
1160        ArrayList<View> toRemove = new ArrayList<View>();
1161        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1162            View child = mNotificationIcons.getChildAt(i);
1163            if (!toShow.contains(child)) {
1164                toRemove.add(child);
1165            }
1166        }
1167
1168        for (View remove : toRemove) {
1169            mNotificationIcons.removeView(remove);
1170        }
1171
1172        for (int i=0; i<toShow.size(); i++) {
1173            View v = toShow.get(i);
1174            if (v.getParent() == null) {
1175                mNotificationIcons.addView(v, i, params);
1176            }
1177        }
1178    }
1179
1180    protected void updateCarrierLabelVisibility(boolean force) {
1181        // TODO: Handle this for the notification stack scroller as well
1182        if (!mShowCarrierInPanel) return;
1183        // The idea here is to only show the carrier label when there is enough room to see it,
1184        // i.e. when there aren't enough notifications to fill the panel.
1185        if (SPEW) {
1186            Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
1187                    mStackScroller.getHeight(), mStackScroller.getHeight(),
1188                    mCarrierLabelHeight));
1189        }
1190
1191        final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
1192        final boolean makeVisible =
1193            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1194            && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
1195                    - mCarrierLabelHeight - mNotificationHeaderHeight)
1196            && mStackScroller.getVisibility() == View.VISIBLE;
1197
1198        if (force || mCarrierLabelVisible != makeVisible) {
1199            mCarrierLabelVisible = makeVisible;
1200            if (DEBUG) {
1201                Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1202            }
1203            mCarrierLabel.animate().cancel();
1204            if (makeVisible) {
1205                mCarrierLabel.setVisibility(View.VISIBLE);
1206            }
1207            mCarrierLabel.animate()
1208                .alpha(makeVisible ? 1f : 0f)
1209                //.setStartDelay(makeVisible ? 500 : 0)
1210                //.setDuration(makeVisible ? 750 : 100)
1211                .setDuration(150)
1212                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1213                    @Override
1214                    public void onAnimationEnd(Animator animation) {
1215                        if (!mCarrierLabelVisible) { // race
1216                            mCarrierLabel.setVisibility(View.INVISIBLE);
1217                            mCarrierLabel.setAlpha(0f);
1218                        }
1219                    }
1220                })
1221                .start();
1222        }
1223    }
1224
1225    @Override
1226    protected void setAreThereNotifications() {
1227        final boolean any = mNotificationData.size() > 0;
1228
1229        final boolean clearable = any && mNotificationData.hasClearableItems();
1230
1231        if (SPEW) {
1232            Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
1233                    + " any=" + any + " clearable=" + clearable);
1234        }
1235
1236        if (mHasFlipSettings
1237                && mFlipSettingsView != null
1238                && mFlipSettingsView.getVisibility() == View.VISIBLE
1239                && mStackScroller.getVisibility() != View.VISIBLE) {
1240            // the flip settings panel is unequivocally showing; we should not be shown
1241            mClearButton.setVisibility(View.INVISIBLE);
1242        } else if (mClearButton.isShown()) {
1243            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
1244                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
1245                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
1246                clearAnimation.addListener(new AnimatorListenerAdapter() {
1247                    @Override
1248                    public void onAnimationEnd(Animator animation) {
1249                        if (mClearButton.getAlpha() <= 0.0f) {
1250                            mClearButton.setVisibility(View.INVISIBLE);
1251                        }
1252                    }
1253
1254                    @Override
1255                    public void onAnimationStart(Animator animation) {
1256                        if (mClearButton.getAlpha() <= 0.0f) {
1257                            mClearButton.setVisibility(View.VISIBLE);
1258                        }
1259                    }
1260                });
1261                clearAnimation.start();
1262            }
1263        } else {
1264            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
1265            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
1266        }
1267        mClearButton.setEnabled(clearable);
1268
1269        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1270        final boolean showDot = (any&&!areLightsOn());
1271        if (showDot != (nlo.getAlpha() == 1.0f)) {
1272            if (showDot) {
1273                nlo.setAlpha(0f);
1274                nlo.setVisibility(View.VISIBLE);
1275            }
1276            nlo.animate()
1277                .alpha(showDot?1:0)
1278                .setDuration(showDot?750:250)
1279                .setInterpolator(new AccelerateInterpolator(2.0f))
1280                .setListener(showDot ? null : new AnimatorListenerAdapter() {
1281                    @Override
1282                    public void onAnimationEnd(Animator _a) {
1283                        nlo.setVisibility(View.GONE);
1284                    }
1285                })
1286                .start();
1287        }
1288
1289        updateCarrierLabelVisibility(false);
1290    }
1291
1292    public void showClock(boolean show) {
1293        if (mStatusBarView == null) return;
1294        View clock = mStatusBarView.findViewById(R.id.clock);
1295        if (clock != null) {
1296            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1297        }
1298    }
1299
1300    /**
1301     * State is one or more of the DISABLE constants from StatusBarManager.
1302     */
1303    public void disable(int state) {
1304        final int old = mDisabled;
1305        final int diff = state ^ old;
1306        mDisabled = state;
1307
1308        if (DEBUG) {
1309            Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1310                old, state, diff));
1311        }
1312
1313        StringBuilder flagdbg = new StringBuilder();
1314        flagdbg.append("disable: < ");
1315        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1316        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1317        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1318        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1319        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1320        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1321        flagdbg.append(((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) ? "PRIVATE" : "private");
1322        flagdbg.append(((diff  & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) ? "* " : " ");
1323        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1324        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1325        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1326        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1327        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1328        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1329        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1330        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1331        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1332        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1333        flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
1334        flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
1335        flagdbg.append(">");
1336        Log.d(TAG, flagdbg.toString());
1337
1338        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1339            mSystemIconArea.animate().cancel();
1340            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1341                mSystemIconArea.animate()
1342                    .alpha(0f)
1343                    .translationY(mNaturalBarHeight*0.5f)
1344                    .setDuration(175)
1345                    .setInterpolator(new DecelerateInterpolator(1.5f))
1346                    .setListener(mMakeIconsInvisible)
1347                    .start();
1348            } else {
1349                mSystemIconArea.setVisibility(View.VISIBLE);
1350                mSystemIconArea.animate()
1351                    .alpha(1f)
1352                    .translationY(0)
1353                    .setStartDelay(0)
1354                    .setInterpolator(new DecelerateInterpolator(1.5f))
1355                    .setDuration(175)
1356                    .start();
1357            }
1358        }
1359
1360        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1361            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1362            showClock(show);
1363        }
1364        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1365            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1366                animateCollapsePanels();
1367            }
1368        }
1369
1370        if ((diff & (StatusBarManager.DISABLE_HOME
1371                        | StatusBarManager.DISABLE_RECENT
1372                        | StatusBarManager.DISABLE_BACK
1373                        | StatusBarManager.DISABLE_SEARCH)) != 0) {
1374            // the nav bar will take care of these
1375            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1376
1377            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1378                // close recents if it's visible
1379                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1380                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1381            }
1382        }
1383
1384        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1385            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1386                if (mTicking) {
1387                    haltTicker();
1388                }
1389
1390                mNotificationIcons.animate()
1391                    .alpha(0f)
1392                    .translationY(mNaturalBarHeight*0.5f)
1393                    .setDuration(175)
1394                    .setInterpolator(new DecelerateInterpolator(1.5f))
1395                    .setListener(mMakeIconsInvisible)
1396                    .start();
1397            } else {
1398                mNotificationIcons.setVisibility(View.VISIBLE);
1399                mNotificationIcons.animate()
1400                    .alpha(1f)
1401                    .translationY(0)
1402                    .setStartDelay(0)
1403                    .setInterpolator(new DecelerateInterpolator(1.5f))
1404                    .setDuration(175)
1405                    .start();
1406            }
1407        } else if ((diff & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) {
1408            if ((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) {
1409                // we are outside a secure keyguard, so we need to switch to "public" mode
1410                setLockscreenPublicMode(true);
1411            } else {
1412                // user has authenticated the device; full notifications may be shown
1413                setLockscreenPublicMode(false);
1414            }
1415            updateNotificationIcons();
1416        }
1417    }
1418
1419    @Override
1420    protected BaseStatusBar.H createHandler() {
1421        return new PhoneStatusBar.H();
1422    }
1423
1424    /**
1425     * All changes to the status bar and notifications funnel through here and are batched.
1426     */
1427    private class H extends BaseStatusBar.H {
1428        public void handleMessage(Message m) {
1429            super.handleMessage(m);
1430            switch (m.what) {
1431                case MSG_OPEN_NOTIFICATION_PANEL:
1432                    animateExpandNotificationsPanel();
1433                    break;
1434                case MSG_OPEN_SETTINGS_PANEL:
1435                    animateExpandSettingsPanel();
1436                    break;
1437                case MSG_CLOSE_PANELS:
1438                    animateCollapsePanels();
1439                    break;
1440                case MSG_SHOW_HEADS_UP:
1441                    setHeadsUpVisibility(true);
1442                    break;
1443                case MSG_HIDE_HEADS_UP:
1444                    setHeadsUpVisibility(false);
1445                    break;
1446                case MSG_ESCALATE_HEADS_UP:
1447                    escalateHeadsUp();
1448                    setHeadsUpVisibility(false);
1449                    break;
1450            }
1451        }
1452    }
1453
1454    /**  if the interrupting notification had a fullscreen intent, fire it now.  */
1455    private void escalateHeadsUp() {
1456        if (mInterruptingNotificationEntry != null) {
1457            final StatusBarNotification sbn = mInterruptingNotificationEntry.notification;
1458            final Notification notification = sbn.getNotification();
1459            if (notification.fullScreenIntent != null) {
1460                if (DEBUG)
1461                    Log.d(TAG, "converting a heads up to fullScreen");
1462                try {
1463                    notification.fullScreenIntent.send();
1464                } catch (PendingIntent.CanceledException e) {
1465                }
1466            }
1467        }
1468    }
1469
1470    public Handler getHandler() {
1471        return mHandler;
1472    }
1473
1474    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1475        public void onFocusChange(View v, boolean hasFocus) {
1476            // Because 'v' is a ViewGroup, all its children will be (un)selected
1477            // too, which allows marqueeing to work.
1478            v.setSelected(hasFocus);
1479        }
1480    };
1481
1482    boolean panelsEnabled() {
1483        return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
1484    }
1485
1486    void makeExpandedVisible() {
1487        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1488        if (mExpandedVisible || !panelsEnabled()) {
1489            return;
1490        }
1491
1492        mExpandedVisible = true;
1493        if (mNavigationBarView != null)
1494            mNavigationBarView.setSlippery(true);
1495
1496        updateCarrierLabelVisibility(true);
1497
1498        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1499
1500        // Expand the window to encompass the full screen in anticipation of the drag.
1501        // This is only possible to do atomically because the status bar is at the top of the screen!
1502        mStatusBarWindowManager.setStatusBarExpanded(true);
1503
1504        visibilityChanged(true);
1505
1506        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
1507    }
1508
1509    public void animateCollapsePanels() {
1510        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
1511    }
1512
1513    public void animateCollapsePanels(int flags) {
1514        if (SPEW) {
1515            Log.d(TAG, "animateCollapse():"
1516                    + " mExpandedVisible=" + mExpandedVisible
1517                    + " flags=" + flags);
1518        }
1519
1520        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1521            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1522            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1523        }
1524
1525        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1526            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1527            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1528        }
1529
1530        if (mStatusBarWindow != null) {
1531
1532            // release focus immediately to kick off focus change transition
1533            mStatusBarWindowManager.setStatusBarFocusable(false);
1534
1535            mStatusBarWindow.cancelExpandHelper();
1536            mStatusBarView.collapseAllPanels(true);
1537        }
1538    }
1539
1540    public ViewPropertyAnimator setVisibilityWhenDone(
1541            final ViewPropertyAnimator a, final View v, final int vis) {
1542        a.setListener(new AnimatorListenerAdapter() {
1543            @Override
1544            public void onAnimationEnd(Animator animation) {
1545                v.setVisibility(vis);
1546                a.setListener(null); // oneshot
1547            }
1548        });
1549        return a;
1550    }
1551
1552    public Animator setVisibilityWhenDone(
1553            final Animator a, final View v, final int vis) {
1554        a.addListener(new AnimatorListenerAdapter() {
1555            @Override
1556            public void onAnimationEnd(Animator animation) {
1557                v.setVisibility(vis);
1558            }
1559        });
1560        return a;
1561    }
1562
1563    public Animator interpolator(TimeInterpolator ti, Animator a) {
1564        a.setInterpolator(ti);
1565        return a;
1566    }
1567
1568    public Animator startDelay(int d, Animator a) {
1569        a.setStartDelay(d);
1570        return a;
1571    }
1572
1573    public Animator start(Animator a) {
1574        a.start();
1575        return a;
1576    }
1577
1578    final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
1579    final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
1580    final int FLIP_DURATION_OUT = 125;
1581    final int FLIP_DURATION_IN = 225;
1582    final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
1583
1584    Animator mScrollViewAnim, mFlipSettingsViewAnim, mNotificationButtonAnim,
1585        mSettingsButtonAnim, mClearButtonAnim;
1586
1587    @Override
1588    public void animateExpandNotificationsPanel() {
1589        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1590        if (!panelsEnabled()) {
1591            return ;
1592        }
1593
1594        mNotificationPanel.expand();
1595        if (mHasFlipSettings && mStackScroller.getVisibility() != View.VISIBLE) {
1596            flipToNotifications();
1597        }
1598
1599        if (false) postStartTracing();
1600    }
1601
1602    public void flipToNotifications() {
1603        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
1604        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1605        if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
1606        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
1607        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1608
1609        mStackScroller.setVisibility(View.VISIBLE);
1610        mScrollViewAnim = start(
1611            startDelay(FLIP_DURATION_OUT,
1612                interpolator(mDecelerateInterpolator,
1613                    ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 0f, 1f)
1614                        .setDuration(FLIP_DURATION_IN)
1615                    )));
1616        mFlipSettingsViewAnim = start(
1617            setVisibilityWhenDone(
1618                interpolator(mAccelerateInterpolator,
1619                        ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
1620                        )
1621                    .setDuration(FLIP_DURATION_OUT),
1622                mFlipSettingsView, View.INVISIBLE));
1623        mNotificationButtonAnim = start(
1624            setVisibilityWhenDone(
1625                ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
1626                    .setDuration(FLIP_DURATION),
1627                mNotificationButton, View.INVISIBLE));
1628        mSettingsButton.setVisibility(View.VISIBLE);
1629        mSettingsButtonAnim = start(
1630            ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
1631                .setDuration(FLIP_DURATION));
1632        mClearButton.setVisibility(View.VISIBLE);
1633        mClearButton.setAlpha(0f);
1634        setAreThereNotifications(); // this will show/hide the button as necessary
1635        mNotificationPanel.postDelayed(new Runnable() {
1636            public void run() {
1637                updateCarrierLabelVisibility(false);
1638            }
1639        }, FLIP_DURATION - 150);
1640        if (mOnFlipRunnable != null) {
1641            mOnFlipRunnable.run();
1642        }
1643    }
1644
1645    @Override
1646    public void animateExpandSettingsPanel() {
1647        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1648        if (!panelsEnabled()) {
1649            return;
1650        }
1651
1652        // Settings are not available in setup
1653        if (!mUserSetup) return;
1654
1655        if (mHasFlipSettings) {
1656            mNotificationPanel.expand();
1657            if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
1658                flipToSettings();
1659            }
1660        } else if (mSettingsPanel != null) {
1661            mSettingsPanel.expand();
1662        }
1663
1664        if (false) postStartTracing();
1665    }
1666
1667    public void switchToSettings() {
1668        // Settings are not available in setup
1669        if (!mUserSetup) return;
1670
1671        mFlipSettingsView.setScaleX(1f);
1672        mFlipSettingsView.setVisibility(View.VISIBLE);
1673        mSettingsButton.setVisibility(View.GONE);
1674        mStackScroller.setVisibility(View.GONE);
1675        mStackScroller.setScaleX(0f);
1676        mNotificationButton.setVisibility(View.VISIBLE);
1677        mNotificationButton.setAlpha(1f);
1678        mClearButton.setVisibility(View.GONE);
1679        if (mOnFlipRunnable != null) {
1680            mOnFlipRunnable.run();
1681        }
1682    }
1683
1684    public boolean isFlippedToSettings() {
1685        if (mFlipSettingsView != null) {
1686            return mFlipSettingsView.getVisibility() == View.VISIBLE;
1687        }
1688        return false;
1689    }
1690
1691    public void flipToSettings() {
1692        // Settings are not available in setup
1693        if (!mUserSetup) return;
1694
1695        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
1696        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1697        if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
1698        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
1699        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1700
1701        mFlipSettingsView.setVisibility(View.VISIBLE);
1702        mFlipSettingsView.setScaleX(0f);
1703        mFlipSettingsViewAnim = start(
1704            startDelay(FLIP_DURATION_OUT,
1705                interpolator(mDecelerateInterpolator,
1706                    ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
1707                        .setDuration(FLIP_DURATION_IN)
1708                    )));
1709        mScrollViewAnim = start(
1710            setVisibilityWhenDone(
1711                interpolator(mAccelerateInterpolator,
1712                        ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 1f, 0f)
1713                        )
1714                    .setDuration(FLIP_DURATION_OUT),
1715                    mStackScroller, View.INVISIBLE));
1716        mSettingsButtonAnim = start(
1717            setVisibilityWhenDone(
1718                ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
1719                    .setDuration(FLIP_DURATION),
1720                    mStackScroller, View.INVISIBLE));
1721        mNotificationButton.setVisibility(View.VISIBLE);
1722        mNotificationButtonAnim = start(
1723            ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
1724                .setDuration(FLIP_DURATION));
1725        mClearButtonAnim = start(
1726            setVisibilityWhenDone(
1727                ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
1728                .setDuration(FLIP_DURATION),
1729                mClearButton, View.INVISIBLE));
1730        mNotificationPanel.postDelayed(new Runnable() {
1731            public void run() {
1732                updateCarrierLabelVisibility(false);
1733            }
1734        }, FLIP_DURATION - 150);
1735        if (mOnFlipRunnable != null) {
1736            mOnFlipRunnable.run();
1737        }
1738    }
1739
1740    public void flipPanels() {
1741        if (mHasFlipSettings) {
1742            if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
1743                flipToSettings();
1744            } else {
1745                flipToNotifications();
1746            }
1747        }
1748    }
1749
1750    public void animateCollapseQuickSettings() {
1751        mStatusBarView.collapseAllPanels(true);
1752    }
1753
1754    void makeExpandedInvisibleSoon() {
1755        mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50);
1756    }
1757
1758    void makeExpandedInvisible() {
1759        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
1760                + " mExpandedVisible=" + mExpandedVisible);
1761
1762        if (!mExpandedVisible || mStatusBarWindow == null) {
1763            return;
1764        }
1765
1766        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
1767        mStatusBarView.collapseAllPanels(/*animate=*/ false);
1768
1769        if (mHasFlipSettings) {
1770            // reset things to their proper state
1771            if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
1772            if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1773            if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
1774            if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
1775            if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1776
1777            mStackScroller.setScaleX(1f);
1778            mStackScroller.setVisibility(View.VISIBLE);
1779            mSettingsButton.setAlpha(1f);
1780            mSettingsButton.setVisibility(View.VISIBLE);
1781            mNotificationPanel.setVisibility(View.GONE);
1782            mFlipSettingsView.setVisibility(View.GONE);
1783            mNotificationButton.setVisibility(View.GONE);
1784            setAreThereNotifications(); // show the clear button
1785        }
1786
1787        mExpandedVisible = false;
1788        if (mNavigationBarView != null)
1789            mNavigationBarView.setSlippery(false);
1790        visibilityChanged(false);
1791
1792        // Shrink the window to the size of the status bar only
1793        mStatusBarWindowManager.setStatusBarExpanded(false);
1794
1795        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1796            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1797        }
1798
1799        // Close any "App info" popups that might have snuck on-screen
1800        dismissPopups();
1801
1802        if (mPostCollapseCleanup != null) {
1803            mPostCollapseCleanup.run();
1804            mPostCollapseCleanup = null;
1805        }
1806
1807        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
1808
1809        showBouncer();
1810    }
1811
1812    public boolean interceptTouchEvent(MotionEvent event) {
1813        if (DEBUG_GESTURES) {
1814            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
1815                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
1816                        event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
1817            }
1818
1819        }
1820
1821        if (SPEW) {
1822            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1823                + mDisabled + " mTracking=" + mTracking);
1824        } else if (CHATTY) {
1825            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1826                Log.d(TAG, String.format(
1827                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1828                            MotionEvent.actionToString(event.getAction()),
1829                            event.getRawX(), event.getRawY(), mDisabled));
1830            }
1831        }
1832
1833        if (DEBUG_GESTURES) {
1834            mGestureRec.add(event);
1835        }
1836
1837        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
1838            final boolean upOrCancel =
1839                    event.getAction() == MotionEvent.ACTION_UP ||
1840                    event.getAction() == MotionEvent.ACTION_CANCEL;
1841            if (upOrCancel && !mExpandedVisible) {
1842                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
1843            } else {
1844                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
1845            }
1846        }
1847        return false;
1848    }
1849
1850    public GestureRecorder getGestureRecorder() {
1851        return mGestureRec;
1852    }
1853
1854    private void setNavigationIconHints(int hints) {
1855        if (hints == mNavigationIconHints) return;
1856
1857        mNavigationIconHints = hints;
1858
1859        if (mNavigationBarView != null) {
1860            mNavigationBarView.setNavigationIconHints(hints);
1861        }
1862        checkBarModes();
1863    }
1864
1865    @Override // CommandQueue
1866    public void setWindowState(int window, int state) {
1867        boolean showing = state == WINDOW_STATE_SHOWING;
1868        if (mStatusBarWindow != null
1869                && window == StatusBarManager.WINDOW_STATUS_BAR
1870                && mStatusBarWindowState != state) {
1871            mStatusBarWindowState = state;
1872            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
1873            if (!showing) {
1874                mStatusBarView.collapseAllPanels(false);
1875            }
1876        }
1877        if (mNavigationBarView != null
1878                && window == StatusBarManager.WINDOW_NAVIGATION_BAR
1879                && mNavigationBarWindowState != state) {
1880            mNavigationBarWindowState = state;
1881            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
1882        }
1883    }
1884
1885    @Override // CommandQueue
1886    public void setSystemUiVisibility(int vis, int mask) {
1887        final int oldVal = mSystemUiVisibility;
1888        final int newVal = (oldVal&~mask) | (vis&mask);
1889        final int diff = newVal ^ oldVal;
1890        if (DEBUG) Log.d(TAG, String.format(
1891                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
1892                Integer.toHexString(vis), Integer.toHexString(mask),
1893                Integer.toHexString(oldVal), Integer.toHexString(newVal),
1894                Integer.toHexString(diff)));
1895        if (diff != 0) {
1896            mSystemUiVisibility = newVal;
1897
1898            // update low profile
1899            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
1900                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
1901                if (lightsOut) {
1902                    animateCollapsePanels();
1903                    if (mTicking) {
1904                        haltTicker();
1905                    }
1906                }
1907
1908                setAreThereNotifications();
1909            }
1910
1911            // update status bar mode
1912            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
1913                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
1914
1915            // update navigation bar mode
1916            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
1917                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
1918                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
1919            final boolean sbModeChanged = sbMode != -1;
1920            final boolean nbModeChanged = nbMode != -1;
1921            boolean checkBarModes = false;
1922            if (sbModeChanged && sbMode != mStatusBarMode) {
1923                mStatusBarMode = sbMode;
1924                checkBarModes = true;
1925            }
1926            if (nbModeChanged && nbMode != mNavigationBarMode) {
1927                mNavigationBarMode = nbMode;
1928                checkBarModes = true;
1929            }
1930            if (checkBarModes) {
1931                checkBarModes();
1932            }
1933            if (sbModeChanged || nbModeChanged) {
1934                // update transient bar autohide
1935                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
1936                    scheduleAutohide();
1937                } else {
1938                    cancelAutohide();
1939                }
1940            }
1941
1942            // ready to unhide
1943            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
1944                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
1945            }
1946            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
1947                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
1948            }
1949
1950            // send updated sysui visibility to window manager
1951            notifyUiVisibilityChanged(mSystemUiVisibility);
1952        }
1953    }
1954
1955    private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
1956            int transientFlag, int translucentFlag) {
1957        final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
1958        final int newMode = barMode(newVis, transientFlag, translucentFlag);
1959        if (oldMode == newMode) {
1960            return -1; // no mode change
1961        }
1962        return newMode;
1963    }
1964
1965    private int barMode(int vis, int transientFlag, int translucentFlag) {
1966        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
1967                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
1968                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
1969                : MODE_OPAQUE;
1970    }
1971
1972    private void checkBarModes() {
1973        if (mDemoMode) return;
1974        int sbMode = mStatusBarMode;
1975        if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0) {
1976            // if panels are expandable, force the status bar opaque on any interaction
1977            sbMode = MODE_OPAQUE;
1978        }
1979        checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
1980        if (mNavigationBarView != null) {
1981            checkBarMode(mNavigationBarMode,
1982                    mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
1983        }
1984    }
1985
1986    private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
1987        final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN;
1988        transitions.transitionTo(mode, anim);
1989    }
1990
1991    private void finishBarAnimations() {
1992        mStatusBarView.getBarTransitions().finishAnimations();
1993        if (mNavigationBarView != null) {
1994            mNavigationBarView.getBarTransitions().finishAnimations();
1995        }
1996    }
1997
1998    private final Runnable mCheckBarModes = new Runnable() {
1999        @Override
2000        public void run() {
2001            checkBarModes();
2002        }};
2003
2004    @Override
2005    public void setInteracting(int barWindow, boolean interacting) {
2006        mInteractingWindows = interacting
2007                ? (mInteractingWindows | barWindow)
2008                : (mInteractingWindows & ~barWindow);
2009        if (mInteractingWindows != 0) {
2010            suspendAutohide();
2011        } else {
2012            resumeSuspendedAutohide();
2013        }
2014        checkBarModes();
2015    }
2016
2017    private void resumeSuspendedAutohide() {
2018        if (mAutohideSuspended) {
2019            scheduleAutohide();
2020            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
2021        }
2022    }
2023
2024    private void suspendAutohide() {
2025        mHandler.removeCallbacks(mAutohide);
2026        mHandler.removeCallbacks(mCheckBarModes);
2027        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
2028    }
2029
2030    private void cancelAutohide() {
2031        mAutohideSuspended = false;
2032        mHandler.removeCallbacks(mAutohide);
2033    }
2034
2035    private void scheduleAutohide() {
2036        cancelAutohide();
2037        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2038    }
2039
2040    private void checkUserAutohide(View v, MotionEvent event) {
2041        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
2042                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2043                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
2044                ) {
2045            userAutohide();
2046        }
2047    }
2048
2049    private void userAutohide() {
2050        cancelAutohide();
2051        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2052    }
2053
2054    private boolean areLightsOn() {
2055        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2056    }
2057
2058    public void setLightsOn(boolean on) {
2059        Log.v(TAG, "setLightsOn(" + on + ")");
2060        if (on) {
2061            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2062        } else {
2063            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2064        }
2065    }
2066
2067    private void notifyUiVisibilityChanged(int vis) {
2068        try {
2069            mWindowManagerService.statusBarVisibilityChanged(vis);
2070        } catch (RemoteException ex) {
2071        }
2072    }
2073
2074    public void topAppWindowChanged(boolean showMenu) {
2075        if (DEBUG) {
2076            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2077        }
2078        if (mNavigationBarView != null) {
2079            mNavigationBarView.setMenuVisibility(showMenu);
2080        }
2081
2082        // See above re: lights-out policy for legacy apps.
2083        if (showMenu) setLightsOn(true);
2084    }
2085
2086    @Override
2087    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
2088        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
2089            || ((vis & InputMethodService.IME_VISIBLE) != 0);
2090
2091        setNavigationIconHints(
2092                altBack ? (mNavigationIconHints | NAVIGATION_HINT_BACK_ALT)
2093                        : (mNavigationIconHints & ~NAVIGATION_HINT_BACK_ALT));
2094        if (mQS != null) mQS.setImeWindowStatus(vis > 0);
2095    }
2096
2097    @Override
2098    public void setHardKeyboardStatus(boolean available, boolean enabled) {}
2099
2100    @Override
2101    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
2102        // no ticking in lights-out mode
2103        if (!areLightsOn()) return;
2104
2105        // no ticking in Setup
2106        if (!isDeviceProvisioned()) return;
2107
2108        // not for you
2109        if (!notificationIsForCurrentProfiles(n)) return;
2110
2111        // Show the ticker if one is requested. Also don't do this
2112        // until status bar window is attached to the window manager,
2113        // because...  well, what's the point otherwise?  And trying to
2114        // run a ticker without being attached will crash!
2115        if (n.getNotification().tickerText != null && mStatusBarWindow != null
2116                && mStatusBarWindow.getWindowToken() != null) {
2117            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2118                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2119                mTicker.addEntry(n);
2120            }
2121        }
2122    }
2123
2124    private class MyTicker extends Ticker {
2125        MyTicker(Context context, View sb) {
2126            super(context, sb);
2127        }
2128
2129        @Override
2130        public void tickerStarting() {
2131            mTicking = true;
2132            mStatusBarContents.setVisibility(View.GONE);
2133            mTickerView.setVisibility(View.VISIBLE);
2134            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2135            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2136        }
2137
2138        @Override
2139        public void tickerDone() {
2140            mStatusBarContents.setVisibility(View.VISIBLE);
2141            mTickerView.setVisibility(View.GONE);
2142            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2143            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2144                        mTickingDoneListener));
2145        }
2146
2147        public void tickerHalting() {
2148            if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2149                mStatusBarContents.setVisibility(View.VISIBLE);
2150                mStatusBarContents
2151                        .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2152            }
2153            mTickerView.setVisibility(View.GONE);
2154            // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2155        }
2156    }
2157
2158    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2159        public void onAnimationEnd(Animation animation) {
2160            mTicking = false;
2161        }
2162        public void onAnimationRepeat(Animation animation) {
2163        }
2164        public void onAnimationStart(Animation animation) {
2165        }
2166    };
2167
2168    private Animation loadAnim(int id, Animation.AnimationListener listener) {
2169        Animation anim = AnimationUtils.loadAnimation(mContext, id);
2170        if (listener != null) {
2171            anim.setAnimationListener(listener);
2172        }
2173        return anim;
2174    }
2175
2176    public static String viewInfo(View v) {
2177        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2178                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2179    }
2180
2181    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2182        synchronized (mQueueLock) {
2183            pw.println("Current Status Bar state:");
2184            pw.println("  mExpandedVisible=" + mExpandedVisible
2185                    + ", mTrackingPosition=" + mTrackingPosition);
2186            pw.println("  mTicking=" + mTicking);
2187            pw.println("  mTracking=" + mTracking);
2188            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2189            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2190            pw.println("  mTickerView: " + viewInfo(mTickerView));
2191            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2192                    + " scroll " + mStackScroller.getScrollX()
2193                    + "," + mStackScroller.getScrollY());
2194        }
2195
2196        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2197        pw.print("  mStatusBarWindowState=");
2198        pw.println(windowStateToString(mStatusBarWindowState));
2199        pw.print("  mStatusBarMode=");
2200        pw.println(BarTransitions.modeToString(mStatusBarMode));
2201        pw.print("  mZenMode=");
2202        pw.println(Settings.Global.zenModeToString(mZenMode));
2203        pw.print("  mUseHeadsUp=");
2204        pw.println(mUseHeadsUp);
2205        pw.print("  interrupting package: ");
2206        pw.println(hunStateToString(mInterruptingNotificationEntry));
2207        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2208        if (mNavigationBarView != null) {
2209            pw.print("  mNavigationBarWindowState=");
2210            pw.println(windowStateToString(mNavigationBarWindowState));
2211            pw.print("  mNavigationBarMode=");
2212            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2213            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2214        }
2215
2216        pw.print("  mNavigationBarView=");
2217        if (mNavigationBarView == null) {
2218            pw.println("null");
2219        } else {
2220            mNavigationBarView.dump(fd, pw, args);
2221        }
2222
2223        pw.println("  Panels: ");
2224        if (mNotificationPanel != null) {
2225            pw.println("    mNotificationPanel=" +
2226                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2227            pw.print  ("      ");
2228            mNotificationPanel.dump(fd, pw, args);
2229        }
2230        if (mSettingsPanel != null) {
2231            pw.println("    mSettingsPanel=" +
2232                mSettingsPanel + " params=" + mSettingsPanel.getLayoutParams().debug(""));
2233            pw.print  ("      ");
2234            mSettingsPanel.dump(fd, pw, args);
2235        }
2236
2237        if (DUMPTRUCK) {
2238            synchronized (mNotificationData) {
2239                int N = mNotificationData.size();
2240                pw.println("  notification icons: " + N);
2241                for (int i=0; i<N; i++) {
2242                    NotificationData.Entry e = mNotificationData.get(i);
2243                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
2244                    StatusBarNotification n = e.notification;
2245                    pw.println("         pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore());
2246                    pw.println("         notification=" + n.getNotification());
2247                    pw.println("         tickerText=\"" + n.getNotification().tickerText + "\"");
2248                }
2249            }
2250
2251            int N = mStatusIcons.getChildCount();
2252            pw.println("  system icons: " + N);
2253            for (int i=0; i<N; i++) {
2254                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2255                pw.println("    [" + i + "] icon=" + ic);
2256            }
2257
2258            if (false) {
2259                pw.println("see the logcat for a dump of the views we have created.");
2260                // must happen on ui thread
2261                mHandler.post(new Runnable() {
2262                        public void run() {
2263                            mStatusBarView.getLocationOnScreen(mAbsPos);
2264                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2265                                    + ") " + mStatusBarView.getWidth() + "x"
2266                                    + getStatusBarHeight());
2267                            mStatusBarView.debug();
2268                        }
2269                    });
2270            }
2271        }
2272
2273        if (DEBUG_GESTURES) {
2274            pw.print("  status bar gestures: ");
2275            mGestureRec.dump(fd, pw, args);
2276        }
2277
2278        mNetworkController.dump(fd, pw, args);
2279    }
2280
2281    private String hunStateToString(Entry entry) {
2282        if (entry == null) return "null";
2283        if (entry.notification == null) return "corrupt";
2284        return entry.notification.getPackageName();
2285    }
2286
2287    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2288        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
2289        pw.println(BarTransitions.modeToString(transitions.getMode()));
2290    }
2291
2292    @Override
2293    public void createAndAddWindows() {
2294        addStatusBarWindow();
2295    }
2296
2297    private void addStatusBarWindow() {
2298        makeStatusBarView();
2299        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2300        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2301    }
2302
2303    void setNotificationIconVisibility(boolean visible, int anim) {
2304        int old = mNotificationIcons.getVisibility();
2305        int v = visible ? View.VISIBLE : View.INVISIBLE;
2306        if (old != v) {
2307            mNotificationIcons.setVisibility(v);
2308            mNotificationIcons.startAnimation(loadAnim(anim, null));
2309        }
2310    }
2311
2312    void updateExpandedInvisiblePosition() {
2313        mTrackingPosition = -mDisplayMetrics.heightPixels;
2314    }
2315
2316    static final float saturate(float a) {
2317        return a < 0f ? 0f : (a > 1f ? 1f : a);
2318    }
2319
2320    @Override
2321    protected int getExpandedViewMaxHeight() {
2322        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
2323    }
2324
2325    @Override
2326    public void updateExpandedViewPos(int thingy) {
2327        if (SPEW) Log.v(TAG, "updateExpandedViewPos");
2328
2329        // on larger devices, the notification panel is propped open a bit
2330        mNotificationPanel.setMinimumHeight(
2331                (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2332
2333        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2334        lp.gravity = mNotificationPanelGravity;
2335        lp.setMarginStart(mNotificationPanelMarginPx);
2336        mNotificationPanel.setLayoutParams(lp);
2337
2338        if (mSettingsPanel != null) {
2339            lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
2340            lp.gravity = mSettingsPanelGravity;
2341            lp.setMarginEnd(mNotificationPanelMarginPx);
2342            mSettingsPanel.setLayoutParams(lp);
2343        }
2344
2345        if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) {
2346            mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx);
2347            mStackScroller.getLocationOnScreen(mStackScrollerPosition);
2348            mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight;
2349        }
2350
2351        updateCarrierLabelVisibility(false);
2352    }
2353
2354    // called by makeStatusbar and also by PhoneStatusBarView
2355    void updateDisplaySize() {
2356        mDisplay.getMetrics(mDisplayMetrics);
2357        mDisplay.getSize(mCurrentDisplaySize);
2358        if (DEBUG_GESTURES) {
2359            mGestureRec.tag("display",
2360                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
2361        }
2362    }
2363
2364    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2365        public void onClick(View v) {
2366            synchronized (mNotificationData) {
2367                mPostCollapseCleanup = new Runnable() {
2368                    @Override
2369                    public void run() {
2370                        if (DEBUG) {
2371                            Log.v(TAG, "running post-collapse cleanup");
2372                        }
2373                        try {
2374                            mBarService.onClearAllNotifications(mCurrentUserId);
2375                        } catch (Exception ex) { }
2376                    }
2377                };
2378
2379                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2380                return;
2381                // TODO: Handle this better with notification stack scroller
2382            }
2383        }
2384    };
2385
2386    public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
2387        if (onlyProvisioned && !isDeviceProvisioned()) return;
2388        try {
2389            // Dismiss the lock screen when Settings starts.
2390            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2391        } catch (RemoteException e) {
2392        }
2393        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2394        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
2395        animateCollapsePanels();
2396    }
2397
2398    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
2399        public void onClick(View v) {
2400            if (mHasSettingsPanel) {
2401                animateExpandSettingsPanel();
2402            } else {
2403                startActivityDismissingKeyguard(
2404                        new Intent(android.provider.Settings.ACTION_SETTINGS), true);
2405            }
2406        }
2407    };
2408
2409    private View.OnClickListener mClockClickListener = new View.OnClickListener() {
2410        public void onClick(View v) {
2411            startActivityDismissingKeyguard(
2412                    new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone
2413        }
2414    };
2415
2416    private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() {
2417        public void onClick(View v) {
2418            animateExpandNotificationsPanel();
2419        }
2420    };
2421
2422    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2423        public void onReceive(Context context, Intent intent) {
2424            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
2425            String action = intent.getAction();
2426            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2427                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2428                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2429                    String reason = intent.getStringExtra("reason");
2430                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2431                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2432                    }
2433                }
2434                animateCollapsePanels(flags);
2435            }
2436            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
2437                mScreenOn = false;
2438                notifyNavigationBarScreenOn(false);
2439                notifyHeadsUpScreenOn(false);
2440                finishBarAnimations();
2441            }
2442            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
2443                mScreenOn = true;
2444                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
2445                repositionNavigationBar();
2446                notifyNavigationBarScreenOn(true);
2447            }
2448            else if (ACTION_DEMO.equals(action)) {
2449                Bundle bundle = intent.getExtras();
2450                if (bundle != null) {
2451                    String command = bundle.getString("command", "").trim().toLowerCase();
2452                    if (command.length() > 0) {
2453                        try {
2454                            dispatchDemoCommand(command, bundle);
2455                        } catch (Throwable t) {
2456                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
2457                        }
2458                    }
2459                }
2460            }
2461        }
2462    };
2463
2464    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
2465    @Override
2466    protected void onConfigurationChanged(Configuration newConfig) {
2467        super.onConfigurationChanged(newConfig); // calls refreshLayout
2468
2469        if (DEBUG) {
2470            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
2471        }
2472        updateDisplaySize(); // populates mDisplayMetrics
2473
2474        updateResources();
2475        repositionNavigationBar();
2476        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2477        updateShowSearchHoldoff();
2478    }
2479
2480    @Override
2481    public void userSwitched(int newUserId) {
2482        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
2483        animateCollapsePanels();
2484        updateNotificationIcons();
2485        resetUserSetupObserver();
2486    }
2487
2488    private void resetUserSetupObserver() {
2489        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
2490        mUserSetupObserver.onChange(false);
2491        mContext.getContentResolver().registerContentObserver(
2492                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
2493                mUserSetupObserver,
2494                mCurrentUserId);
2495    }
2496
2497    private void setHeadsUpVisibility(boolean vis) {
2498        if (!ENABLE_HEADS_UP) return;
2499        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
2500        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
2501        if (!vis) {
2502            if (DEBUG) Log.d(TAG, "setting heads up entry to null");
2503            mInterruptingNotificationEntry = null;
2504        }
2505    }
2506
2507    public void animateHeadsUp(boolean animateInto, float frac) {
2508        if (!ENABLE_HEADS_UP || mHeadsUpNotificationView == null) return;
2509        frac = frac / 0.4f;
2510        frac = frac < 1.0f ? frac : 1.0f;
2511        float alpha = 1.0f - frac;
2512        float offset = mHeadsUpVerticalOffset * frac;
2513        offset = animateInto ? offset : 0f;
2514        mHeadsUpNotificationView.setAlpha(alpha);
2515        mHeadsUpNotificationView.setY(offset);
2516    }
2517
2518    public void onHeadsUpDismissed() {
2519        if (mInterruptingNotificationEntry == null) return;
2520        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
2521        if (mHeadsUpNotificationView.isClearable()) {
2522            try {
2523                mBarService.onNotificationClear(
2524                        mInterruptingNotificationEntry.notification.getPackageName(),
2525                        mInterruptingNotificationEntry.notification.getTag(),
2526                        mInterruptingNotificationEntry.notification.getId(),
2527                        mInterruptingNotificationEntry.notification.getUserId());
2528            } catch (android.os.RemoteException ex) {
2529                // oh well
2530            }
2531        }
2532    }
2533
2534    /**
2535     * Reload some of our resources when the configuration changes.
2536     *
2537     * We don't reload everything when the configuration changes -- we probably
2538     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2539     * meantime, just update the things that we know change.
2540     */
2541    void updateResources() {
2542        final Context context = mContext;
2543        final Resources res = context.getResources();
2544
2545        if (mClearButton instanceof TextView) {
2546            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
2547        }
2548
2549        // Update the QuickSettings container
2550        if (mQS != null) mQS.updateResources();
2551
2552        loadDimens();
2553    }
2554
2555    protected void loadDimens() {
2556        final Resources res = mContext.getResources();
2557
2558        mNaturalBarHeight = res.getDimensionPixelSize(
2559                com.android.internal.R.dimen.status_bar_height);
2560
2561        int newIconSize = res.getDimensionPixelSize(
2562            com.android.internal.R.dimen.status_bar_icon_size);
2563        int newIconHPadding = res.getDimensionPixelSize(
2564            R.dimen.status_bar_icon_padding);
2565
2566        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2567//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2568            mIconHPadding = newIconHPadding;
2569            mIconSize = newIconSize;
2570            //reloadAllNotificationIcons(); // reload the tray
2571        }
2572
2573        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2574
2575        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2576        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2577        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2578        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2579
2580        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2581        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2582
2583        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2584        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2585
2586        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2587
2588        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
2589
2590        mNotificationPanelMarginBottomPx
2591            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
2592        mNotificationPanelMarginPx
2593            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
2594        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
2595        if (mNotificationPanelGravity <= 0) {
2596            mNotificationPanelGravity = Gravity.START | Gravity.TOP;
2597        }
2598        mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
2599        Log.d(TAG, "mSettingsPanelGravity = " + mSettingsPanelGravity);
2600        if (mSettingsPanelGravity <= 0) {
2601            mSettingsPanelGravity = Gravity.END | Gravity.TOP;
2602        }
2603
2604        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
2605        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
2606
2607        mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
2608        if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
2609            mNotificationPanelMinHeightFrac = 0f;
2610        }
2611
2612        mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
2613        mRowHeight =  res.getDimensionPixelSize(R.dimen.notification_row_min_height);
2614
2615        if (false) Log.v(TAG, "updateResources");
2616    }
2617
2618    //
2619    // tracing
2620    //
2621
2622    void postStartTracing() {
2623        mHandler.postDelayed(mStartTracing, 3000);
2624    }
2625
2626    void vibrate() {
2627        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2628                Context.VIBRATOR_SERVICE);
2629        vib.vibrate(250, AudioManager.STREAM_SYSTEM);
2630    }
2631
2632    Runnable mStartTracing = new Runnable() {
2633        public void run() {
2634            vibrate();
2635            SystemClock.sleep(250);
2636            Log.d(TAG, "startTracing");
2637            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2638            mHandler.postDelayed(mStopTracing, 10000);
2639        }
2640    };
2641
2642    Runnable mStopTracing = new Runnable() {
2643        public void run() {
2644            android.os.Debug.stopMethodTracing();
2645            Log.d(TAG, "stopTracing");
2646            vibrate();
2647        }
2648    };
2649
2650    @Override
2651    protected void haltTicker() {
2652        mTicker.halt();
2653    }
2654
2655    @Override
2656    protected boolean shouldDisableNavbarGestures() {
2657        return !isDeviceProvisioned()
2658                || mExpandedVisible
2659                || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
2660    }
2661
2662    public void startSettingsActivity(String action) {
2663        if (mQS != null) {
2664            mQS.startSettingsActivity(action);
2665        }
2666    }
2667
2668    private static class FastColorDrawable extends Drawable {
2669        private final int mColor;
2670
2671        public FastColorDrawable(int color) {
2672            mColor = 0xff000000 | color;
2673        }
2674
2675        @Override
2676        public void draw(Canvas canvas) {
2677            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
2678        }
2679
2680        @Override
2681        public void setAlpha(int alpha) {
2682        }
2683
2684        @Override
2685        public void setColorFilter(ColorFilter cf) {
2686        }
2687
2688        @Override
2689        public int getOpacity() {
2690            return PixelFormat.OPAQUE;
2691        }
2692
2693        @Override
2694        public void setBounds(int left, int top, int right, int bottom) {
2695        }
2696
2697        @Override
2698        public void setBounds(Rect bounds) {
2699        }
2700    }
2701
2702    @Override
2703    public void destroy() {
2704        super.destroy();
2705        if (mStatusBarWindow != null) {
2706            mWindowManager.removeViewImmediate(mStatusBarWindow);
2707            mStatusBarWindow = null;
2708        }
2709        if (mNavigationBarView != null) {
2710            mWindowManager.removeViewImmediate(mNavigationBarView);
2711            mNavigationBarView = null;
2712        }
2713        mContext.unregisterReceiver(mBroadcastReceiver);
2714    }
2715
2716    private boolean mDemoModeAllowed;
2717    private boolean mDemoMode;
2718    private DemoStatusIcons mDemoStatusIcons;
2719
2720    @Override
2721    public void dispatchDemoCommand(String command, Bundle args) {
2722        if (!mDemoModeAllowed) {
2723            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
2724                    "sysui_demo_allowed", 0) != 0;
2725        }
2726        if (!mDemoModeAllowed) return;
2727        if (command.equals(COMMAND_ENTER)) {
2728            mDemoMode = true;
2729        } else if (command.equals(COMMAND_EXIT)) {
2730            mDemoMode = false;
2731            checkBarModes();
2732        } else if (!mDemoMode) {
2733            // automatically enter demo mode on first demo command
2734            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
2735        }
2736        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
2737        if (modeChange || command.equals(COMMAND_CLOCK)) {
2738            dispatchDemoCommandToView(command, args, R.id.clock);
2739        }
2740        if (modeChange || command.equals(COMMAND_BATTERY)) {
2741            dispatchDemoCommandToView(command, args, R.id.battery);
2742        }
2743        if (modeChange || command.equals(COMMAND_STATUS)) {
2744            if (mDemoStatusIcons == null) {
2745                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
2746            }
2747            mDemoStatusIcons.dispatchDemoCommand(command, args);
2748        }
2749        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
2750            mNetworkController.dispatchDemoCommand(command, args);
2751        }
2752        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
2753            View notifications = mStatusBarView == null ? null
2754                    : mStatusBarView.findViewById(R.id.notification_icon_area);
2755            if (notifications != null) {
2756                String visible = args.getString("visible");
2757                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
2758                notifications.setVisibility(vis);
2759            }
2760        }
2761        if (command.equals(COMMAND_BARS)) {
2762            String mode = args.getString("mode");
2763            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
2764                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
2765                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
2766                    -1;
2767            if (barMode != -1) {
2768                boolean animate = true;
2769                if (mStatusBarView != null) {
2770                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
2771                }
2772                if (mNavigationBarView != null) {
2773                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
2774                }
2775            }
2776        }
2777    }
2778
2779    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
2780        if (mStatusBarView == null) return;
2781        View v = mStatusBarView.findViewById(id);
2782        if (v instanceof DemoMode) {
2783            ((DemoMode)v).dispatchDemoCommand(command, args);
2784        }
2785    }
2786
2787    public boolean isOnKeyguard() {
2788        return mOnKeyguard;
2789    }
2790
2791    public void showKeyguard() {
2792        mOnKeyguard = true;
2793        instantExpandNotificationsPanel();
2794        if (isFlippedToSettings()) {
2795            flipToNotifications();
2796        }
2797        mStatusBarWindow.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
2798        mKeyguardStatusView.setVisibility(View.VISIBLE);
2799        mNotificationPanelHeader.setVisibility(View.GONE);
2800        if (mKeyguardSettingsFlipButton == null) {
2801            ViewStub flipStub = (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_flip_stub);
2802            mKeyguardSettingsFlipButton = flipStub.inflate();
2803            installSettingsButton(mKeyguardSettingsFlipButton);
2804        }
2805        mKeyguardSettingsFlipButton.setVisibility(View.VISIBLE);
2806        mKeyguardSettingsFlipButton.findViewById(R.id.settings_button).setVisibility(View.VISIBLE);
2807        mStackScroller.setAlpha(0.8f);
2808        mStackScroller.setLayerType(View.LAYER_TYPE_HARDWARE, null);
2809        updateExpansionStates();
2810    }
2811
2812    public void hideKeyguard() {
2813        mOnKeyguard = false;
2814        mStatusBarWindow.setSystemUiVisibility(0);
2815        mKeyguardStatusView.setVisibility(View.GONE);
2816        mNotificationPanelHeader.setVisibility(View.VISIBLE);
2817        mKeyguardSettingsFlipButton.setVisibility(View.GONE);
2818        mStackScroller.setAlpha(1f);
2819        mStackScroller.setLayerType(View.LAYER_TYPE_NONE, null);
2820        updateExpansionStates();
2821    }
2822
2823    public void userActivity() {
2824        if (mOnKeyguard) {
2825            mKeyguardViewMediatorCallback.userActivity();
2826        }
2827    }
2828
2829    private void showBouncer() {
2830        if (mOnKeyguard) {
2831            mStatusBarKeyguardViewManager.dismiss();
2832        }
2833    }
2834
2835    private void instantExpandNotificationsPanel() {
2836        mExpandedVisible = true;
2837        mNotificationPanel.setExpandedFraction(1);
2838    }
2839
2840    /**
2841     * @return a ViewGroup that spans the entire panel which contains the quick settings
2842     */
2843    public ViewGroup getQuickSettingsOverlayParent() {
2844        if (mHasSettingsPanel) {
2845            if (mHasFlipSettings) {
2846                return mNotificationPanel;
2847            } else {
2848                return mSettingsPanel;
2849            }
2850        } else {
2851            return null;
2852        }
2853    }
2854
2855    private void installSettingsButton(View parent) {
2856        ImageView settingsButton = (ImageView) mStatusBarWindow.findViewById(R.id.settings_button);
2857        if (settingsButton != null) {
2858            settingsButton.setOnClickListener(new View.OnClickListener() {
2859                @Override
2860                public void onClick(View v) {
2861                    animateExpandSettingsPanel();
2862                    v.setVisibility(View.INVISIBLE);
2863                }
2864            });
2865            if (mHasSettingsPanel) {
2866                // the settings panel is hiding behind this button
2867                settingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
2868                settingsButton.setVisibility(View.VISIBLE);
2869            } else {
2870                // no settings panel, go straight to settings
2871                settingsButton.setVisibility(View.VISIBLE);
2872                settingsButton.setImageResource(R.drawable.ic_notify_settings);
2873            }
2874        }
2875    }
2876}
2877