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