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