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