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