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