PhoneStatusBar.java revision 8b12f22bc1f654d0afe1a5588ed3667428b305c8
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.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
31
32import android.animation.Animator;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.TimeInterpolator;
35import android.app.ActivityManager;
36import android.app.ActivityManagerNative;
37import android.app.IActivityManager;
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.Bitmap;
49import android.graphics.Canvas;
50import android.graphics.ColorFilter;
51import android.graphics.PixelFormat;
52import android.graphics.Point;
53import android.graphics.PorterDuff;
54import android.graphics.Rect;
55import android.graphics.drawable.ColorDrawable;
56import android.graphics.drawable.Drawable;
57import android.inputmethodservice.InputMethodService;
58import android.media.AudioAttributes;
59import android.media.MediaMetadata;
60import android.media.session.MediaController;
61import android.media.session.MediaSession;
62import android.media.session.MediaSessionManager;
63import android.media.session.PlaybackState;
64import android.os.AsyncTask;
65import android.os.Bundle;
66import android.os.Handler;
67import android.os.IBinder;
68import android.os.Message;
69import android.os.PowerManager;
70import android.os.RemoteException;
71import android.os.SystemClock;
72import android.os.UserHandle;
73import android.provider.Settings;
74import android.service.notification.NotificationListenerService;
75import android.service.notification.NotificationListenerService.RankingMap;
76import android.service.notification.StatusBarNotification;
77import android.util.ArraySet;
78import android.util.DisplayMetrics;
79import android.util.EventLog;
80import android.util.Log;
81import android.view.Display;
82import android.view.Gravity;
83import android.view.HardwareCanvas;
84import android.view.KeyEvent;
85import android.view.LayoutInflater;
86import android.view.MotionEvent;
87import android.view.VelocityTracker;
88import android.view.View;
89import android.view.ViewGroup;
90import android.view.ViewGroup.LayoutParams;
91import android.view.ViewPropertyAnimator;
92import android.view.ViewStub;
93import android.view.ViewTreeObserver;
94import android.view.WindowManager;
95import android.view.accessibility.AccessibilityEvent;
96import android.view.accessibility.AccessibilityManager;
97import android.view.animation.AccelerateDecelerateInterpolator;
98import android.view.animation.AccelerateInterpolator;
99import android.view.animation.Animation;
100import android.view.animation.AnimationUtils;
101import android.view.animation.DecelerateInterpolator;
102import android.view.animation.Interpolator;
103import android.view.animation.LinearInterpolator;
104import android.view.animation.PathInterpolator;
105import android.widget.FrameLayout;
106import android.widget.ImageView;
107import android.widget.LinearLayout;
108import android.widget.TextView;
109
110import com.android.internal.statusbar.StatusBarIcon;
111import com.android.keyguard.KeyguardHostView.OnDismissAction;
112import com.android.keyguard.ViewMediatorCallback;
113import com.android.systemui.BatteryMeterView;
114import com.android.systemui.DemoMode;
115import com.android.systemui.EventLogTags;
116import com.android.systemui.FontSizeUtils;
117import com.android.systemui.R;
118import com.android.systemui.doze.DozeService;
119import com.android.systemui.keyguard.KeyguardViewMediator;
120import com.android.systemui.qs.QSPanel;
121import com.android.systemui.statusbar.ActivatableNotificationView;
122import com.android.systemui.statusbar.BaseStatusBar;
123import com.android.systemui.statusbar.CommandQueue;
124import com.android.systemui.statusbar.DismissView;
125import com.android.systemui.statusbar.DragDownHelper;
126import com.android.systemui.statusbar.EmptyShadeView;
127import com.android.systemui.statusbar.ExpandableNotificationRow;
128import com.android.systemui.statusbar.GestureRecorder;
129import com.android.systemui.statusbar.KeyguardIndicationController;
130import com.android.systemui.statusbar.NotificationData;
131import com.android.systemui.statusbar.NotificationData.Entry;
132import com.android.systemui.statusbar.NotificationOverflowContainer;
133import com.android.systemui.statusbar.SignalClusterView;
134import com.android.systemui.statusbar.SpeedBumpView;
135import com.android.systemui.statusbar.StatusBarIconView;
136import com.android.systemui.statusbar.StatusBarState;
137import com.android.systemui.statusbar.policy.AccessibilityController;
138import com.android.systemui.statusbar.policy.BatteryController;
139import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
140import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
141import com.android.systemui.statusbar.policy.BrightnessMirrorController;
142import com.android.systemui.statusbar.policy.CastControllerImpl;
143import com.android.systemui.statusbar.policy.FlashlightController;
144import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
145import com.android.systemui.statusbar.policy.HotspotControllerImpl;
146import com.android.systemui.statusbar.policy.KeyButtonView;
147import com.android.systemui.statusbar.policy.KeyguardMonitor;
148import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
149import com.android.systemui.statusbar.policy.LocationControllerImpl;
150import com.android.systemui.statusbar.policy.NetworkControllerImpl;
151import com.android.systemui.statusbar.policy.NextAlarmController;
152import com.android.systemui.statusbar.policy.PreviewInflater;
153import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
154import com.android.systemui.statusbar.policy.SecurityControllerImpl;
155import com.android.systemui.statusbar.policy.UserInfoController;
156import com.android.systemui.statusbar.policy.UserSwitcherController;
157import com.android.systemui.statusbar.policy.ZenModeController;
158import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
159import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
160import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
161import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
162import com.android.systemui.volume.VolumeComponent;
163
164import java.io.FileDescriptor;
165import java.io.PrintWriter;
166import java.util.ArrayList;
167import java.util.Collection;
168import java.util.Collections;
169import java.util.List;
170
171public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
172        DragDownHelper.DragDownCallback, ActivityStarter {
173    static final String TAG = "PhoneStatusBar";
174    public static final boolean DEBUG = BaseStatusBar.DEBUG;
175    public static final boolean SPEW = false;
176    public static final boolean DUMPTRUCK = true; // extra dumpsys info
177    public static final boolean DEBUG_GESTURES = false;
178    public static final boolean DEBUG_MEDIA = false;
179    public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
180
181    public static final boolean DEBUG_WINDOW_STATE = false;
182
183    // additional instrumentation for testing purposes; intended to be left on during development
184    public static final boolean CHATTY = DEBUG;
185
186    public static final String ACTION_STATUSBAR_START
187            = "com.android.internal.policy.statusbar.START";
188
189    public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
190
191    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
192    private static final int MSG_CLOSE_PANELS = 1001;
193    private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
194    // 1020-1040 reserved for BaseStatusBar
195
196    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
197
198    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
199    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
200
201    private static final int STATUS_OR_NAV_TRANSIENT =
202            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
203    private static final long AUTOHIDE_TIMEOUT_MS = 3000;
204
205    /** The minimum delay in ms between reports of notification visibility. */
206    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
207
208    /**
209     * The delay to reset the hint text when the hint animation is finished running.
210     */
211    private static final int HINT_RESET_DELAY_MS = 1200;
212
213    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
214            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
215            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
216            .build();
217
218    public static final int FADE_KEYGUARD_START_DELAY = 100;
219    public static final int FADE_KEYGUARD_DURATION = 300;
220
221    /** Allow some time inbetween the long press for back and recents. */
222    private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
223
224    PhoneStatusBarPolicy mIconPolicy;
225
226    // These are no longer handled by the policy, because we need custom strategies for them
227    BluetoothControllerImpl mBluetoothController;
228    SecurityControllerImpl mSecurityController;
229    BatteryController mBatteryController;
230    LocationControllerImpl mLocationController;
231    NetworkControllerImpl mNetworkController;
232    HotspotControllerImpl mHotspotController;
233    RotationLockControllerImpl mRotationLockController;
234    UserInfoController mUserInfoController;
235    ZenModeController mZenModeController;
236    CastControllerImpl mCastController;
237    VolumeComponent mVolumeComponent;
238    KeyguardUserSwitcher mKeyguardUserSwitcher;
239    FlashlightController mFlashlightController;
240    UserSwitcherController mUserSwitcherController;
241    NextAlarmController mNextAlarmController;
242    KeyguardMonitor mKeyguardMonitor;
243    BrightnessMirrorController mBrightnessMirrorController;
244    AccessibilityController mAccessibilityController;
245
246    int mNaturalBarHeight = -1;
247    int mIconSize = -1;
248    int mIconHPadding = -1;
249    Display mDisplay;
250    Point mCurrentDisplaySize = new Point();
251
252    StatusBarWindowView mStatusBarWindow;
253    PhoneStatusBarView mStatusBarView;
254    private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
255    private StatusBarWindowManager mStatusBarWindowManager;
256    private UnlockMethodCache mUnlockMethodCache;
257    private DozeServiceHost mDozeServiceHost;
258
259    int mPixelFormat;
260    Object mQueueLock = new Object();
261
262    // viewgroup containing the normal contents of the statusbar
263    LinearLayout mStatusBarContents;
264
265    // right-hand icons
266    LinearLayout mSystemIconArea;
267    LinearLayout mSystemIcons;
268
269    // left-hand icons
270    LinearLayout mStatusIcons;
271    LinearLayout mStatusIconsKeyguard;
272
273    // the icons themselves
274    IconMerger mNotificationIcons;
275    View mNotificationIconArea;
276
277    // [+>
278    View mMoreIcon;
279
280    // expanded notifications
281    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
282    View mExpandedContents;
283    int mNotificationPanelGravity;
284    int mNotificationPanelMarginBottomPx;
285    float mNotificationPanelMinHeightFrac;
286    TextView mNotificationPanelDebugText;
287
288    // settings
289    View mFlipSettingsView;
290    private QSPanel mQSPanel;
291
292    // top bar
293    StatusBarHeaderView mHeader;
294    KeyguardStatusBarView mKeyguardStatusBar;
295    View mKeyguardStatusView;
296    KeyguardBottomAreaView mKeyguardBottomArea;
297    boolean mLeaveOpenOnKeyguardHide;
298    KeyguardIndicationController mKeyguardIndicationController;
299
300    private boolean mKeyguardFadingAway;
301    private long mKeyguardFadingAwayDelay;
302    private long mKeyguardFadingAwayDuration;
303
304    int mKeyguardMaxNotificationCount;
305
306    // carrier/wifi label
307    private TextView mCarrierLabel;
308    private boolean mCarrierLabelVisible = false;
309    private int mCarrierLabelHeight;
310    private int mStatusBarHeaderHeight;
311
312    private boolean mShowCarrierInPanel = false;
313
314    // position
315    int[] mPositionTmp = new int[2];
316    boolean mExpandedVisible;
317
318    // on-screen navigation buttons
319    private NavigationBarView mNavigationBarView = null;
320    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
321
322    // the tracker view
323    int mTrackingPosition; // the position of the top of the tracking view.
324
325    // ticker
326    private boolean mTickerEnabled;
327    private Ticker mTicker;
328    private View mTickerView;
329    private boolean mTicking;
330
331    // Tracking finger for opening/closing.
332    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
333    boolean mTracking;
334    VelocityTracker mVelocityTracker;
335
336    int[] mAbsPos = new int[2];
337    ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
338
339    // for disabling the status bar
340    int mDisabled = 0;
341
342    // tracking calls to View.setSystemUiVisibility()
343    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
344
345    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
346
347    // XXX: gesture research
348    private final GestureRecorder mGestureRec = DEBUG_GESTURES
349        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
350        : null;
351
352    private int mNavigationIconHints = 0;
353
354    // ensure quick settings is disabled until the current user makes it through the setup wizard
355    private boolean mUserSetup = false;
356    private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
357        @Override
358        public void onChange(boolean selfChange) {
359            final boolean userSetup = 0 != Settings.Secure.getIntForUser(
360                    mContext.getContentResolver(),
361                    Settings.Secure.USER_SETUP_COMPLETE,
362                    0 /*default */,
363                    mCurrentUserId);
364            if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
365                    "selfChange=%s userSetup=%s mUserSetup=%s",
366                    selfChange, userSetup, mUserSetup));
367
368            if (userSetup != mUserSetup) {
369                mUserSetup = userSetup;
370                if (!mUserSetup && mStatusBarView != null)
371                    animateCollapseQuickSettings();
372            }
373        }
374    };
375
376    final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
377        @Override
378        public void onChange(boolean selfChange) {
379            boolean wasUsing = mUseHeadsUp;
380            mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
381                    && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
382                    mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
383                    Settings.Global.HEADS_UP_OFF);
384            mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
385                    mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
386            Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
387            if (wasUsing != mUseHeadsUp) {
388                if (!mUseHeadsUp) {
389                    Log.d(TAG, "dismissing any existing heads up notification on disable event");
390                    setHeadsUpVisibility(false);
391                    mHeadsUpNotificationView.release();
392                    removeHeadsUpView();
393                } else {
394                    addHeadsUpView();
395                }
396            }
397        }
398    };
399
400    private int mInteractingWindows;
401    private boolean mAutohideSuspended;
402    private int mStatusBarMode;
403    private int mNavigationBarMode;
404    private Boolean mScreenOn;
405
406    private ViewMediatorCallback mKeyguardViewMediatorCallback;
407    private ScrimController mScrimController;
408
409    private final Runnable mAutohide = new Runnable() {
410        @Override
411        public void run() {
412            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
413            if (mSystemUiVisibility != requested) {
414                notifyUiVisibilityChanged(requested);
415            }
416        }};
417
418    private boolean mVisible;
419    private boolean mWaitingForKeyguardExit;
420    private boolean mDozing;
421
422    private Interpolator mLinearOutSlowIn;
423    private Interpolator mLinearInterpolator = new LinearInterpolator();
424    private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
425    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
426    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
427
428    private FrameLayout mBackdrop;
429    private ImageView mBackdropFront, mBackdropBack;
430
431    private MediaSessionManager mMediaSessionManager;
432    private MediaController mMediaController;
433    private String mMediaNotificationKey;
434    private MediaMetadata mMediaMetadata;
435    private MediaController.Callback mMediaListener
436            = new MediaController.Callback() {
437        @Override
438        public void onPlaybackStateChanged(PlaybackState state) {
439            super.onPlaybackStateChanged(state);
440            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
441        }
442
443        @Override
444        public void onMetadataChanged(MediaMetadata metadata) {
445            super.onMetadataChanged(metadata);
446            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
447            mMediaMetadata = metadata;
448            updateMediaMetaData(true);
449        }
450    };
451
452    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
453            new OnChildLocationsChangedListener() {
454        @Override
455        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
456            userActivity();
457        }
458    };
459
460    private int mDisabledUnmodified;
461
462    /** Keys of notifications currently visible to the user. */
463    private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
464    private long mLastVisibilityReportUptimeMs;
465
466    private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
467
468    private int mDrawCount;
469    private Runnable mLaunchTransitionEndRunnable;
470    private boolean mLaunchTransitionFadingAway;
471    private ExpandableNotificationRow mDraggedDownRow;
472
473    private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
474            | ViewState.LOCATION_TOP_STACK_PEEKING
475            | ViewState.LOCATION_MAIN_AREA
476            | ViewState.LOCATION_BOTTOM_STACK_PEEKING;
477
478    private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
479            new OnChildLocationsChangedListener() {
480                @Override
481                public void onChildLocationsChanged(
482                        NotificationStackScrollLayout stackScrollLayout) {
483                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
484                        // Visibilities will be reported when the existing
485                        // callback is executed.
486                        return;
487                    }
488                    // Calculate when we're allowed to run the visibility
489                    // reporter. Note that this timestamp might already have
490                    // passed. That's OK, the callback will just be executed
491                    // ASAP.
492                    long nextReportUptimeMs =
493                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
494                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
495                }
496            };
497
498    // Tracks notifications currently visible in mNotificationStackScroller and
499    // emits visibility events via NoMan on changes.
500    private final Runnable mVisibilityReporter = new Runnable() {
501        private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>();
502        private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>();
503
504        @Override
505        public void run() {
506            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
507
508            // 1. Loop over mNotificationData entries:
509            //   A. Keep list of visible notifications.
510            //   B. Keep list of previously hidden, now visible notifications.
511            // 2. Compute no-longer visible notifications by removing currently
512            //    visible notifications from the set of previously visible
513            //    notifications.
514            // 3. Report newly visible and no-longer visible notifications.
515            // 4. Keep currently visible notifications for next report.
516            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
517            int N = activeNotifications.size();
518            for (int i = 0; i < N; i++) {
519                Entry entry = activeNotifications.get(i);
520                String key = entry.notification.getKey();
521                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key);
522                boolean currentlyVisible =
523                        (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0;
524                if (currentlyVisible) {
525                    // Build new set of visible notifications.
526                    mTmpCurrentlyVisibleNotifications.add(key);
527                }
528                if (!previouslyVisible && currentlyVisible) {
529                    mTmpNewlyVisibleNotifications.add(key);
530                }
531            }
532            ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications;
533            noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
534
535            logNotificationVisibilityChanges(
536                    mTmpNewlyVisibleNotifications, noLongerVisibleNotifications);
537
538            mCurrentlyVisibleNotifications.clear();
539            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
540
541            mTmpNewlyVisibleNotifications.clear();
542            mTmpCurrentlyVisibleNotifications.clear();
543        }
544    };
545
546    private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
547        @Override
548        public void onClick(View v) {
549            goToLockedShade(null);
550        }
551    };
552
553    @Override
554    public void start() {
555        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
556                .getDefaultDisplay();
557        updateDisplaySize();
558        super.start(); // calls createAndAddWindows()
559
560        mMediaSessionManager
561                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
562        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
563        // in session state
564
565        addNavigationBar();
566
567        // Lastly, call to the icon policy to install/update all the icons.
568        mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController);
569        mSettingsObserver.onChange(false); // set up
570
571        mHeadsUpObserver.onChange(true); // set up
572        if (ENABLE_HEADS_UP) {
573            mContext.getContentResolver().registerContentObserver(
574                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
575                    mHeadsUpObserver);
576            mContext.getContentResolver().registerContentObserver(
577                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
578                    mHeadsUpObserver);
579        }
580        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
581        startKeyguard();
582
583        mDozeServiceHost = new DozeServiceHost();
584        putComponent(DozeService.Host.class, mDozeServiceHost);
585        putComponent(PhoneStatusBar.class, this);
586
587        setControllerUsers();
588
589        notifyUserAboutHiddenNotifications();
590    }
591
592    // ================================================================================
593    // Constructing the view
594    // ================================================================================
595    protected PhoneStatusBarView makeStatusBarView() {
596        final Context context = mContext;
597
598        Resources res = context.getResources();
599
600        updateDisplaySize(); // populates mDisplayMetrics
601        updateResources();
602
603        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
604
605        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
606                R.layout.super_status_bar, null);
607        mStatusBarWindow.mService = this;
608        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
609            @Override
610            public boolean onTouch(View v, MotionEvent event) {
611                checkUserAutohide(v, event);
612                if (event.getAction() == MotionEvent.ACTION_DOWN) {
613                    if (mExpandedVisible) {
614                        animateCollapsePanels();
615                    }
616                }
617                return mStatusBarWindow.onTouchEvent(event);
618            }});
619
620        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
621        mStatusBarView.setBar(this);
622
623        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
624        mStatusBarView.setPanelHolder(holder);
625
626        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
627                R.id.notification_panel);
628        mNotificationPanel.setStatusBar(this);
629
630        if (!ActivityManager.isHighEndGfx()) {
631            mStatusBarWindow.setBackground(null);
632            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
633                    R.color.notification_panel_solid_background)));
634        }
635        if (ENABLE_HEADS_UP) {
636            mHeadsUpNotificationView =
637                    (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
638            mHeadsUpNotificationView.setVisibility(View.GONE);
639            mHeadsUpNotificationView.setBar(this);
640        }
641        if (MULTIUSER_DEBUG) {
642            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
643                    R.id.header_debug_info);
644            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
645        }
646
647        updateShowSearchHoldoff();
648
649        try {
650            boolean showNav = mWindowManagerService.hasNavigationBar();
651            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
652            if (showNav) {
653                mNavigationBarView =
654                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
655
656                mNavigationBarView.setDisabledFlags(mDisabled);
657                mNavigationBarView.setBar(this);
658                mNavigationBarView.setOnVerticalChangedListener(
659                        new NavigationBarView.OnVerticalChangedListener() {
660                    @Override
661                    public void onVerticalChanged(boolean isVertical) {
662                        if (mSearchPanelView != null) {
663                            mSearchPanelView.setHorizontal(isVertical);
664                        }
665                        mNotificationPanel.setQsScrimEnabled(!isVertical);
666                    }
667                });
668                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
669                    @Override
670                    public boolean onTouch(View v, MotionEvent event) {
671                        checkUserAutohide(v, event);
672                        return false;
673                    }});
674            }
675        } catch (RemoteException ex) {
676            // no window manager? good luck with that
677        }
678
679        // figure out which pixel-format to use for the status bar.
680        mPixelFormat = PixelFormat.OPAQUE;
681
682        mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
683        mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
684        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
685        mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
686        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
687        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
688        mNotificationIcons.setOverflowIndicator(mMoreIcon);
689        mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
690
691        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
692                R.id.notification_stack_scroller);
693        mStackScroller.setLongPressListener(getNotificationLongClicker());
694        mStackScroller.setPhoneStatusBar(this);
695
696        mKeyguardIconOverflowContainer =
697                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
698                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
699        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
700        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
701        mStackScroller.addView(mKeyguardIconOverflowContainer);
702
703        SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
704                        R.layout.status_bar_notification_speed_bump, mStackScroller, false);
705        mStackScroller.setSpeedBumpView(speedBump);
706        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
707                R.layout.status_bar_no_notifications, mStackScroller, false);
708        mStackScroller.setEmptyShadeView(mEmptyShadeView);
709        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
710                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
711        mDismissView.setOnButtonClickListener(new View.OnClickListener() {
712            @Override
713            public void onClick(View v) {
714                clearAllNotifications();
715            }
716        });
717        mStackScroller.setDismissView(mDismissView);
718        mExpandedContents = mStackScroller;
719
720        mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
721                mStatusBarWindow.findViewById(R.id.scrim_in_front));
722        mStatusBarView.setScrimController(mScrimController);
723
724        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
725        mHeader.setActivityStarter(this);
726        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
727        mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
728        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
729        mKeyguardBottomArea =
730                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
731        mKeyguardBottomArea.setActivityStarter(this);
732        mKeyguardIndicationController = new KeyguardIndicationController(mContext,
733                (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
734                        R.id.keyguard_indication_text));
735        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
736
737        mTickerEnabled = res.getBoolean(R.bool.enable_ticker);
738        if (mTickerEnabled) {
739            final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub);
740            if (tickerStub != null) {
741                mTickerView = tickerStub.inflate();
742                mTicker = new MyTicker(context, mStatusBarView);
743
744                TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText);
745                tickerView.mTicker = mTicker;
746            }
747        }
748
749        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
750
751        // set the inital view visibility
752        setAreThereNotifications();
753
754        // Other icons
755        mLocationController = new LocationControllerImpl(mContext); // will post a notification
756        mBatteryController = new BatteryController(mContext);
757        mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
758            @Override
759            public void onPowerSaveChanged() {
760                mHandler.post(mCheckBarModes);
761                if (mDozeServiceHost != null) {
762                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
763                }
764            }
765            @Override
766            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
767                // noop
768            }
769        });
770        mNetworkController = new NetworkControllerImpl(mContext);
771        mHotspotController = new HotspotControllerImpl(mContext);
772        mBluetoothController = new BluetoothControllerImpl(mContext);
773        mSecurityController = new SecurityControllerImpl(mContext);
774        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
775            mRotationLockController = new RotationLockControllerImpl(mContext);
776        }
777        mUserInfoController = new UserInfoController(mContext);
778        mVolumeComponent = getComponent(VolumeComponent.class);
779        mZenModeController = mVolumeComponent.getZenController();
780        mCastController = new CastControllerImpl(mContext);
781        final SignalClusterView signalCluster =
782                (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
783        final SignalClusterView signalClusterKeyguard =
784                (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
785        final SignalClusterView signalClusterQs =
786                (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
787        mNetworkController.addSignalCluster(signalCluster);
788        mNetworkController.addSignalCluster(signalClusterKeyguard);
789        mNetworkController.addSignalCluster(signalClusterQs);
790        signalCluster.setSecurityController(mSecurityController);
791        signalCluster.setNetworkController(mNetworkController);
792        signalClusterKeyguard.setSecurityController(mSecurityController);
793        signalClusterKeyguard.setNetworkController(mNetworkController);
794        signalClusterQs.setSecurityController(mSecurityController);
795        signalClusterQs.setNetworkController(mNetworkController);
796        final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
797        if (isAPhone) {
798            mNetworkController.addEmergencyLabelView(mHeader);
799        }
800
801        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
802        mShowCarrierInPanel = (mCarrierLabel != null);
803        if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
804        if (mShowCarrierInPanel) {
805            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
806
807            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
808            // for other devices, we show whatever network is connected
809            if (mNetworkController.hasMobileDataFeature()) {
810                mNetworkController.addMobileLabelView(mCarrierLabel);
811            } else {
812                mNetworkController.addCombinedLabelView(mCarrierLabel);
813            }
814
815            // set up the dynamic hide/show of the label
816            // TODO: uncomment, handle this for the Stack scroller aswell
817//                ((NotificationRowLayout) mStackScroller)
818// .setOnSizeChangedListener(new OnSizeChangedListener() {
819//                @Override
820//                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
821//                    updateCarrierLabelVisibility(false);
822        }
823
824        mFlashlightController = new FlashlightController(mContext);
825        mKeyguardBottomArea.setFlashlightController(mFlashlightController);
826        mKeyguardBottomArea.setPhoneStatusBar(this);
827        mAccessibilityController = new AccessibilityController(mContext);
828        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
829        mNextAlarmController = new NextAlarmController(mContext);
830        mKeyguardMonitor = new KeyguardMonitor();
831        mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
832
833        mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
834                (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
835                mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
836
837
838        // Set up the quick settings tile panel
839        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
840        if (mQSPanel != null) {
841            final QSTileHost qsh = new QSTileHost(mContext, this,
842                    mBluetoothController, mLocationController, mRotationLockController,
843                    mNetworkController, mZenModeController, mHotspotController,
844                    mCastController, mFlashlightController,
845                    mUserSwitcherController, mKeyguardMonitor,
846                    mSecurityController);
847            mQSPanel.setHost(qsh);
848            mQSPanel.setTiles(qsh.getTiles());
849            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
850            mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
851            mHeader.setQSPanel(mQSPanel);
852            qsh.setCallback(new QSTileHost.Callback() {
853                @Override
854                public void onTilesChanged() {
855                    mQSPanel.setTiles(qsh.getTiles());
856                }
857            });
858        }
859
860        mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop);
861        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
862        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
863
864        // User info. Trigger first load.
865        mHeader.setUserInfoController(mUserInfoController);
866        mKeyguardStatusBar.setUserInfoController(mUserInfoController);
867        mUserInfoController.reloadUserInfo();
868
869        mHeader.setBatteryController(mBatteryController);
870        ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
871                mBatteryController);
872        mKeyguardStatusBar.setBatteryController(mBatteryController);
873        mHeader.setNextAlarmController(mNextAlarmController);
874
875        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
876        mBroadcastReceiver.onReceive(mContext,
877                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
878
879        // receive broadcasts
880        IntentFilter filter = new IntentFilter();
881        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
882        filter.addAction(Intent.ACTION_SCREEN_OFF);
883        filter.addAction(Intent.ACTION_SCREEN_ON);
884        if (DEBUG_MEDIA_FAKE_ARTWORK) {
885            filter.addAction("fake_artwork");
886        }
887        filter.addAction(ACTION_DEMO);
888        context.registerReceiver(mBroadcastReceiver, filter);
889
890        // listen for USER_SETUP_COMPLETE setting (per-user)
891        resetUserSetupObserver();
892
893        startGlyphRasterizeHack();
894        return mStatusBarView;
895    }
896
897    private void clearAllNotifications() {
898
899        // animate-swipe all dismissable notifications, then animate the shade closed
900        int numChildren = mStackScroller.getChildCount();
901
902        final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
903        for (int i = 0; i < numChildren; i++) {
904            final View child = mStackScroller.getChildAt(i);
905            if (mStackScroller.canChildBeDismissed(child)) {
906                if (child.getVisibility() == View.VISIBLE) {
907                    viewsToHide.add(child);
908                }
909            }
910        }
911        if (viewsToHide.isEmpty()) {
912            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
913            return;
914        }
915
916        addPostCollapseAction(new Runnable() {
917            @Override
918            public void run() {
919                try {
920                    mBarService.onClearAllNotifications(mCurrentUserId);
921                } catch (Exception ex) { }
922            }
923        });
924
925        performDismissAllAnimations(viewsToHide);
926
927    }
928
929    private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
930        Runnable animationFinishAction = new Runnable() {
931            @Override
932            public void run() {
933                mStackScroller.post(new Runnable() {
934                    @Override
935                    public void run() {
936                        mStackScroller.setDismissAllInProgress(false);
937                    }
938                });
939                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
940            }
941        };
942
943        // let's disable our normal animations
944        mStackScroller.setDismissAllInProgress(true);
945
946        // Decrease the delay for every row we animate to give the sense of
947        // accelerating the swipes
948        int rowDelayDecrement = 10;
949        int currentDelay = 140;
950        int totalDelay = 0;
951        int numItems = hideAnimatedList.size();
952        for (int i = 0; i < numItems; i++) {
953            View view = hideAnimatedList.get(i);
954            Runnable endRunnable = null;
955            if (i == numItems - 1) {
956                endRunnable = animationFinishAction;
957            }
958            mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260);
959            currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
960            totalDelay += currentDelay;
961        }
962    }
963
964    /**
965     * Hack to improve glyph rasterization for scaled text views.
966     */
967    private void startGlyphRasterizeHack() {
968        mStatusBarView.getViewTreeObserver().addOnPreDrawListener(
969                new ViewTreeObserver.OnPreDrawListener() {
970            @Override
971            public boolean onPreDraw() {
972                if (mDrawCount == 1) {
973                    mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this);
974                    HardwareCanvas.setProperty("extraRasterBucket",
975                            Float.toString(StackScrollAlgorithm.DIMMED_SCALE));
976                    HardwareCanvas.setProperty("extraRasterBucket", Float.toString(
977                            mContext.getResources().getDimensionPixelSize(
978                                    R.dimen.qs_time_collapsed_size)
979                            / mContext.getResources().getDimensionPixelSize(
980                                    R.dimen.qs_time_expanded_size)));
981                }
982                mDrawCount++;
983                return true;
984            }
985        });
986    }
987
988    @Override
989    protected void setZenMode(int mode) {
990        super.setZenMode(mode);
991        if (mIconPolicy != null) {
992            mIconPolicy.setZenMode(mode);
993        }
994    }
995
996    private void startKeyguard() {
997        KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
998        mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
999                mStatusBarWindow, mStatusBarWindowManager, mScrimController);
1000        mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
1001    }
1002
1003    @Override
1004    protected View getStatusBarView() {
1005        return mStatusBarView;
1006    }
1007
1008    public StatusBarWindowView getStatusBarWindow() {
1009        return mStatusBarWindow;
1010    }
1011
1012    @Override
1013    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
1014        boolean opaque = false;
1015        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1016                LayoutParams.MATCH_PARENT,
1017                LayoutParams.MATCH_PARENT,
1018                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
1019                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1020                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1021                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1022                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
1023        if (ActivityManager.isHighEndGfx()) {
1024            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1025        }
1026        lp.gravity = Gravity.BOTTOM | Gravity.START;
1027        lp.setTitle("SearchPanel");
1028        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
1029        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
1030        return lp;
1031    }
1032
1033    @Override
1034    protected void updateSearchPanel() {
1035        super.updateSearchPanel();
1036        if (mNavigationBarView != null) {
1037            mNavigationBarView.setDelegateView(mSearchPanelView);
1038        }
1039    }
1040
1041    @Override
1042    public void showSearchPanel() {
1043        super.showSearchPanel();
1044        mHandler.removeCallbacks(mShowSearchPanel);
1045
1046        // we want to freeze the sysui state wherever it is
1047        mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
1048
1049        if (mNavigationBarView != null) {
1050            WindowManager.LayoutParams lp =
1051                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1052            lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1053            mWindowManager.updateViewLayout(mNavigationBarView, lp);
1054        }
1055    }
1056
1057    @Override
1058    public void hideSearchPanel() {
1059        super.hideSearchPanel();
1060        if (mNavigationBarView != null) {
1061            WindowManager.LayoutParams lp =
1062                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1063            lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1064            mWindowManager.updateViewLayout(mNavigationBarView, lp);
1065        }
1066    }
1067
1068    public int getStatusBarHeight() {
1069        if (mNaturalBarHeight < 0) {
1070            final Resources res = mContext.getResources();
1071            mNaturalBarHeight =
1072                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
1073        }
1074        return mNaturalBarHeight;
1075    }
1076
1077    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
1078        public void onClick(View v) {
1079            awakenDreams();
1080            toggleRecentApps();
1081        }
1082    };
1083
1084    private long mLastLockToAppLongPress;
1085    private View.OnLongClickListener mLongPressBackRecentsListener =
1086            new View.OnLongClickListener() {
1087        @Override
1088        public boolean onLongClick(View v) {
1089            handleLongPressBackRecents(v);
1090            return true;
1091        }
1092    };
1093
1094    private int mShowSearchHoldoff = 0;
1095    private Runnable mShowSearchPanel = new Runnable() {
1096        public void run() {
1097            showSearchPanel();
1098            awakenDreams();
1099        }
1100    };
1101
1102    View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
1103        public boolean onTouch(View v, MotionEvent event) {
1104            switch(event.getAction()) {
1105                case MotionEvent.ACTION_DOWN:
1106                if (!shouldDisableNavbarGestures()) {
1107                    mHandler.removeCallbacks(mShowSearchPanel);
1108                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
1109                }
1110            break;
1111
1112            case MotionEvent.ACTION_UP:
1113            case MotionEvent.ACTION_CANCEL:
1114                mHandler.removeCallbacks(mShowSearchPanel);
1115                awakenDreams();
1116            break;
1117        }
1118        return false;
1119        }
1120    };
1121
1122    private void awakenDreams() {
1123        if (mDreamManager != null) {
1124            try {
1125                mDreamManager.awaken();
1126            } catch (RemoteException e) {
1127                // fine, stay asleep then
1128            }
1129        }
1130    }
1131
1132    private void prepareNavigationBarView() {
1133        mNavigationBarView.reorient();
1134
1135        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
1136        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
1137        mNavigationBarView.getRecentsButton().setLongClickable(true);
1138        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
1139        mNavigationBarView.getBackButton().setLongClickable(true);
1140        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
1141        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
1142        updateSearchPanel();
1143    }
1144
1145    // For small-screen devices (read: phones) that lack hardware navigation buttons
1146    private void addNavigationBar() {
1147        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
1148        if (mNavigationBarView == null) return;
1149
1150        prepareNavigationBarView();
1151
1152        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
1153    }
1154
1155    private void repositionNavigationBar() {
1156        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
1157
1158        prepareNavigationBarView();
1159
1160        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
1161    }
1162
1163    private void notifyNavigationBarScreenOn(boolean screenOn) {
1164        if (mNavigationBarView == null) return;
1165        mNavigationBarView.notifyScreenOn(screenOn);
1166    }
1167
1168    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
1169        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1170                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
1171                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
1172                    0
1173                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1174                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1175                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1176                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1177                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1178                PixelFormat.TRANSLUCENT);
1179        // this will allow the navbar to run in an overlay on devices that support this
1180        if (ActivityManager.isHighEndGfx()) {
1181            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1182        }
1183
1184        lp.setTitle("NavigationBar");
1185        lp.windowAnimations = 0;
1186        return lp;
1187    }
1188
1189    private void addHeadsUpView() {
1190        int headsUpHeight = mContext.getResources()
1191                .getDimensionPixelSize(R.dimen.heads_up_window_height);
1192        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1193                LayoutParams.MATCH_PARENT, headsUpHeight,
1194                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
1195                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1196                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1197                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1198                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1199                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1200                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1201                PixelFormat.TRANSLUCENT);
1202        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1203        lp.gravity = Gravity.TOP;
1204        lp.setTitle("Heads Up");
1205        lp.packageName = mContext.getPackageName();
1206        lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
1207
1208        mWindowManager.addView(mHeadsUpNotificationView, lp);
1209    }
1210
1211    private void removeHeadsUpView() {
1212        mWindowManager.removeView(mHeadsUpNotificationView);
1213    }
1214
1215    public void refreshAllStatusBarIcons() {
1216        refreshAllIconsForLayout(mStatusIcons);
1217        refreshAllIconsForLayout(mStatusIconsKeyguard);
1218        refreshAllIconsForLayout(mNotificationIcons);
1219    }
1220
1221    private void refreshAllIconsForLayout(LinearLayout ll) {
1222        final int count = ll.getChildCount();
1223        for (int n = 0; n < count; n++) {
1224            View child = ll.getChildAt(n);
1225            if (child instanceof StatusBarIconView) {
1226                ((StatusBarIconView) child).updateDrawable();
1227            }
1228        }
1229    }
1230
1231    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
1232        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1233                + " icon=" + icon);
1234        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
1235        view.set(icon);
1236        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
1237                LayoutParams.WRAP_CONTENT, mIconSize));
1238        view = new StatusBarIconView(mContext, slot, null);
1239        view.set(icon);
1240        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
1241                LayoutParams.WRAP_CONTENT, mIconSize));
1242    }
1243
1244    public void updateIcon(String slot, int index, int viewIndex,
1245            StatusBarIcon old, StatusBarIcon icon) {
1246        if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1247                + " old=" + old + " icon=" + icon);
1248        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
1249        view.set(icon);
1250        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
1251        view.set(icon);
1252    }
1253
1254    public void removeIcon(String slot, int index, int viewIndex) {
1255        if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
1256        mStatusIcons.removeViewAt(viewIndex);
1257        mStatusIconsKeyguard.removeViewAt(viewIndex);
1258    }
1259
1260    public UserHandle getCurrentUserHandle() {
1261        return new UserHandle(mCurrentUserId);
1262    }
1263
1264    @Override
1265    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
1266        if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
1267        if (mUseHeadsUp && shouldInterrupt(notification)) {
1268            if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
1269            Entry interruptionCandidate = new Entry(notification, null);
1270            ViewGroup holder = mHeadsUpNotificationView.getHolder();
1271            if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
1272                // 1. Populate mHeadsUpNotificationView
1273                mHeadsUpNotificationView.showNotification(interruptionCandidate);
1274
1275                // do not show the notification in the shade, yet.
1276                return;
1277            }
1278        }
1279
1280        Entry shadeEntry = createNotificationViews(notification);
1281        if (shadeEntry == null) {
1282            return;
1283        }
1284
1285        if (notification.getNotification().fullScreenIntent != null) {
1286            // Stop screensaver if the notification has a full-screen intent.
1287            // (like an incoming phone call)
1288            awakenDreams();
1289
1290            // not immersive & a full-screen alert should be shown
1291            if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1292            try {
1293                notification.getNotification().fullScreenIntent.send();
1294            } catch (PendingIntent.CanceledException e) {
1295            }
1296        } else {
1297            // usual case: status bar visible & not immersive
1298
1299            // show the ticker if there isn't already a heads up
1300            if (mHeadsUpNotificationView.getEntry() == null) {
1301                tick(notification, true);
1302            }
1303        }
1304        addNotificationViews(shadeEntry, ranking);
1305        // Recalculate the position of the sliding windows and the titles.
1306        setAreThereNotifications();
1307        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1308    }
1309
1310    public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
1311        NotificationData.Entry shadeEntry = createNotificationViews(notification);
1312        if (shadeEntry == null) {
1313            return;
1314        }
1315        shadeEntry.setInterruption();
1316
1317        addNotificationViews(shadeEntry, null);
1318        // Recalculate the position of the sliding windows and the titles.
1319        setAreThereNotifications();
1320        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1321    }
1322
1323    @Override
1324    public void resetHeadsUpDecayTimer() {
1325        mHandler.removeMessages(MSG_DECAY_HEADS_UP);
1326        if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
1327                && mHeadsUpNotificationView.isClearable()) {
1328            mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
1329        }
1330    }
1331
1332    @Override
1333    public void scheduleHeadsUpOpen() {
1334        mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
1335    }
1336
1337    @Override
1338    public void scheduleHeadsUpClose() {
1339        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1340    }
1341
1342    @Override
1343    public void scheduleHeadsUpEscalation() {
1344        mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1345    }
1346
1347    @Override
1348    protected void updateNotificationRanking(RankingMap ranking) {
1349        mNotificationData.updateRanking(ranking);
1350        updateNotifications();
1351    }
1352
1353    @Override
1354    public void removeNotification(String key, RankingMap ranking) {
1355        if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
1356                && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
1357            mHeadsUpNotificationView.clear();
1358        }
1359
1360        StatusBarNotification old = removeNotificationViews(key, ranking);
1361        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1362
1363        if (old != null) {
1364            // Cancel the ticker if it's still running
1365            if (mTickerEnabled) {
1366                mTicker.removeEntry(old);
1367            }
1368
1369            // Recalculate the position of the sliding windows and the titles.
1370            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1371
1372            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
1373                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
1374                if (mState == StatusBarState.SHADE) {
1375                    animateCollapsePanels();
1376                } else if (mState == StatusBarState.SHADE_LOCKED) {
1377                    goToKeyguard();
1378                }
1379            }
1380        }
1381        setAreThereNotifications();
1382    }
1383
1384    @Override
1385    protected void refreshLayout(int layoutDirection) {
1386        if (mNavigationBarView != null) {
1387            mNavigationBarView.setLayoutDirection(layoutDirection);
1388        }
1389        refreshAllStatusBarIcons();
1390    }
1391
1392    private void updateShowSearchHoldoff() {
1393        mShowSearchHoldoff = mContext.getResources().getInteger(
1394            R.integer.config_show_search_delay);
1395    }
1396
1397    private void updateNotificationShade() {
1398        if (mStackScroller == null) return;
1399
1400        // Do not modify the notifications during collapse.
1401        if (isCollapsing()) {
1402            addPostCollapseAction(new Runnable() {
1403                @Override
1404                public void run() {
1405                    updateNotificationShade();
1406                }
1407            });
1408            return;
1409        }
1410
1411        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1412        ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
1413        final int N = activeNotifications.size();
1414        for (int i=0; i<N; i++) {
1415            Entry ent = activeNotifications.get(i);
1416            int vis = ent.notification.getNotification().visibility;
1417
1418            // Display public version of the notification if we need to redact.
1419            final boolean hideSensitive =
1420                    !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
1421            boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
1422            boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
1423            boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
1424            boolean showingPublic = sensitive && isLockscreenPublicMode();
1425            ent.row.setSensitive(sensitive);
1426            if (ent.autoRedacted && ent.legacy) {
1427                // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
1428                // for legacy auto redacted notifications.
1429                if (showingPublic) {
1430                    ent.row.setShowingLegacyBackground(false);
1431                } else {
1432                    ent.row.setShowingLegacyBackground(true);
1433                }
1434            }
1435            toShow.add(ent.row);
1436        }
1437
1438        ArrayList<View> toRemove = new ArrayList<View>();
1439        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1440            View child = mStackScroller.getChildAt(i);
1441            if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1442                toRemove.add(child);
1443            }
1444        }
1445
1446        for (View remove : toRemove) {
1447            mStackScroller.removeView(remove);
1448        }
1449        for (int i=0; i<toShow.size(); i++) {
1450            View v = toShow.get(i);
1451            if (v.getParent() == null) {
1452                mStackScroller.addView(v);
1453            }
1454        }
1455
1456        // So after all this work notifications still aren't sorted correctly.
1457        // Let's do that now by advancing through toShow and mStackScroller in
1458        // lock-step, making sure mStackScroller matches what we see in toShow.
1459        int j = 0;
1460        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1461            View child = mStackScroller.getChildAt(i);
1462            if (!(child instanceof ExpandableNotificationRow)) {
1463                // We don't care about non-notification views.
1464                continue;
1465            }
1466
1467            if (child == toShow.get(j)) {
1468                // Everything is well, advance both lists.
1469                j++;
1470                continue;
1471            }
1472
1473            // Oops, wrong notification at this position. Put the right one
1474            // here and advance both lists.
1475            mStackScroller.changeViewPosition(toShow.get(j), i);
1476            j++;
1477        }
1478        updateRowStates();
1479        updateSpeedbump();
1480        updateClearAll();
1481        updateEmptyShadeView();
1482
1483        mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned());
1484        mShadeUpdates.check();
1485    }
1486
1487    private boolean packageHasVisibilityOverride(String key) {
1488        return mNotificationData.getVisibilityOverride(key)
1489                != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
1490    }
1491
1492    private void updateClearAll() {
1493        boolean showDismissView =
1494                mState != StatusBarState.KEYGUARD &&
1495                mNotificationData.hasActiveClearableNotifications();
1496        mStackScroller.updateDismissView(showDismissView);
1497    }
1498
1499    private void updateEmptyShadeView() {
1500        boolean showEmptyShade =
1501                mState != StatusBarState.KEYGUARD &&
1502                        mNotificationData.getActiveNotifications().size() == 0;
1503        mNotificationPanel.setShadeEmpty(showEmptyShade);
1504    }
1505
1506    private void updateSpeedbump() {
1507        int speedbumpIndex = -1;
1508        int currentIndex = 0;
1509        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1510        final int N = activeNotifications.size();
1511        for (int i = 0; i < N; i++) {
1512            Entry entry = activeNotifications.get(i);
1513            if (entry.row.getVisibility() != View.GONE &&
1514                    mNotificationData.isAmbient(entry.key)) {
1515                speedbumpIndex = currentIndex;
1516                break;
1517            }
1518            currentIndex++;
1519        }
1520        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
1521    }
1522
1523    @Override
1524    protected void updateNotifications() {
1525        // TODO: Move this into updateNotificationIcons()?
1526        if (mNotificationIcons == null) return;
1527
1528        mNotificationData.filterAndSort();
1529
1530        updateNotificationShade();
1531        updateNotificationIcons();
1532    }
1533
1534    private void updateNotificationIcons() {
1535        final LinearLayout.LayoutParams params
1536            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1537
1538        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1539        final int N = activeNotifications.size();
1540        ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
1541
1542        // Filter out notifications with low scores.
1543        for (int i = 0; i < N; i++) {
1544            Entry ent = activeNotifications.get(i);
1545            if (ent.notification.getScore() < HIDE_ICONS_BELOW_SCORE &&
1546                    !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
1547                continue;
1548            }
1549            toShow.add(ent.icon);
1550        }
1551
1552        if (DEBUG) {
1553            Log.d(TAG, "refreshing icons: " + toShow.size() +
1554                    " notifications, mNotificationIcons=" + mNotificationIcons);
1555        }
1556
1557        ArrayList<View> toRemove = new ArrayList<View>();
1558        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1559            View child = mNotificationIcons.getChildAt(i);
1560            if (!toShow.contains(child)) {
1561                toRemove.add(child);
1562            }
1563        }
1564
1565        final int toRemoveCount = toRemove.size();
1566        for (int i = 0; i < toRemoveCount; i++) {
1567            mNotificationIcons.removeView(toRemove.get(i));
1568        }
1569
1570        for (int i=0; i<toShow.size(); i++) {
1571            View v = toShow.get(i);
1572            if (v.getParent() == null) {
1573                mNotificationIcons.addView(v, i, params);
1574            }
1575        }
1576
1577        // Resort notification icons
1578        final int childCount = mNotificationIcons.getChildCount();
1579        for (int i = 0; i < childCount; i++) {
1580            View actual = mNotificationIcons.getChildAt(i);
1581            StatusBarIconView expected = toShow.get(i);
1582            if (actual == expected) {
1583                continue;
1584            }
1585            mNotificationIcons.removeView(expected);
1586            mNotificationIcons.addView(expected, i);
1587        }
1588    }
1589
1590    @Override
1591    protected void updateRowStates() {
1592        super.updateRowStates();
1593        mNotificationPanel.notifyVisibleChildrenChanged();
1594    }
1595
1596    protected void updateCarrierLabelVisibility(boolean force) {
1597        // TODO: Handle this for the notification stack scroller as well
1598        if (!mShowCarrierInPanel) return;
1599        // The idea here is to only show the carrier label when there is enough room to see it,
1600        // i.e. when there aren't enough notifications to fill the panel.
1601        if (SPEW) {
1602            Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
1603                    mStackScroller.getHeight(), mStackScroller.getHeight(),
1604                    mCarrierLabelHeight));
1605        }
1606
1607        // Emergency calls only is shown in the expanded header now.
1608        final boolean emergencyCallsShownElsewhere = true;
1609        final boolean makeVisible =
1610            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1611            && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
1612                    - mCarrierLabelHeight - mStatusBarHeaderHeight)
1613            && mStackScroller.getVisibility() == View.VISIBLE
1614            && mState != StatusBarState.KEYGUARD;
1615
1616        if (force || mCarrierLabelVisible != makeVisible) {
1617            mCarrierLabelVisible = makeVisible;
1618            if (DEBUG) {
1619                Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1620            }
1621            mCarrierLabel.animate().cancel();
1622            if (makeVisible) {
1623                mCarrierLabel.setVisibility(View.VISIBLE);
1624            }
1625            mCarrierLabel.animate()
1626                .alpha(makeVisible ? 1f : 0f)
1627                //.setStartDelay(makeVisible ? 500 : 0)
1628                //.setDuration(makeVisible ? 750 : 100)
1629                .setDuration(150)
1630                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1631                    @Override
1632                    public void onAnimationEnd(Animator animation) {
1633                        if (!mCarrierLabelVisible) { // race
1634                            mCarrierLabel.setVisibility(View.INVISIBLE);
1635                            mCarrierLabel.setAlpha(0f);
1636                        }
1637                    }
1638                })
1639                .start();
1640        }
1641    }
1642
1643    @Override
1644    protected void setAreThereNotifications() {
1645
1646        if (SPEW) {
1647            final boolean clearable = hasActiveNotifications() &&
1648                    mNotificationData.hasActiveClearableNotifications();
1649            Log.d(TAG, "setAreThereNotifications: N=" +
1650                    mNotificationData.getActiveNotifications().size() + " any=" +
1651                    hasActiveNotifications() + " clearable=" + clearable);
1652        }
1653
1654        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1655        final boolean showDot = hasActiveNotifications() && !areLightsOn();
1656        if (showDot != (nlo.getAlpha() == 1.0f)) {
1657            if (showDot) {
1658                nlo.setAlpha(0f);
1659                nlo.setVisibility(View.VISIBLE);
1660            }
1661            nlo.animate()
1662                .alpha(showDot?1:0)
1663                .setDuration(showDot?750:250)
1664                .setInterpolator(new AccelerateInterpolator(2.0f))
1665                .setListener(showDot ? null : new AnimatorListenerAdapter() {
1666                    @Override
1667                    public void onAnimationEnd(Animator _a) {
1668                        nlo.setVisibility(View.GONE);
1669                    }
1670                })
1671                .start();
1672        }
1673
1674        findAndUpdateMediaNotifications();
1675
1676        updateCarrierLabelVisibility(false);
1677    }
1678
1679    public void findAndUpdateMediaNotifications() {
1680        boolean metaDataChanged = false;
1681
1682        synchronized (mNotificationData) {
1683            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1684            final int N = activeNotifications.size();
1685            Entry mediaNotification = null;
1686            MediaController controller = null;
1687            for (int i = 0; i < N; i++) {
1688                final Entry entry = activeNotifications.get(i);
1689                if (isMediaNotification(entry)) {
1690                    final MediaSession.Token token = entry.notification.getNotification().extras
1691                            .getParcelable(Notification.EXTRA_MEDIA_SESSION);
1692                    if (token != null) {
1693                        controller = new MediaController(mContext, token);
1694                        if (controller != null) {
1695                            // we've got a live one, here
1696                            mediaNotification = entry;
1697                        }
1698                    }
1699                }
1700            }
1701
1702            if (mediaNotification == null) {
1703                // Still nothing? OK, let's just look for live media sessions and see if they match
1704                // one of our notifications. This will catch apps that aren't (yet!) using media
1705                // notifications.
1706
1707                if (mMediaSessionManager != null) {
1708                    final List<MediaController> sessions
1709                            = mMediaSessionManager.getActiveSessionsForUser(
1710                                    null,
1711                                    UserHandle.USER_ALL);
1712
1713                    for (MediaController aController : sessions) {
1714                        if (aController == null) continue;
1715                        final PlaybackState state = aController.getPlaybackState();
1716                        if (state == null) continue;
1717                        switch (state.getState()) {
1718                            case PlaybackState.STATE_STOPPED:
1719                            case PlaybackState.STATE_ERROR:
1720                                continue;
1721                            default:
1722                                // now to see if we have one like this
1723                                final String pkg = aController.getPackageName();
1724
1725                                for (int i = 0; i < N; i++) {
1726                                    final Entry entry = activeNotifications.get(i);
1727                                    if (entry.notification.getPackageName().equals(pkg)) {
1728                                        if (DEBUG_MEDIA) {
1729                                            Log.v(TAG, "DEBUG_MEDIA: found controller matching "
1730                                                + entry.notification.getKey());
1731                                        }
1732                                        controller = aController;
1733                                        mediaNotification = entry;
1734                                        break;
1735                                    }
1736                                }
1737                        }
1738                    }
1739                }
1740            }
1741
1742            if (controller != mMediaController) {
1743                // We have a new media session
1744
1745                if (mMediaController != null) {
1746                    // something old was playing
1747                    Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
1748                            + mMediaController);
1749                    mMediaController.unregisterCallback(mMediaListener);
1750                }
1751                mMediaController = controller;
1752
1753                if (mMediaController != null) {
1754                    mMediaController.registerCallback(mMediaListener);
1755                    mMediaMetadata = mMediaController.getMetadata();
1756                    if (DEBUG_MEDIA) {
1757                        Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
1758                                + mMediaMetadata);
1759                    }
1760
1761                    final String notificationKey = mediaNotification == null
1762                            ? null
1763                            : mediaNotification.notification.getKey();
1764
1765                    if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
1766                        // we have a new notification!
1767                        if (DEBUG_MEDIA) {
1768                            Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
1769                                    + notificationKey + " controller=" + controller);
1770                        }
1771                        mMediaNotificationKey = notificationKey;
1772                    }
1773                } else {
1774                    mMediaMetadata = null;
1775                    mMediaNotificationKey = null;
1776                }
1777
1778                metaDataChanged = true;
1779            } else {
1780                // Media session unchanged
1781
1782                if (DEBUG_MEDIA) {
1783                    Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
1784                }
1785            }
1786        }
1787
1788        updateMediaMetaData(metaDataChanged);
1789    }
1790
1791    /**
1792     * Hide the album artwork that is fading out and release its bitmap.
1793     */
1794    private Runnable mHideBackdropFront = new Runnable() {
1795        @Override
1796        public void run() {
1797            if (DEBUG_MEDIA) {
1798                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
1799            }
1800            mBackdropFront.setVisibility(View.INVISIBLE);
1801            mBackdropFront.animate().cancel();
1802            mBackdropFront.setImageDrawable(null);
1803        }
1804    };
1805
1806    /**
1807     * Refresh or remove lockscreen artwork from media metadata.
1808     */
1809    public void updateMediaMetaData(boolean metaDataChanged) {
1810        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
1811
1812        if (mBackdrop == null) return; // called too early
1813
1814        if (DEBUG_MEDIA) {
1815            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
1816                + " metadata=" + mMediaMetadata
1817                + " metaDataChanged=" + metaDataChanged
1818                + " state=" + mState);
1819        }
1820
1821        Bitmap artworkBitmap = null;
1822        if (mMediaMetadata != null) {
1823            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
1824            if (artworkBitmap == null) {
1825                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
1826                // might still be null
1827            }
1828        }
1829
1830        final boolean hasArtwork = artworkBitmap != null;
1831
1832        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
1833                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
1834            // time to show some art!
1835            if (mBackdrop.getVisibility() != View.VISIBLE) {
1836                mBackdrop.setVisibility(View.VISIBLE);
1837                mBackdrop.animate().alpha(1f);
1838                metaDataChanged = true;
1839                if (DEBUG_MEDIA) {
1840                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
1841                }
1842            }
1843            if (metaDataChanged) {
1844                if (mBackdropBack.getDrawable() != null) {
1845                    mBackdropFront.setImageDrawable(mBackdropBack.getDrawable());
1846                    mBackdropFront.setAlpha(1f);
1847                    mBackdropFront.setVisibility(View.VISIBLE);
1848                } else {
1849                    mBackdropFront.setVisibility(View.INVISIBLE);
1850                }
1851
1852                if (DEBUG_MEDIA_FAKE_ARTWORK) {
1853                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
1854                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
1855                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
1856                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
1857                } else {
1858                    mBackdropBack.setImageBitmap(artworkBitmap);
1859                }
1860
1861                if (mBackdropFront.getVisibility() == View.VISIBLE) {
1862                    if (DEBUG_MEDIA) {
1863                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
1864                                + mBackdropFront.getDrawable()
1865                                + " to "
1866                                + mBackdropBack.getDrawable());
1867                    }
1868                    mBackdropFront.animate()
1869                            .setDuration(250)
1870                            .alpha(0f).withEndAction(mHideBackdropFront);
1871                }
1872            }
1873        } else {
1874            // need to hide the album art, either because we are unlocked or because
1875            // the metadata isn't there to support it
1876            if (mBackdrop.getVisibility() != View.GONE) {
1877                if (DEBUG_MEDIA) {
1878                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
1879                }
1880                mBackdrop.animate()
1881                        .alpha(0f)
1882                        .setInterpolator(mBackdropInterpolator)
1883                        .setDuration(300)
1884                        .setStartDelay(0)
1885                        .withEndAction(new Runnable() {
1886                            @Override
1887                            public void run() {
1888                                mBackdrop.setVisibility(View.GONE);
1889                                mBackdropFront.animate().cancel();
1890                                mBackdropBack.animate().cancel();
1891                                mHandler.post(mHideBackdropFront);
1892                            }
1893                        });
1894                if (mKeyguardFadingAway) {
1895                    mBackdrop.animate()
1896
1897                            // Make it disappear faster, as the focus should be on the activity behind.
1898                            .setDuration(mKeyguardFadingAwayDuration / 2)
1899                            .setStartDelay(mKeyguardFadingAwayDelay)
1900                            .setInterpolator(mLinearInterpolator)
1901                            .start();
1902                }
1903            }
1904        }
1905    }
1906
1907    public void showClock(boolean show) {
1908        if (mStatusBarView == null) return;
1909        View clock = mStatusBarView.findViewById(R.id.clock);
1910        if (clock != null) {
1911            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1912        }
1913    }
1914
1915    private int adjustDisableFlags(int state) {
1916        if (!mLaunchTransitionFadingAway
1917                && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
1918            state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
1919            state |= StatusBarManager.DISABLE_SYSTEM_INFO;
1920        }
1921        return state;
1922    }
1923
1924    /**
1925     * State is one or more of the DISABLE constants from StatusBarManager.
1926     */
1927    public void disable(int state, boolean animate) {
1928        mDisabledUnmodified = state;
1929        state = adjustDisableFlags(state);
1930        final int old = mDisabled;
1931        final int diff = state ^ old;
1932        mDisabled = state;
1933
1934        if (DEBUG) {
1935            Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1936                old, state, diff));
1937        }
1938
1939        StringBuilder flagdbg = new StringBuilder();
1940        flagdbg.append("disable: < ");
1941        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1942        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1943        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1944        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1945        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1946        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1947        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1948        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1949        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1950        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1951        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1952        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1953        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1954        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1955        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1956        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1957        flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
1958        flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
1959        flagdbg.append(">");
1960        Log.d(TAG, flagdbg.toString());
1961
1962        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1963            mSystemIconArea.animate().cancel();
1964            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1965                animateStatusBarHide(mSystemIconArea, animate);
1966            } else {
1967                animateStatusBarShow(mSystemIconArea, animate);
1968            }
1969        }
1970
1971        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1972            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1973            showClock(show);
1974        }
1975        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1976            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1977                animateCollapsePanels();
1978            }
1979        }
1980
1981        if ((diff & (StatusBarManager.DISABLE_HOME
1982                        | StatusBarManager.DISABLE_RECENT
1983                        | StatusBarManager.DISABLE_BACK
1984                        | StatusBarManager.DISABLE_SEARCH)) != 0) {
1985            // the nav bar will take care of these
1986            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1987
1988            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1989                // close recents if it's visible
1990                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
1991                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
1992            }
1993        }
1994
1995        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1996            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1997                if (mTicking) {
1998                    haltTicker();
1999                }
2000                animateStatusBarHide(mNotificationIconArea, animate);
2001            } else {
2002                animateStatusBarShow(mNotificationIconArea, animate);
2003            }
2004        }
2005
2006        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
2007            mDisableNotificationAlerts =
2008                    (state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
2009            mHeadsUpObserver.onChange(true);
2010        }
2011    }
2012
2013    /**
2014     * Animates {@code v}, a view that is part of the status bar, out.
2015     */
2016    private void animateStatusBarHide(final View v, boolean animate) {
2017        v.animate().cancel();
2018        if (!animate) {
2019            v.setAlpha(0f);
2020            v.setVisibility(View.INVISIBLE);
2021            return;
2022        }
2023        v.animate()
2024                .alpha(0f)
2025                .setDuration(160)
2026                .setStartDelay(0)
2027                .setInterpolator(ALPHA_OUT)
2028                .withEndAction(new Runnable() {
2029                    @Override
2030                    public void run() {
2031                        v.setVisibility(View.INVISIBLE);
2032                    }
2033                });
2034    }
2035
2036    /**
2037     * Animates {@code v}, a view that is part of the status bar, in.
2038     */
2039    private void animateStatusBarShow(View v, boolean animate) {
2040        v.animate().cancel();
2041        v.setVisibility(View.VISIBLE);
2042        if (!animate) {
2043            v.setAlpha(1f);
2044            return;
2045        }
2046        v.animate()
2047                .alpha(1f)
2048                .setDuration(320)
2049                .setInterpolator(ALPHA_IN)
2050                .setStartDelay(50)
2051
2052                // We need to clean up any pending end action from animateStatusBarHide if we call
2053                // both hide and show in the same frame before the animation actually gets started.
2054                // cancel() doesn't really remove the end action.
2055                .withEndAction(null);
2056
2057        // Synchronize the motion with the Keyguard fading if necessary.
2058        if (mKeyguardFadingAway) {
2059            v.animate()
2060                    .setDuration(mKeyguardFadingAwayDuration)
2061                    .setInterpolator(mLinearOutSlowIn)
2062                    .setStartDelay(mKeyguardFadingAwayDelay)
2063                    .start();
2064        }
2065    }
2066
2067    @Override
2068    protected BaseStatusBar.H createHandler() {
2069        return new PhoneStatusBar.H();
2070    }
2071
2072    @Override
2073    public void startActivity(Intent intent, boolean dismissShade) {
2074        startActivityDismissingKeyguard(intent, false, dismissShade);
2075    }
2076
2077    public ScrimController getScrimController() {
2078        return mScrimController;
2079    }
2080
2081    public void setQsExpanded(boolean expanded) {
2082        mStatusBarWindowManager.setQsExpanded(expanded);
2083    }
2084
2085    public boolean isGoingToNotificationShade() {
2086        return mLeaveOpenOnKeyguardHide;
2087    }
2088
2089    public boolean isQsExpanded() {
2090        return mNotificationPanel.isQsExpanded();
2091    }
2092
2093    public boolean isFalsingThresholdNeeded() {
2094        boolean onKeyguard = getBarState() == StatusBarState.KEYGUARD;
2095        boolean isMethodInSecure = mUnlockMethodCache.isMethodInsecure();
2096        return onKeyguard && isMethodInSecure;
2097    }
2098
2099    @Override  // NotificationData.Environment
2100    public String getCurrentMediaNotificationKey() {
2101        return mMediaNotificationKey;
2102    }
2103
2104    /**
2105     * All changes to the status bar and notifications funnel through here and are batched.
2106     */
2107    private class H extends BaseStatusBar.H {
2108        public void handleMessage(Message m) {
2109            super.handleMessage(m);
2110            switch (m.what) {
2111                case MSG_OPEN_NOTIFICATION_PANEL:
2112                    animateExpandNotificationsPanel();
2113                    break;
2114                case MSG_OPEN_SETTINGS_PANEL:
2115                    animateExpandSettingsPanel();
2116                    break;
2117                case MSG_CLOSE_PANELS:
2118                    animateCollapsePanels();
2119                    break;
2120                case MSG_SHOW_HEADS_UP:
2121                    setHeadsUpVisibility(true);
2122                    break;
2123                case MSG_DECAY_HEADS_UP:
2124                    mHeadsUpNotificationView.release();
2125                    setHeadsUpVisibility(false);
2126                    break;
2127                case MSG_HIDE_HEADS_UP:
2128                    mHeadsUpNotificationView.release();
2129                    setHeadsUpVisibility(false);
2130                    break;
2131                case MSG_ESCALATE_HEADS_UP:
2132                    escalateHeadsUp();
2133                    setHeadsUpVisibility(false);
2134                    break;
2135            }
2136        }
2137    }
2138
2139    /**  if the interrupting notification had a fullscreen intent, fire it now.  */
2140    private void escalateHeadsUp() {
2141        if (mHeadsUpNotificationView.getEntry() != null) {
2142            final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
2143            mHeadsUpNotificationView.release();
2144            final Notification notification = sbn.getNotification();
2145            if (notification.fullScreenIntent != null) {
2146                if (DEBUG)
2147                    Log.d(TAG, "converting a heads up to fullScreen");
2148                try {
2149                    notification.fullScreenIntent.send();
2150                } catch (PendingIntent.CanceledException e) {
2151                }
2152            }
2153        }
2154    }
2155
2156    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
2157        public void onFocusChange(View v, boolean hasFocus) {
2158            // Because 'v' is a ViewGroup, all its children will be (un)selected
2159            // too, which allows marqueeing to work.
2160            v.setSelected(hasFocus);
2161        }
2162    };
2163
2164    boolean panelsEnabled() {
2165        return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
2166    }
2167
2168    void makeExpandedVisible(boolean force) {
2169        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
2170        if (!force && (mExpandedVisible || !panelsEnabled())) {
2171            return;
2172        }
2173
2174        mExpandedVisible = true;
2175        if (mNavigationBarView != null)
2176            mNavigationBarView.setSlippery(true);
2177
2178        updateCarrierLabelVisibility(true);
2179
2180        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2181
2182        // Expand the window to encompass the full screen in anticipation of the drag.
2183        // This is only possible to do atomically because the status bar is at the top of the screen!
2184        mStatusBarWindowManager.setStatusBarExpanded(true);
2185        mStatusBarView.setFocusable(false);
2186
2187        visibilityChanged(true);
2188        mWaitingForKeyguardExit = false;
2189        disable(mDisabledUnmodified, !force /* animate */);
2190        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2191    }
2192
2193    public void animateCollapsePanels() {
2194        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2195    }
2196
2197    private final Runnable mAnimateCollapsePanels = new Runnable() {
2198        @Override
2199        public void run() {
2200            animateCollapsePanels();
2201        }
2202    };
2203
2204    public void postAnimateCollapsePanels() {
2205        mHandler.post(mAnimateCollapsePanels);
2206    }
2207
2208    public void animateCollapsePanels(int flags) {
2209        animateCollapsePanels(flags, false /* force */);
2210    }
2211
2212    public void animateCollapsePanels(int flags, boolean force) {
2213        if (!force &&
2214                (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
2215            runPostCollapseRunnables();
2216            return;
2217        }
2218        if (SPEW) {
2219            Log.d(TAG, "animateCollapse():"
2220                    + " mExpandedVisible=" + mExpandedVisible
2221                    + " flags=" + flags);
2222        }
2223
2224        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
2225            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
2226                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2227                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2228            }
2229        }
2230
2231        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
2232            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
2233            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
2234        }
2235
2236        if (mStatusBarWindow != null) {
2237            // release focus immediately to kick off focus change transition
2238            mStatusBarWindowManager.setStatusBarFocusable(false);
2239
2240            mStatusBarWindow.cancelExpandHelper();
2241            mStatusBarView.collapseAllPanels(true);
2242        }
2243    }
2244
2245    private void runPostCollapseRunnables() {
2246        int size = mPostCollapseRunnables.size();
2247        for (int i = 0; i < size; i++) {
2248            mPostCollapseRunnables.get(i).run();
2249        }
2250        mPostCollapseRunnables.clear();
2251    }
2252
2253    public ViewPropertyAnimator setVisibilityWhenDone(
2254            final ViewPropertyAnimator a, final View v, final int vis) {
2255        a.setListener(new AnimatorListenerAdapter() {
2256            @Override
2257            public void onAnimationEnd(Animator animation) {
2258                v.setVisibility(vis);
2259                a.setListener(null); // oneshot
2260            }
2261        });
2262        return a;
2263    }
2264
2265    public Animator setVisibilityWhenDone(
2266            final Animator a, final View v, final int vis) {
2267        a.addListener(new AnimatorListenerAdapter() {
2268            @Override
2269            public void onAnimationEnd(Animator animation) {
2270                v.setVisibility(vis);
2271            }
2272        });
2273        return a;
2274    }
2275
2276    public Animator interpolator(TimeInterpolator ti, Animator a) {
2277        a.setInterpolator(ti);
2278        return a;
2279    }
2280
2281    public Animator startDelay(int d, Animator a) {
2282        a.setStartDelay(d);
2283        return a;
2284    }
2285
2286    public Animator start(Animator a) {
2287        a.start();
2288        return a;
2289    }
2290
2291    final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
2292    final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
2293    final int FLIP_DURATION_OUT = 125;
2294    final int FLIP_DURATION_IN = 225;
2295    final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
2296
2297    Animator mScrollViewAnim, mClearButtonAnim;
2298
2299    @Override
2300    public void animateExpandNotificationsPanel() {
2301        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2302        if (!panelsEnabled()) {
2303            return ;
2304        }
2305
2306        mNotificationPanel.expand();
2307
2308        if (false) postStartTracing();
2309    }
2310
2311    @Override
2312    public void animateExpandSettingsPanel() {
2313        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2314        if (!panelsEnabled()) {
2315            return;
2316        }
2317
2318        // Settings are not available in setup
2319        if (!mUserSetup) return;
2320
2321        mNotificationPanel.expand();
2322        mNotificationPanel.openQs();
2323
2324        if (false) postStartTracing();
2325    }
2326
2327    public void animateCollapseQuickSettings() {
2328        mStatusBarView.collapseAllPanels(true);
2329    }
2330
2331    void makeExpandedInvisible() {
2332        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
2333                + " mExpandedVisible=" + mExpandedVisible);
2334
2335        if (!mExpandedVisible || mStatusBarWindow == null) {
2336            return;
2337        }
2338
2339        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
2340        mStatusBarView.collapseAllPanels(/*animate=*/ false);
2341
2342        // reset things to their proper state
2343        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
2344        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
2345
2346        mStackScroller.setVisibility(View.VISIBLE);
2347        mNotificationPanel.setVisibility(View.GONE);
2348
2349        setAreThereNotifications(); // show the clear button
2350
2351        mNotificationPanel.closeQs();
2352
2353        mExpandedVisible = false;
2354        if (mNavigationBarView != null)
2355            mNavigationBarView.setSlippery(false);
2356        visibilityChanged(false);
2357
2358        // Shrink the window to the size of the status bar only
2359        mStatusBarWindowManager.setStatusBarExpanded(false);
2360        mStatusBarView.setFocusable(true);
2361
2362        // Close any "App info" popups that might have snuck on-screen
2363        dismissPopups();
2364
2365        runPostCollapseRunnables();
2366        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2367        showBouncer();
2368        disable(mDisabledUnmodified, true /* animate */);
2369    }
2370
2371    public boolean interceptTouchEvent(MotionEvent event) {
2372        if (DEBUG_GESTURES) {
2373            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
2374                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
2375                        event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
2376            }
2377
2378        }
2379
2380        if (SPEW) {
2381            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
2382                + mDisabled + " mTracking=" + mTracking);
2383        } else if (CHATTY) {
2384            if (event.getAction() != MotionEvent.ACTION_MOVE) {
2385                Log.d(TAG, String.format(
2386                            "panel: %s at (%f, %f) mDisabled=0x%08x",
2387                            MotionEvent.actionToString(event.getAction()),
2388                            event.getRawX(), event.getRawY(), mDisabled));
2389            }
2390        }
2391
2392        if (DEBUG_GESTURES) {
2393            mGestureRec.add(event);
2394        }
2395
2396        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
2397            final boolean upOrCancel =
2398                    event.getAction() == MotionEvent.ACTION_UP ||
2399                    event.getAction() == MotionEvent.ACTION_CANCEL;
2400            if (upOrCancel && !mExpandedVisible) {
2401                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2402            } else {
2403                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2404            }
2405        }
2406        return false;
2407    }
2408
2409    public GestureRecorder getGestureRecorder() {
2410        return mGestureRec;
2411    }
2412
2413    private void setNavigationIconHints(int hints) {
2414        if (hints == mNavigationIconHints) return;
2415
2416        mNavigationIconHints = hints;
2417
2418        if (mNavigationBarView != null) {
2419            mNavigationBarView.setNavigationIconHints(hints);
2420        }
2421        checkBarModes();
2422    }
2423
2424    @Override // CommandQueue
2425    public void setWindowState(int window, int state) {
2426        boolean showing = state == WINDOW_STATE_SHOWING;
2427        if (mStatusBarWindow != null
2428                && window == StatusBarManager.WINDOW_STATUS_BAR
2429                && mStatusBarWindowState != state) {
2430            mStatusBarWindowState = state;
2431            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
2432            if (!showing) {
2433                mStatusBarView.collapseAllPanels(false);
2434            }
2435        }
2436        if (mNavigationBarView != null
2437                && window == StatusBarManager.WINDOW_NAVIGATION_BAR
2438                && mNavigationBarWindowState != state) {
2439            mNavigationBarWindowState = state;
2440            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
2441        }
2442    }
2443
2444    @Override // CommandQueue
2445    public void buzzBeepBlinked() {
2446        if (mDozeServiceHost != null) {
2447            mDozeServiceHost.fireBuzzBeepBlinked();
2448        }
2449    }
2450
2451    @Override
2452    public void notificationLightOff() {
2453        if (mDozeServiceHost != null) {
2454            mDozeServiceHost.fireNotificationLight(false);
2455        }
2456    }
2457
2458    @Override
2459    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
2460        if (mDozeServiceHost != null) {
2461            mDozeServiceHost.fireNotificationLight(true);
2462        }
2463    }
2464
2465    @Override // CommandQueue
2466    public void setSystemUiVisibility(int vis, int mask) {
2467        final int oldVal = mSystemUiVisibility;
2468        final int newVal = (oldVal&~mask) | (vis&mask);
2469        final int diff = newVal ^ oldVal;
2470        if (DEBUG) Log.d(TAG, String.format(
2471                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
2472                Integer.toHexString(vis), Integer.toHexString(mask),
2473                Integer.toHexString(oldVal), Integer.toHexString(newVal),
2474                Integer.toHexString(diff)));
2475        if (diff != 0) {
2476            // we never set the recents bit via this method, so save the prior state to prevent
2477            // clobbering the bit below
2478            final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
2479
2480            mSystemUiVisibility = newVal;
2481
2482            // update low profile
2483            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
2484                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
2485                if (lightsOut) {
2486                    animateCollapsePanels();
2487                    if (mTicking) {
2488                        haltTicker();
2489                    }
2490                }
2491
2492                setAreThereNotifications();
2493            }
2494
2495            // update status bar mode
2496            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
2497                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
2498
2499            // update navigation bar mode
2500            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
2501                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
2502                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
2503            final boolean sbModeChanged = sbMode != -1;
2504            final boolean nbModeChanged = nbMode != -1;
2505            boolean checkBarModes = false;
2506            if (sbModeChanged && sbMode != mStatusBarMode) {
2507                mStatusBarMode = sbMode;
2508                checkBarModes = true;
2509            }
2510            if (nbModeChanged && nbMode != mNavigationBarMode) {
2511                mNavigationBarMode = nbMode;
2512                checkBarModes = true;
2513            }
2514            if (checkBarModes) {
2515                checkBarModes();
2516            }
2517            if (sbModeChanged || nbModeChanged) {
2518                // update transient bar autohide
2519                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
2520                    scheduleAutohide();
2521                } else {
2522                    cancelAutohide();
2523                }
2524            }
2525
2526            // ready to unhide
2527            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
2528                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
2529            }
2530            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
2531                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
2532            }
2533
2534            // restore the recents bit
2535            if (wasRecentsVisible) {
2536                mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
2537            }
2538
2539            // send updated sysui visibility to window manager
2540            notifyUiVisibilityChanged(mSystemUiVisibility);
2541        }
2542    }
2543
2544    private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
2545            int transientFlag, int translucentFlag) {
2546        final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
2547        final int newMode = barMode(newVis, transientFlag, translucentFlag);
2548        if (oldMode == newMode) {
2549            return -1; // no mode change
2550        }
2551        return newMode;
2552    }
2553
2554    private int barMode(int vis, int transientFlag, int translucentFlag) {
2555        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
2556                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
2557                : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
2558                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
2559                : MODE_OPAQUE;
2560    }
2561
2562    private void checkBarModes() {
2563        if (mDemoMode) return;
2564        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
2565        if (mNavigationBarView != null) {
2566            checkBarMode(mNavigationBarMode,
2567                    mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
2568        }
2569    }
2570
2571    private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
2572        final boolean powerSave = mBatteryController.isPowerSave();
2573        final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
2574                && !powerSave;
2575        if (powerSave && getBarState() == StatusBarState.SHADE) {
2576            mode = MODE_WARNING;
2577        }
2578        transitions.transitionTo(mode, anim);
2579    }
2580
2581    private void finishBarAnimations() {
2582        mStatusBarView.getBarTransitions().finishAnimations();
2583        if (mNavigationBarView != null) {
2584            mNavigationBarView.getBarTransitions().finishAnimations();
2585        }
2586    }
2587
2588    private final Runnable mCheckBarModes = new Runnable() {
2589        @Override
2590        public void run() {
2591            checkBarModes();
2592        }
2593    };
2594
2595    @Override
2596    public void setInteracting(int barWindow, boolean interacting) {
2597        mInteractingWindows = interacting
2598                ? (mInteractingWindows | barWindow)
2599                : (mInteractingWindows & ~barWindow);
2600        if (mInteractingWindows != 0) {
2601            suspendAutohide();
2602        } else {
2603            resumeSuspendedAutohide();
2604        }
2605        checkBarModes();
2606    }
2607
2608    private void resumeSuspendedAutohide() {
2609        if (mAutohideSuspended) {
2610            scheduleAutohide();
2611            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
2612        }
2613    }
2614
2615    private void suspendAutohide() {
2616        mHandler.removeCallbacks(mAutohide);
2617        mHandler.removeCallbacks(mCheckBarModes);
2618        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
2619    }
2620
2621    private void cancelAutohide() {
2622        mAutohideSuspended = false;
2623        mHandler.removeCallbacks(mAutohide);
2624    }
2625
2626    private void scheduleAutohide() {
2627        cancelAutohide();
2628        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2629    }
2630
2631    private void checkUserAutohide(View v, MotionEvent event) {
2632        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
2633                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2634                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
2635                ) {
2636            userAutohide();
2637        }
2638    }
2639
2640    private void userAutohide() {
2641        cancelAutohide();
2642        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2643    }
2644
2645    private boolean areLightsOn() {
2646        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2647    }
2648
2649    public void setLightsOn(boolean on) {
2650        Log.v(TAG, "setLightsOn(" + on + ")");
2651        if (on) {
2652            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2653        } else {
2654            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2655        }
2656    }
2657
2658    private void notifyUiVisibilityChanged(int vis) {
2659        try {
2660            mWindowManagerService.statusBarVisibilityChanged(vis);
2661        } catch (RemoteException ex) {
2662        }
2663    }
2664
2665    public void topAppWindowChanged(boolean showMenu) {
2666        if (DEBUG) {
2667            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2668        }
2669        if (mNavigationBarView != null) {
2670            mNavigationBarView.setMenuVisibility(showMenu);
2671        }
2672
2673        // See above re: lights-out policy for legacy apps.
2674        if (showMenu) setLightsOn(true);
2675    }
2676
2677    @Override
2678    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
2679            boolean showImeSwitcher) {
2680        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2681        int flags = mNavigationIconHints;
2682        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2683            flags |= NAVIGATION_HINT_BACK_ALT;
2684        } else {
2685            flags &= ~NAVIGATION_HINT_BACK_ALT;
2686        }
2687        if (showImeSwitcher) {
2688            flags |= NAVIGATION_HINT_IME_SHOWN;
2689        } else {
2690            flags &= ~NAVIGATION_HINT_IME_SHOWN;
2691        }
2692
2693        setNavigationIconHints(flags);
2694    }
2695
2696    @Override
2697    protected void tick(StatusBarNotification n, boolean firstTime) {
2698        if (!mTickerEnabled) return;
2699
2700        // no ticking in lights-out mode
2701        if (!areLightsOn()) return;
2702
2703        // no ticking in Setup
2704        if (!isDeviceProvisioned()) return;
2705
2706        // not for you
2707        if (!isNotificationForCurrentProfiles(n)) return;
2708
2709        // Show the ticker if one is requested. Also don't do this
2710        // until status bar window is attached to the window manager,
2711        // because...  well, what's the point otherwise?  And trying to
2712        // run a ticker without being attached will crash!
2713        if (n.getNotification().tickerText != null && mStatusBarWindow != null
2714                && mStatusBarWindow.getWindowToken() != null) {
2715            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2716                    | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2717                mTicker.addEntry(n);
2718            }
2719        }
2720    }
2721
2722    private class MyTicker extends Ticker {
2723        MyTicker(Context context, View sb) {
2724            super(context, sb);
2725            if (!mTickerEnabled) {
2726                Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
2727            }
2728        }
2729
2730        @Override
2731        public void tickerStarting() {
2732            if (!mTickerEnabled) return;
2733            mTicking = true;
2734            mStatusBarContents.setVisibility(View.GONE);
2735            mTickerView.setVisibility(View.VISIBLE);
2736            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2737            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2738        }
2739
2740        @Override
2741        public void tickerDone() {
2742            if (!mTickerEnabled) return;
2743            mStatusBarContents.setVisibility(View.VISIBLE);
2744            mTickerView.setVisibility(View.GONE);
2745            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2746            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2747                        mTickingDoneListener));
2748        }
2749
2750        public void tickerHalting() {
2751            if (!mTickerEnabled) return;
2752            if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2753                mStatusBarContents.setVisibility(View.VISIBLE);
2754                mStatusBarContents
2755                        .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2756            }
2757            mTickerView.setVisibility(View.GONE);
2758            // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2759        }
2760    }
2761
2762    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2763        public void onAnimationEnd(Animation animation) {
2764            mTicking = false;
2765        }
2766        public void onAnimationRepeat(Animation animation) {
2767        }
2768        public void onAnimationStart(Animation animation) {
2769        }
2770    };
2771
2772    private Animation loadAnim(int id, Animation.AnimationListener listener) {
2773        Animation anim = AnimationUtils.loadAnimation(mContext, id);
2774        if (listener != null) {
2775            anim.setAnimationListener(listener);
2776        }
2777        return anim;
2778    }
2779
2780    public static String viewInfo(View v) {
2781        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2782                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2783    }
2784
2785    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2786        synchronized (mQueueLock) {
2787            pw.println("Current Status Bar state:");
2788            pw.println("  mExpandedVisible=" + mExpandedVisible
2789                    + ", mTrackingPosition=" + mTrackingPosition);
2790            pw.println("  mTickerEnabled=" + mTickerEnabled);
2791            if (mTickerEnabled) {
2792                pw.println("  mTicking=" + mTicking);
2793                pw.println("  mTickerView: " + viewInfo(mTickerView));
2794            }
2795            pw.println("  mTracking=" + mTracking);
2796            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2797            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2798            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2799                    + " scroll " + mStackScroller.getScrollX()
2800                    + "," + mStackScroller.getScrollY());
2801        }
2802
2803        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2804        pw.print("  mStatusBarWindowState=");
2805        pw.println(windowStateToString(mStatusBarWindowState));
2806        pw.print("  mStatusBarMode=");
2807        pw.println(BarTransitions.modeToString(mStatusBarMode));
2808        pw.print("  mDozing="); pw.println(mDozing);
2809        pw.print("  mZenMode=");
2810        pw.println(Settings.Global.zenModeToString(mZenMode));
2811        pw.print("  mUseHeadsUp=");
2812        pw.println(mUseHeadsUp);
2813        pw.print("  interrupting package: ");
2814        pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
2815        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2816        if (mNavigationBarView != null) {
2817            pw.print("  mNavigationBarWindowState=");
2818            pw.println(windowStateToString(mNavigationBarWindowState));
2819            pw.print("  mNavigationBarMode=");
2820            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2821            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2822        }
2823
2824        pw.print("  mNavigationBarView=");
2825        if (mNavigationBarView == null) {
2826            pw.println("null");
2827        } else {
2828            mNavigationBarView.dump(fd, pw, args);
2829        }
2830
2831        pw.print("  mMediaSessionManager=");
2832        pw.println(mMediaSessionManager);
2833        pw.print("  mMediaNotificationKey=");
2834        pw.println(mMediaNotificationKey);
2835        pw.print("  mMediaController=");
2836        pw.print(mMediaController);
2837        if (mMediaController != null) {
2838            pw.print(" state=" + mMediaController.getPlaybackState());
2839        }
2840        pw.println();
2841        pw.print("  mMediaMetadata=");
2842        pw.print(mMediaMetadata);
2843        if (mMediaMetadata != null) {
2844            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
2845        }
2846        pw.println();
2847
2848        pw.println("  Panels: ");
2849        if (mNotificationPanel != null) {
2850            pw.println("    mNotificationPanel=" +
2851                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2852            pw.print  ("      ");
2853            mNotificationPanel.dump(fd, pw, args);
2854        }
2855
2856        if (DUMPTRUCK) {
2857            synchronized (mNotificationData) {
2858                mNotificationData.dump(pw, "  ");
2859            }
2860
2861            int N = mStatusIcons.getChildCount();
2862            pw.println("  system icons: " + N);
2863            for (int i=0; i<N; i++) {
2864                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2865                pw.println("    [" + i + "] icon=" + ic);
2866            }
2867
2868            if (false) {
2869                pw.println("see the logcat for a dump of the views we have created.");
2870                // must happen on ui thread
2871                mHandler.post(new Runnable() {
2872                        public void run() {
2873                            mStatusBarView.getLocationOnScreen(mAbsPos);
2874                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2875                                    + ") " + mStatusBarView.getWidth() + "x"
2876                                    + getStatusBarHeight());
2877                            mStatusBarView.debug();
2878                        }
2879                    });
2880            }
2881        }
2882
2883        if (DEBUG_GESTURES) {
2884            pw.print("  status bar gestures: ");
2885            mGestureRec.dump(fd, pw, args);
2886        }
2887
2888        if (mNetworkController != null) {
2889            mNetworkController.dump(fd, pw, args);
2890        }
2891        if (mBluetoothController != null) {
2892            mBluetoothController.dump(fd, pw, args);
2893        }
2894        if (mCastController != null) {
2895            mCastController.dump(fd, pw, args);
2896        }
2897        if (mUserSwitcherController != null) {
2898            mUserSwitcherController.dump(fd, pw, args);
2899        }
2900        if (mBatteryController != null) {
2901            mBatteryController.dump(fd, pw, args);
2902        }
2903        if (mNextAlarmController != null) {
2904            mNextAlarmController.dump(fd, pw, args);
2905        }
2906        if (mSecurityController != null) {
2907            mSecurityController.dump(fd, pw, args);
2908        }
2909    }
2910
2911    private String hunStateToString(Entry entry) {
2912        if (entry == null) return "null";
2913        if (entry.notification == null) return "corrupt";
2914        return entry.notification.getPackageName();
2915    }
2916
2917    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2918        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
2919        pw.println(BarTransitions.modeToString(transitions.getMode()));
2920    }
2921
2922    @Override
2923    public void createAndAddWindows() {
2924        addStatusBarWindow();
2925    }
2926
2927    private void addStatusBarWindow() {
2928        makeStatusBarView();
2929        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2930        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2931    }
2932
2933    static final float saturate(float a) {
2934        return a < 0f ? 0f : (a > 1f ? 1f : a);
2935    }
2936
2937    @Override
2938    public void updateExpandedViewPos(int thingy) {
2939        if (SPEW) Log.v(TAG, "updateExpandedViewPos");
2940
2941        // on larger devices, the notification panel is propped open a bit
2942        mNotificationPanel.setMinimumHeight(
2943                (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2944
2945        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2946        lp.gravity = mNotificationPanelGravity;
2947        mNotificationPanel.setLayoutParams(lp);
2948
2949        updateCarrierLabelVisibility(false);
2950    }
2951
2952    // called by makeStatusbar and also by PhoneStatusBarView
2953    void updateDisplaySize() {
2954        mDisplay.getMetrics(mDisplayMetrics);
2955        mDisplay.getSize(mCurrentDisplaySize);
2956        if (DEBUG_GESTURES) {
2957            mGestureRec.tag("display",
2958                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
2959        }
2960    }
2961
2962    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
2963            final boolean dismissShade) {
2964        if (onlyProvisioned && !isDeviceProvisioned()) return;
2965
2966        final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
2967                mContext, intent, mCurrentUserId);
2968        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
2969        dismissKeyguardThenExecute(new OnDismissAction() {
2970            @Override
2971            public boolean onDismiss() {
2972                AsyncTask.execute(new Runnable() {
2973                    public void run() {
2974                        try {
2975                            if (keyguardShowing && !afterKeyguardGone) {
2976                                ActivityManagerNative.getDefault()
2977                                        .keyguardWaitingForActivityDrawn();
2978                            }
2979                            intent.setFlags(
2980                                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2981                            mContext.startActivityAsUser(
2982                                    intent, new UserHandle(UserHandle.USER_CURRENT));
2983                            overrideActivityPendingAppTransition(
2984                                    keyguardShowing && !afterKeyguardGone);
2985                        } catch (RemoteException e) {
2986                        }
2987                    }
2988                });
2989                if (dismissShade) {
2990                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
2991                }
2992                return true;
2993            }
2994        }, afterKeyguardGone);
2995    }
2996
2997    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2998        public void onReceive(Context context, Intent intent) {
2999            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3000            String action = intent.getAction();
3001            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
3002                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
3003                String reason = intent.getStringExtra("reason");
3004                if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
3005                    flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
3006                }
3007                animateCollapsePanels(flags);
3008            }
3009            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
3010                mScreenOn = false;
3011                notifyNavigationBarScreenOn(false);
3012                notifyHeadsUpScreenOn(false);
3013                finishBarAnimations();
3014                stopNotificationLogging();
3015                resetUserExpandedStates();
3016            }
3017            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
3018                mScreenOn = true;
3019                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
3020                repositionNavigationBar();
3021                notifyNavigationBarScreenOn(true);
3022                startNotificationLoggingIfScreenOnAndVisible();
3023            }
3024            else if (ACTION_DEMO.equals(action)) {
3025                Bundle bundle = intent.getExtras();
3026                if (bundle != null) {
3027                    String command = bundle.getString("command", "").trim().toLowerCase();
3028                    if (command.length() > 0) {
3029                        try {
3030                            dispatchDemoCommand(command, bundle);
3031                        } catch (Throwable t) {
3032                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
3033                        }
3034                    }
3035                }
3036            } else if ("fake_artwork".equals(action)) {
3037                if (DEBUG_MEDIA_FAKE_ARTWORK) {
3038                    updateMediaMetaData(true);
3039                }
3040            }
3041        }
3042    };
3043
3044    private void resetUserExpandedStates() {
3045        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3046        final int notificationCount = activeNotifications.size();
3047        for (int i = 0; i < notificationCount; i++) {
3048            NotificationData.Entry entry = activeNotifications.get(i);
3049            if (entry.row != null) {
3050                entry.row.resetUserExpansion();
3051            }
3052        }
3053    }
3054
3055    @Override
3056    protected void dismissKeyguardThenExecute(final OnDismissAction action,
3057            boolean afterKeyguardGone) {
3058        if (mStatusBarKeyguardViewManager.isShowing()) {
3059            if (UnlockMethodCache.getInstance(mContext).isMethodInsecure()
3060                    && mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) {
3061                action.onDismiss();
3062                mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
3063                    @Override
3064                    public void run() {
3065                        mStatusBarKeyguardViewManager.dismiss();
3066                    }
3067                });
3068            } else {
3069                mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone);
3070            }
3071        } else {
3072            action.onDismiss();
3073        }
3074    }
3075
3076    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
3077    @Override
3078    protected void onConfigurationChanged(Configuration newConfig) {
3079        super.onConfigurationChanged(newConfig); // calls refreshLayout
3080
3081        if (DEBUG) {
3082            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
3083        }
3084        updateDisplaySize(); // populates mDisplayMetrics
3085
3086        updateResources();
3087        updateClockSize();
3088        repositionNavigationBar();
3089        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
3090        updateShowSearchHoldoff();
3091        updateRowStates();
3092    }
3093
3094    @Override
3095    public void userSwitched(int newUserId) {
3096        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
3097        animateCollapsePanels();
3098        updateNotifications();
3099        resetUserSetupObserver();
3100        setControllerUsers();
3101    }
3102
3103    private void setControllerUsers() {
3104        if (mZenModeController != null) {
3105            mZenModeController.setUserId(mCurrentUserId);
3106        }
3107    }
3108
3109    private void resetUserSetupObserver() {
3110        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
3111        mUserSetupObserver.onChange(false);
3112        mContext.getContentResolver().registerContentObserver(
3113                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
3114                mUserSetupObserver,
3115                mCurrentUserId);
3116    }
3117
3118    private void setHeadsUpVisibility(boolean vis) {
3119        if (!ENABLE_HEADS_UP) return;
3120        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
3121        EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
3122                vis ? mHeadsUpNotificationView.getKey() : "",
3123                vis ? 1 : 0);
3124        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
3125    }
3126
3127    public void onHeadsUpDismissed() {
3128        mHeadsUpNotificationView.dismiss();
3129    }
3130
3131    /**
3132     * Reload some of our resources when the configuration changes.
3133     *
3134     * We don't reload everything when the configuration changes -- we probably
3135     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
3136     * meantime, just update the things that we know change.
3137     */
3138    void updateResources() {
3139        // Update the quick setting tiles
3140        if (mQSPanel != null) {
3141            mQSPanel.updateResources();
3142        }
3143
3144        loadDimens();
3145        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
3146                mContext, android.R.interpolator.linear_out_slow_in);
3147
3148        if (mNotificationPanel != null) {
3149            mNotificationPanel.updateResources();
3150        }
3151        if (mHeadsUpNotificationView != null) {
3152            mHeadsUpNotificationView.updateResources();
3153        }
3154        if (mBrightnessMirrorController != null) {
3155            mBrightnessMirrorController.updateResources();
3156        }
3157    }
3158
3159    private void updateClockSize() {
3160        if (mStatusBarView == null) return;
3161        TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
3162        if (clock != null) {
3163            FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size);
3164        }
3165    }
3166    protected void loadDimens() {
3167        final Resources res = mContext.getResources();
3168
3169        mNaturalBarHeight = res.getDimensionPixelSize(
3170                com.android.internal.R.dimen.status_bar_height);
3171
3172        int newIconSize = res.getDimensionPixelSize(
3173            com.android.internal.R.dimen.status_bar_icon_size);
3174        int newIconHPadding = res.getDimensionPixelSize(
3175            R.dimen.status_bar_icon_padding);
3176
3177        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
3178//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
3179            mIconHPadding = newIconHPadding;
3180            mIconSize = newIconSize;
3181            //reloadAllNotificationIcons(); // reload the tray
3182        }
3183
3184        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
3185
3186        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
3187        if (mNotificationPanelGravity <= 0) {
3188            mNotificationPanelGravity = Gravity.START | Gravity.TOP;
3189        }
3190
3191        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
3192        mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
3193
3194        mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
3195        if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
3196            mNotificationPanelMinHeightFrac = 0f;
3197        }
3198
3199        mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
3200        mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
3201        mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
3202
3203        mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
3204
3205        if (DEBUG) Log.v(TAG, "updateResources");
3206    }
3207
3208    // Visibility reporting
3209
3210    @Override
3211    protected void visibilityChanged(boolean visible) {
3212        mVisible = visible;
3213        if (visible) {
3214            startNotificationLoggingIfScreenOnAndVisible();
3215        } else {
3216            stopNotificationLogging();
3217        }
3218        super.visibilityChanged(visible);
3219    }
3220
3221    private void stopNotificationLogging() {
3222        // Report all notifications as invisible and turn down the
3223        // reporter.
3224        if (!mCurrentlyVisibleNotifications.isEmpty()) {
3225            logNotificationVisibilityChanges(
3226                    Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
3227            mCurrentlyVisibleNotifications.clear();
3228        }
3229        mHandler.removeCallbacks(mVisibilityReporter);
3230        mStackScroller.setChildLocationsChangedListener(null);
3231    }
3232
3233    private void startNotificationLoggingIfScreenOnAndVisible() {
3234        if (mVisible && mScreenOn) {
3235            mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
3236            // Some transitions like mScreenOn=false -> mScreenOn=true don't
3237            // cause the scroller to emit child location events. Hence generate
3238            // one ourselves to guarantee that we're reporting visible
3239            // notifications.
3240            // (Note that in cases where the scroller does emit events, this
3241            // additional event doesn't break anything.)
3242            mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
3243        }
3244    }
3245
3246    private void logNotificationVisibilityChanges(
3247            Collection<String> newlyVisible, Collection<String> noLongerVisible) {
3248        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
3249            return;
3250        }
3251        String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
3252        String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
3253        try {
3254            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
3255        } catch (RemoteException e) {
3256            // Ignore.
3257        }
3258    }
3259
3260    //
3261    // tracing
3262    //
3263
3264    void postStartTracing() {
3265        mHandler.postDelayed(mStartTracing, 3000);
3266    }
3267
3268    void vibrate() {
3269        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
3270                Context.VIBRATOR_SERVICE);
3271        vib.vibrate(250, VIBRATION_ATTRIBUTES);
3272    }
3273
3274    Runnable mStartTracing = new Runnable() {
3275        public void run() {
3276            vibrate();
3277            SystemClock.sleep(250);
3278            Log.d(TAG, "startTracing");
3279            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
3280            mHandler.postDelayed(mStopTracing, 10000);
3281        }
3282    };
3283
3284    Runnable mStopTracing = new Runnable() {
3285        public void run() {
3286            android.os.Debug.stopMethodTracing();
3287            Log.d(TAG, "stopTracing");
3288            vibrate();
3289        }
3290    };
3291
3292    @Override
3293    protected void haltTicker() {
3294        if (mTickerEnabled) {
3295            mTicker.halt();
3296        }
3297    }
3298
3299    @Override
3300    protected boolean shouldDisableNavbarGestures() {
3301        return !isDeviceProvisioned()
3302                || mExpandedVisible
3303                || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
3304    }
3305
3306    public void postStartSettingsActivity(final Intent intent, int delay) {
3307        mHandler.postDelayed(new Runnable() {
3308            @Override
3309            public void run() {
3310                handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
3311            }
3312        }, delay);
3313    }
3314
3315    private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
3316        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
3317    }
3318
3319    private static class FastColorDrawable extends Drawable {
3320        private final int mColor;
3321
3322        public FastColorDrawable(int color) {
3323            mColor = 0xff000000 | color;
3324        }
3325
3326        @Override
3327        public void draw(Canvas canvas) {
3328            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
3329        }
3330
3331        @Override
3332        public void setAlpha(int alpha) {
3333        }
3334
3335        @Override
3336        public void setColorFilter(ColorFilter cf) {
3337        }
3338
3339        @Override
3340        public int getOpacity() {
3341            return PixelFormat.OPAQUE;
3342        }
3343
3344        @Override
3345        public void setBounds(int left, int top, int right, int bottom) {
3346        }
3347
3348        @Override
3349        public void setBounds(Rect bounds) {
3350        }
3351    }
3352
3353    @Override
3354    public void destroy() {
3355        super.destroy();
3356        if (mStatusBarWindow != null) {
3357            mWindowManager.removeViewImmediate(mStatusBarWindow);
3358            mStatusBarWindow = null;
3359        }
3360        if (mNavigationBarView != null) {
3361            mWindowManager.removeViewImmediate(mNavigationBarView);
3362            mNavigationBarView = null;
3363        }
3364        mContext.unregisterReceiver(mBroadcastReceiver);
3365    }
3366
3367    private boolean mDemoModeAllowed;
3368    private boolean mDemoMode;
3369    private DemoStatusIcons mDemoStatusIcons;
3370
3371    @Override
3372    public void dispatchDemoCommand(String command, Bundle args) {
3373        if (!mDemoModeAllowed) {
3374            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
3375                    "sysui_demo_allowed", 0) != 0;
3376        }
3377        if (!mDemoModeAllowed) return;
3378        if (command.equals(COMMAND_ENTER)) {
3379            mDemoMode = true;
3380        } else if (command.equals(COMMAND_EXIT)) {
3381            mDemoMode = false;
3382            checkBarModes();
3383        } else if (!mDemoMode) {
3384            // automatically enter demo mode on first demo command
3385            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
3386        }
3387        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
3388        if (modeChange || command.equals(COMMAND_CLOCK)) {
3389            dispatchDemoCommandToView(command, args, R.id.clock);
3390        }
3391        if (modeChange || command.equals(COMMAND_BATTERY)) {
3392            dispatchDemoCommandToView(command, args, R.id.battery);
3393        }
3394        if (modeChange || command.equals(COMMAND_STATUS)) {
3395            if (mDemoStatusIcons == null) {
3396                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
3397            }
3398            mDemoStatusIcons.dispatchDemoCommand(command, args);
3399        }
3400        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
3401            mNetworkController.dispatchDemoCommand(command, args);
3402        }
3403        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
3404            View notifications = mStatusBarView == null ? null
3405                    : mStatusBarView.findViewById(R.id.notification_icon_area);
3406            if (notifications != null) {
3407                String visible = args.getString("visible");
3408                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
3409                notifications.setVisibility(vis);
3410            }
3411        }
3412        if (command.equals(COMMAND_BARS)) {
3413            String mode = args.getString("mode");
3414            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
3415                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
3416                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
3417                    "transparent".equals(mode) ? MODE_TRANSPARENT :
3418                    "warning".equals(mode) ? MODE_WARNING :
3419                    -1;
3420            if (barMode != -1) {
3421                boolean animate = true;
3422                if (mStatusBarView != null) {
3423                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
3424                }
3425                if (mNavigationBarView != null) {
3426                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
3427                }
3428            }
3429        }
3430    }
3431
3432    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
3433        if (mStatusBarView == null) return;
3434        View v = mStatusBarView.findViewById(id);
3435        if (v instanceof DemoMode) {
3436            ((DemoMode)v).dispatchDemoCommand(command, args);
3437        }
3438    }
3439
3440    /**
3441     * @return The {@link StatusBarState} the status bar is in.
3442     */
3443    public int getBarState() {
3444        return mState;
3445    }
3446
3447    public void showKeyguard() {
3448        setBarState(StatusBarState.KEYGUARD);
3449        updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3450        instantExpandNotificationsPanel();
3451        mLeaveOpenOnKeyguardHide = false;
3452        if (mDraggedDownRow != null) {
3453            mDraggedDownRow.setUserLocked(false);
3454            mDraggedDownRow.notifyHeightChanged();
3455            mDraggedDownRow = null;
3456        }
3457    }
3458
3459    public boolean isCollapsing() {
3460        return mNotificationPanel.isCollapsing();
3461    }
3462
3463    public void addPostCollapseAction(Runnable r) {
3464        mPostCollapseRunnables.add(r);
3465    }
3466
3467    public boolean isInLaunchTransition() {
3468        return mNotificationPanel.isLaunchTransitionRunning()
3469                || mNotificationPanel.isLaunchTransitionFinished();
3470    }
3471
3472    /**
3473     * Fades the content of the keyguard away after the launch transition is done.
3474     *
3475     * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
3476     *                     starts
3477     * @param endRunnable the runnable to be run when the transition is done
3478     */
3479    public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
3480            final Runnable endRunnable) {
3481        Runnable hideRunnable = new Runnable() {
3482            @Override
3483            public void run() {
3484                mLaunchTransitionFadingAway = true;
3485                if (beforeFading != null) {
3486                    beforeFading.run();
3487                }
3488                mNotificationPanel.setAlpha(1);
3489                mNotificationPanel.animate()
3490                        .alpha(0)
3491                        .setStartDelay(FADE_KEYGUARD_START_DELAY)
3492                        .setDuration(FADE_KEYGUARD_DURATION)
3493                        .withLayer()
3494                        .withEndAction(new Runnable() {
3495                            @Override
3496                            public void run() {
3497                                mNotificationPanel.setAlpha(1);
3498                                if (endRunnable != null) {
3499                                    endRunnable.run();
3500                                }
3501                                mLaunchTransitionFadingAway = false;
3502                            }
3503                        });
3504            }
3505        };
3506        if (mNotificationPanel.isLaunchTransitionRunning()) {
3507            mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
3508        } else {
3509            hideRunnable.run();
3510        }
3511    }
3512
3513    /**
3514     * @return true if we would like to stay in the shade, false if it should go away entirely
3515     */
3516    public boolean hideKeyguard() {
3517        boolean staying = mLeaveOpenOnKeyguardHide;
3518        setBarState(StatusBarState.SHADE);
3519        if (mLeaveOpenOnKeyguardHide) {
3520            mLeaveOpenOnKeyguardHide = false;
3521            mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
3522            if (mDraggedDownRow != null) {
3523                mDraggedDownRow.setUserLocked(false);
3524                mDraggedDownRow = null;
3525            }
3526        } else {
3527            instantCollapseNotificationPanel();
3528        }
3529        updateKeyguardState(staying, false /* fromShadeLocked */);
3530        return staying;
3531    }
3532
3533    public long calculateGoingToFullShadeDelay() {
3534        return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
3535    }
3536
3537    /**
3538     * Notifies the status bar the Keyguard is fading away with the specified timings.
3539     *
3540     * @param delay the animation delay in miliseconds
3541     * @param fadeoutDuration the duration of the exit animation, in milliseconds
3542     */
3543    public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
3544        mKeyguardFadingAway = true;
3545        mKeyguardFadingAwayDelay = delay;
3546        mKeyguardFadingAwayDuration = fadeoutDuration;
3547        mWaitingForKeyguardExit = false;
3548        disable(mDisabledUnmodified, true /* animate */);
3549    }
3550
3551    /**
3552     * Notifies that the Keyguard fading away animation is done.
3553     */
3554    public void finishKeyguardFadingAway() {
3555        mKeyguardFadingAway = false;
3556    }
3557
3558    private void updatePublicMode() {
3559        setLockscreenPublicMode(
3560                (mStatusBarKeyguardViewManager.isShowing() ||
3561                        mStatusBarKeyguardViewManager.isOccluded())
3562                && mStatusBarKeyguardViewManager.isSecure());
3563    }
3564
3565    private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
3566        if (mState == StatusBarState.KEYGUARD) {
3567            mKeyguardIndicationController.setVisible(true);
3568            mNotificationPanel.resetViews();
3569            mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
3570        } else {
3571            mKeyguardIndicationController.setVisible(false);
3572            mKeyguardUserSwitcher.setKeyguard(false,
3573                    goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
3574        }
3575        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3576            mScrimController.setKeyguardShowing(true);
3577        } else {
3578            mScrimController.setKeyguardShowing(false);
3579        }
3580        mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
3581        updateDozingState();
3582        updatePublicMode();
3583        updateStackScrollerState(goingToFullShade);
3584        updateNotifications();
3585        checkBarModes();
3586        updateCarrierLabelVisibility(false);
3587        updateMediaMetaData(false);
3588        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
3589                mStatusBarKeyguardViewManager.isSecure());
3590    }
3591
3592    private void updateDozingState() {
3593        if (mState != StatusBarState.KEYGUARD) {
3594            return;
3595        }
3596        mNotificationPanel.setDozing(mDozing);
3597        if (mDozing) {
3598            mKeyguardBottomArea.setVisibility(View.INVISIBLE);
3599            mStackScroller.setDark(true, false /*animate*/);
3600        } else {
3601            mKeyguardBottomArea.setVisibility(View.VISIBLE);
3602            mStackScroller.setDark(false, false /*animate*/);
3603        }
3604        mScrimController.setDozing(mDozing);
3605    }
3606
3607    public void updateStackScrollerState(boolean goingToFullShade) {
3608        if (mStackScroller == null) return;
3609        boolean onKeyguard = mState == StatusBarState.KEYGUARD;
3610        mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
3611        mStackScroller.setDimmed(onKeyguard, false /* animate */);
3612        mStackScroller.setExpandingEnabled(!onKeyguard);
3613        ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
3614        mStackScroller.setActivatedChild(null);
3615        if (activatedChild != null) {
3616            activatedChild.makeInactive(false /* animate */);
3617        }
3618    }
3619
3620    public void userActivity() {
3621        if (mState == StatusBarState.KEYGUARD) {
3622            mKeyguardViewMediatorCallback.userActivity();
3623        }
3624    }
3625
3626    public boolean interceptMediaKey(KeyEvent event) {
3627        return mState == StatusBarState.KEYGUARD
3628                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
3629    }
3630
3631    public boolean onMenuPressed() {
3632        return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
3633    }
3634
3635    public boolean onBackPressed() {
3636        if (mStatusBarKeyguardViewManager.onBackPressed()) {
3637            return true;
3638        }
3639        if (mNotificationPanel.isQsExpanded()) {
3640            if (mNotificationPanel.isQsDetailShowing()) {
3641                mNotificationPanel.closeQsDetail();
3642            } else {
3643                mNotificationPanel.animateCloseQs();
3644            }
3645            return true;
3646        }
3647        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
3648            animateCollapsePanels();
3649            return true;
3650        }
3651        return false;
3652    }
3653
3654    public boolean onSpacePressed() {
3655        if (mScreenOn != null && mScreenOn
3656                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
3657            animateCollapsePanels(0 /* flags */, true /* force */);
3658            return true;
3659        }
3660        return false;
3661    }
3662
3663    private void showBouncer() {
3664        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3665            mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
3666            mStatusBarKeyguardViewManager.dismiss();
3667        }
3668    }
3669
3670    private void instantExpandNotificationsPanel() {
3671
3672        // Make our window larger and the panel expanded.
3673        makeExpandedVisible(true);
3674        mNotificationPanel.instantExpand();
3675    }
3676
3677    private void instantCollapseNotificationPanel() {
3678        mNotificationPanel.setExpandedFraction(0);
3679    }
3680
3681    @Override
3682    public void onActivated(ActivatableNotificationView view) {
3683        mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
3684        ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
3685        if (previousView != null) {
3686            previousView.makeInactive(true /* animate */);
3687        }
3688        mStackScroller.setActivatedChild(view);
3689    }
3690
3691    /**
3692     * @param state The {@link StatusBarState} to set.
3693     */
3694    public void setBarState(int state) {
3695        mState = state;
3696        mStatusBarWindowManager.setStatusBarState(state);
3697    }
3698
3699    @Override
3700    public void onActivationReset(ActivatableNotificationView view) {
3701        if (view == mStackScroller.getActivatedChild()) {
3702            mKeyguardIndicationController.hideTransientIndication();
3703            mStackScroller.setActivatedChild(null);
3704        }
3705    }
3706
3707    public void onTrackingStarted() {
3708        runPostCollapseRunnables();
3709    }
3710
3711    public void onUnlockHintStarted() {
3712        mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
3713    }
3714
3715    public void onHintFinished() {
3716        // Delay the reset a bit so the user can read the text.
3717        mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
3718    }
3719
3720    public void onCameraHintStarted() {
3721        mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
3722    }
3723
3724    public void onPhoneHintStarted() {
3725        mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
3726    }
3727
3728    public void onTrackingStopped(boolean expand) {
3729        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3730            if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
3731                showBouncer();
3732            }
3733        }
3734    }
3735
3736    @Override
3737    protected int getMaxKeyguardNotifications() {
3738        return mKeyguardMaxNotificationCount;
3739    }
3740
3741    public NavigationBarView getNavigationBarView() {
3742        return mNavigationBarView;
3743    }
3744
3745    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
3746
3747    @Override
3748    public boolean onDraggedDown(View startingChild) {
3749        if (hasActiveNotifications()) {
3750
3751            // We have notifications, go to locked shade.
3752            goToLockedShade(startingChild);
3753            return true;
3754        } else {
3755
3756            // No notifications - abort gesture.
3757            return false;
3758        }
3759    }
3760
3761    @Override
3762    public void onDragDownReset() {
3763        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
3764    }
3765
3766    @Override
3767    public void onThresholdReached() {
3768        mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
3769    }
3770
3771    @Override
3772    public void onTouchSlopExceeded() {
3773        mStackScroller.removeLongPressCallback();
3774    }
3775
3776    @Override
3777    public void setEmptyDragAmount(float amount) {
3778        mNotificationPanel.setEmptyDragAmount(amount);
3779    }
3780
3781    /**
3782     * If secure with redaction: Show bouncer, go to unlocked shade.
3783     *
3784     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
3785     *
3786     * @param expandView The view to expand after going to the shade.
3787     */
3788    public void goToLockedShade(View expandView) {
3789        ExpandableNotificationRow row = null;
3790        if (expandView instanceof ExpandableNotificationRow) {
3791            row = (ExpandableNotificationRow) expandView;
3792            row.setUserExpanded(true);
3793        }
3794        boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
3795                || !mShowLockscreenNotifications;
3796        if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
3797            mLeaveOpenOnKeyguardHide = true;
3798            showBouncer();
3799            mDraggedDownRow = row;
3800        } else {
3801            mNotificationPanel.animateToFullShade(0 /* delay */);
3802            setBarState(StatusBarState.SHADE_LOCKED);
3803            updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3804            if (row != null) {
3805                row.setUserLocked(false);
3806            }
3807        }
3808    }
3809
3810    /**
3811     * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
3812     */
3813    public void goToKeyguard() {
3814        if (mState == StatusBarState.SHADE_LOCKED) {
3815            mStackScroller.onGoToKeyguard();
3816            setBarState(StatusBarState.KEYGUARD);
3817            updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
3818        }
3819    }
3820
3821    /**
3822     * @return a ViewGroup that spans the entire panel which contains the quick settings
3823     */
3824    public ViewGroup getQuickSettingsOverlayParent() {
3825        return mNotificationPanel;
3826    }
3827
3828    public long getKeyguardFadingAwayDelay() {
3829        return mKeyguardFadingAwayDelay;
3830    }
3831
3832    public long getKeyguardFadingAwayDuration() {
3833        return mKeyguardFadingAwayDuration;
3834    }
3835
3836    public LinearLayout getSystemIcons() {
3837        return mSystemIcons;
3838    }
3839
3840    public LinearLayout getSystemIconArea() {
3841        return mSystemIconArea;
3842    }
3843
3844    @Override
3845    public void setBouncerShowing(boolean bouncerShowing) {
3846        super.setBouncerShowing(bouncerShowing);
3847        disable(mDisabledUnmodified, true /* animate */);
3848    }
3849
3850    public void onScreenTurnedOff() {
3851        mStackScroller.setAnimationsEnabled(false);
3852    }
3853
3854    public void onScreenTurnedOn() {
3855        mStackScroller.setAnimationsEnabled(true);
3856        mNotificationPanel.onScreenTurnedOn();
3857    }
3858
3859    /**
3860     * This handles long-press of both back and recents.  They are
3861     * handled together to capture them both being long-pressed
3862     * at the same time to exit screen pinning (lock task).
3863     *
3864     * When accessibility mode is on, only a long-press from recents
3865     * is required to exit.
3866     *
3867     * In all other circumstances we try to pass through long-press events
3868     * for Back, so that apps can still use it.  Which can be from two things.
3869     * 1) Not currently in screen pinning (lock task).
3870     * 2) Back is long-pressed without recents.
3871     */
3872    private void handleLongPressBackRecents(View v) {
3873        try {
3874            boolean sendBackLongPress = false;
3875            IActivityManager activityManager = ActivityManagerNative.getDefault();
3876            boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
3877            if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
3878                long time = System.currentTimeMillis();
3879                // If we recently long-pressed the other button then they were
3880                // long-pressed 'together'
3881                if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
3882                    activityManager.stopLockTaskModeOnCurrent();
3883                } else if ((v.getId() == R.id.back)
3884                        && !mNavigationBarView.getRecentsButton().isPressed()) {
3885                    // If we aren't pressing recents right now then they presses
3886                    // won't be together, so send the standard long-press action.
3887                    sendBackLongPress = true;
3888                }
3889                mLastLockToAppLongPress = time;
3890            } else {
3891                // If this is back still need to handle sending the long-press event.
3892                if (v.getId() == R.id.back) {
3893                    sendBackLongPress = true;
3894                } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
3895                    // When in accessibility mode a long press that is recents (not back)
3896                    // should stop lock task.
3897                    activityManager.stopLockTaskModeOnCurrent();
3898                }
3899            }
3900            if (sendBackLongPress) {
3901                KeyButtonView keyButtonView = (KeyButtonView) v;
3902                keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
3903                keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
3904            }
3905        } catch (RemoteException e) {
3906            Log.d(TAG, "Unable to reach activity manager", e);
3907        }
3908    }
3909
3910    // Recents
3911
3912    @Override
3913    protected void showRecents(boolean triggeredFromAltTab) {
3914        // Set the recents visibility flag
3915        mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
3916        notifyUiVisibilityChanged(mSystemUiVisibility);
3917        super.showRecents(triggeredFromAltTab);
3918    }
3919
3920    @Override
3921    protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
3922        // Unset the recents visibility flag
3923        mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
3924        notifyUiVisibilityChanged(mSystemUiVisibility);
3925        super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
3926    }
3927
3928    @Override
3929    protected void toggleRecents() {
3930        // Toggle the recents visibility flag
3931        mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
3932        notifyUiVisibilityChanged(mSystemUiVisibility);
3933        super.toggleRecents();
3934    }
3935
3936    @Override
3937    public void onVisibilityChanged(boolean visible) {
3938        // Update the recents visibility flag
3939        if (visible) {
3940            mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
3941        } else {
3942            mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
3943        }
3944        notifyUiVisibilityChanged(mSystemUiVisibility);
3945    }
3946
3947    public boolean hasActiveNotifications() {
3948        return !mNotificationData.getActiveNotifications().isEmpty();
3949    }
3950
3951    public void wakeUpIfDozing(long time) {
3952        if (mDozeServiceHost != null && mDozeServiceHost.isDozing()) {
3953            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
3954            pm.wakeUp(time);
3955        }
3956    }
3957
3958    private final class ShadeUpdates {
3959        private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
3960        private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
3961
3962        public void check() {
3963            mNewVisibleNotifications.clear();
3964            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3965            for (int i = 0; i < activeNotifications.size(); i++) {
3966                final Entry entry = activeNotifications.get(i);
3967                final boolean visible = entry.row != null
3968                        && entry.row.getVisibility() == View.VISIBLE;
3969                if (visible) {
3970                    mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
3971                }
3972            }
3973            final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
3974            mVisibleNotifications.clear();
3975            mVisibleNotifications.addAll(mNewVisibleNotifications);
3976
3977            // We have new notifications
3978            if (updates && mDozeServiceHost != null) {
3979                mDozeServiceHost.fireNewNotifications();
3980            }
3981        }
3982    }
3983
3984    private final class DozeServiceHost implements DozeService.Host {
3985        // Amount of time to allow to update the time shown on the screen before releasing
3986        // the wakelock.  This timeout is design to compensate for the fact that we don't
3987        // currently have a way to know when time display contents have actually been
3988        // refreshed once we've finished rendering a new frame.
3989        private static final long PROCESSING_TIME = 500;
3990
3991        private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
3992        private final H mHandler = new H();
3993
3994        private DozeService mCurrentDozeService;
3995
3996        @Override
3997        public String toString() {
3998            return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + " mCurrentDozeService="
3999                    + mCurrentDozeService + "]";
4000        }
4001
4002        public boolean isDozing() {
4003            return mCurrentDozeService != null;
4004        }
4005
4006        public void firePowerSaveChanged(boolean active) {
4007            for (Callback callback : mCallbacks) {
4008                callback.onPowerSaveChanged(active);
4009            }
4010        }
4011
4012        public void fireBuzzBeepBlinked() {
4013            for (Callback callback : mCallbacks) {
4014                callback.onBuzzBeepBlinked();
4015            }
4016        }
4017
4018        public void fireNotificationLight(boolean on) {
4019            for (Callback callback : mCallbacks) {
4020                callback.onNotificationLight(on);
4021            }
4022        }
4023
4024        public void fireNewNotifications() {
4025            for (Callback callback : mCallbacks) {
4026                callback.onNewNotifications();
4027            }
4028        }
4029
4030        @Override
4031        public void addCallback(Callback callback) {
4032            mCallbacks.add(callback);
4033        }
4034
4035        @Override
4036        public void removeCallback(Callback callback) {
4037            mCallbacks.remove(callback);
4038        }
4039
4040        @Override
4041        public void requestDoze(DozeService dozeService) {
4042            if (dozeService == null) return;
4043            dozeService.stayAwake(PROCESSING_TIME);
4044            mHandler.obtainMessage(H.REQUEST_DOZE, dozeService).sendToTarget();
4045        }
4046
4047        @Override
4048        public void requestPulse(int pulses, boolean delayed, DozeService dozeService) {
4049            if (dozeService == null) return;
4050            dozeService.stayAwake(PROCESSING_TIME);
4051            mHandler.obtainMessage(H.REQUEST_PULSE, pulses, delayed ? 1 : 0, dozeService)
4052                    .sendToTarget();
4053        }
4054
4055        @Override
4056        public void dozingStopped(DozeService dozeService) {
4057            if (dozeService == null) return;
4058            dozeService.stayAwake(PROCESSING_TIME);
4059            mHandler.obtainMessage(H.DOZING_STOPPED, dozeService).sendToTarget();
4060        }
4061
4062        @Override
4063        public boolean isPowerSaveActive() {
4064            return mBatteryController != null && mBatteryController.isPowerSave();
4065        }
4066
4067        private void handleRequestDoze(DozeService dozeService) {
4068            mCurrentDozeService = dozeService;
4069            if (!mDozing) {
4070                mDozing = true;
4071                updateDozingState();
4072            }
4073            mCurrentDozeService.startDozing();
4074        }
4075
4076        private void handleRequestPulse(int pulses, boolean delayed, DozeService dozeService) {
4077            if (!dozeService.equals(mCurrentDozeService)) return;
4078            final long stayAwake = mScrimController.pulse(pulses, delayed);
4079            mCurrentDozeService.stayAwake(stayAwake);
4080        }
4081
4082        private void handleDozingStopped(DozeService dozeService) {
4083            if (dozeService.equals(mCurrentDozeService)) {
4084                mCurrentDozeService = null;
4085            }
4086            if (mDozing) {
4087                mDozing = false;
4088                updateDozingState();
4089            }
4090        }
4091
4092        private final class H extends Handler {
4093            private static final int REQUEST_DOZE = 1;
4094            private static final int REQUEST_PULSE = 2;
4095            private static final int DOZING_STOPPED = 3;
4096
4097            @Override
4098            public void handleMessage(Message msg) {
4099                if (msg.what == REQUEST_DOZE) {
4100                    handleRequestDoze((DozeService) msg.obj);
4101                } else if (msg.what == REQUEST_PULSE) {
4102                    handleRequestPulse(msg.arg1, msg.arg2 != 0, (DozeService) msg.obj);
4103                } else if (msg.what == DOZING_STOPPED) {
4104                    handleDozingStopped((DozeService) msg.obj);
4105                }
4106            }
4107        }
4108    }
4109}
4110