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