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