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