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