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