PhoneStatusBar.java revision 8e8a69e90fbc35f794fa9f6d3bd9e1227ed1a6e8
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    public 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 || !ActivityManager.supportsMultiWindow()) {
1153                return false;
1154            }
1155
1156            boolean initiallyDocked = WindowManagerProxy.getInstance().getDockSide()
1157                    == WindowManager.DOCKED_INVALID;
1158            boolean dockedAtEnd = toggleSplitScreenMode();
1159            if (dockedAtEnd != initiallyDocked) {
1160                int logAction = dockedAtEnd ? MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS
1161                        : MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS;
1162                MetricsLogger.action(mContext, logAction);
1163                return true;
1164            }
1165            return false;
1166        }
1167    };
1168
1169    @Override
1170    protected boolean toggleSplitScreenMode() {
1171        if (mRecents == null) {
1172            return false;
1173        }
1174        int dockSide = WindowManagerProxy.getInstance().getDockSide();
1175        if (dockSide == WindowManager.DOCKED_INVALID) {
1176            return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
1177                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null);
1178        } else {
1179            EventBus.getDefault().send(new UndockingTaskEvent());
1180            return false;
1181        }
1182    }
1183
1184    private final View.OnLongClickListener mLongPressHomeListener
1185            = new View.OnLongClickListener() {
1186        @Override
1187        public boolean onLongClick(View v) {
1188            if (shouldDisableNavbarGestures()) {
1189                return false;
1190            }
1191            MetricsLogger.action(mContext, MetricsEvent.ACTION_ASSIST_LONG_PRESS);
1192            mAssistManager.startAssist(new Bundle() /* args */);
1193            awakenDreams();
1194            if (mNavigationBarView != null) {
1195                mNavigationBarView.abortCurrentGesture();
1196            }
1197            return true;
1198        }
1199    };
1200
1201    private final View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
1202        public boolean onTouch(View v, MotionEvent event) {
1203            switch (event.getAction()) {
1204                case MotionEvent.ACTION_UP:
1205                case MotionEvent.ACTION_CANCEL:
1206                    awakenDreams();
1207                    break;
1208            }
1209            return false;
1210        }
1211    };
1212
1213    private void awakenDreams() {
1214        if (mDreamManager != null) {
1215            try {
1216                mDreamManager.awaken();
1217            } catch (RemoteException e) {
1218                // fine, stay asleep then
1219            }
1220        }
1221    }
1222
1223    private void prepareNavigationBarView() {
1224        mNavigationBarView.reorient();
1225
1226        ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
1227        recentsButton.setOnClickListener(mRecentsClickListener);
1228        recentsButton.setOnTouchListener(mRecentsPreloadOnTouchListener);
1229        recentsButton.setLongClickable(true);
1230        recentsButton.setOnLongClickListener(mRecentsLongClickListener);
1231
1232        ButtonDispatcher backButton = mNavigationBarView.getBackButton();
1233        backButton.setLongClickable(true);
1234        backButton.setOnLongClickListener(mLongPressBackListener);
1235
1236        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
1237        homeButton.setOnTouchListener(mHomeActionListener);
1238        homeButton.setOnLongClickListener(mLongPressHomeListener);
1239
1240        mAssistManager.onConfigurationChanged();
1241    }
1242
1243    // For small-screen devices (read: phones) that lack hardware navigation buttons
1244    protected void addNavigationBar() {
1245        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
1246        if (mNavigationBarView == null) return;
1247
1248        prepareNavigationBarView();
1249
1250        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
1251    }
1252
1253    protected void repositionNavigationBar() {
1254        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
1255
1256        prepareNavigationBarView();
1257
1258        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
1259    }
1260
1261    private void notifyNavigationBarScreenOn(boolean screenOn) {
1262        if (mNavigationBarView == null) return;
1263        mNavigationBarView.notifyScreenOn(screenOn);
1264    }
1265
1266    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
1267        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1268                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
1269                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
1270                    0
1271                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1272                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1273                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1274                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1275                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1276                PixelFormat.TRANSLUCENT);
1277        // this will allow the navbar to run in an overlay on devices that support this
1278        if (ActivityManager.isHighEndGfx()) {
1279            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1280        }
1281
1282        lp.setTitle("NavigationBar");
1283        lp.windowAnimations = 0;
1284        return lp;
1285    }
1286
1287    @Override
1288    public void setIcon(String slot, StatusBarIcon icon) {
1289        mIconController.setIcon(slot, icon);
1290    }
1291
1292    @Override
1293    public void removeIcon(String slot) {
1294        mIconController.removeIcon(slot);
1295    }
1296
1297    public UserHandle getCurrentUserHandle() {
1298        return new UserHandle(mCurrentUserId);
1299    }
1300
1301    @Override
1302    public void addNotification(StatusBarNotification notification, RankingMap ranking,
1303            Entry oldEntry) {
1304        if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
1305
1306        mNotificationData.updateRanking(ranking);
1307        Entry shadeEntry = createNotificationViews(notification);
1308        if (shadeEntry == null) {
1309            return;
1310        }
1311        boolean isHeadsUped = mUseHeadsUp && shouldPeek(shadeEntry);
1312        if (isHeadsUped) {
1313            mHeadsUpManager.showNotification(shadeEntry);
1314            // Mark as seen immediately
1315            setNotificationShown(notification);
1316        }
1317
1318        if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
1319            if (shouldSuppressFullScreenIntent(notification.getKey())) {
1320                if (DEBUG) {
1321                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
1322                }
1323            } else if (mNotificationData.getImportance(notification.getKey())
1324                    < NotificationListenerService.Ranking.IMPORTANCE_MAX) {
1325                if (DEBUG) {
1326                    Log.d(TAG, "No Fullscreen intent: not important enough: "
1327                            + notification.getKey());
1328                }
1329            } else {
1330                // Stop screensaver if the notification has a full-screen intent.
1331                // (like an incoming phone call)
1332                awakenDreams();
1333
1334                // not immersive & a full-screen alert should be shown
1335                if (DEBUG)
1336                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1337                try {
1338                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
1339                            notification.getKey());
1340                    notification.getNotification().fullScreenIntent.send();
1341                    shadeEntry.notifyFullScreenIntentLaunched();
1342                    MetricsLogger.count(mContext, "note_fullscreen", 1);
1343                } catch (PendingIntent.CanceledException e) {
1344                }
1345            }
1346        }
1347        addNotificationViews(shadeEntry, ranking);
1348        // Recalculate the position of the sliding windows and the titles.
1349        setAreThereNotifications();
1350    }
1351
1352    private boolean shouldSuppressFullScreenIntent(String key) {
1353        if (mPowerManager.isInteractive()) {
1354            return mNotificationData.shouldSuppressScreenOn(key);
1355        } else {
1356            return mNotificationData.shouldSuppressScreenOff(key);
1357        }
1358    }
1359
1360    @Override
1361    protected void updateNotificationRanking(RankingMap ranking) {
1362        mNotificationData.updateRanking(ranking);
1363        updateNotifications();
1364    }
1365
1366    @Override
1367    public void removeNotification(String key, RankingMap ranking) {
1368        boolean deferRemoval = false;
1369        if (mHeadsUpManager.isHeadsUp(key)) {
1370            deferRemoval = !mHeadsUpManager.removeNotification(key);
1371        }
1372        if (key.equals(mMediaNotificationKey)) {
1373            clearCurrentMediaNotification();
1374            updateMediaMetaData(true, true);
1375        }
1376        if (deferRemoval) {
1377            mLatestRankingMap = ranking;
1378            mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
1379            return;
1380        }
1381        StatusBarNotification old = removeNotificationViews(key, ranking);
1382        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1383
1384        if (old != null) {
1385            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
1386                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
1387                if (mState == StatusBarState.SHADE) {
1388                    animateCollapsePanels();
1389                } else if (mState == StatusBarState.SHADE_LOCKED) {
1390                    goToKeyguard();
1391                }
1392            }
1393        }
1394        setAreThereNotifications();
1395    }
1396
1397    @Override
1398    protected void refreshLayout(int layoutDirection) {
1399        if (mNavigationBarView != null) {
1400            mNavigationBarView.setLayoutDirection(layoutDirection);
1401        }
1402    }
1403
1404    private void updateNotificationShade() {
1405        if (mStackScroller == null) return;
1406
1407        // Do not modify the notifications during collapse.
1408        if (isCollapsing()) {
1409            addPostCollapseAction(new Runnable() {
1410                @Override
1411                public void run() {
1412                    updateNotificationShade();
1413                }
1414            });
1415            return;
1416        }
1417
1418        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1419        ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
1420        final int N = activeNotifications.size();
1421        for (int i=0; i<N; i++) {
1422            Entry ent = activeNotifications.get(i);
1423            int vis = ent.notification.getNotification().visibility;
1424
1425            // Display public version of the notification if we need to redact.
1426            final boolean hideSensitive =
1427                    !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
1428            boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
1429            boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
1430            boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
1431            boolean showingPublic = sensitive && isLockscreenPublicMode();
1432            if (showingPublic) {
1433                updatePublicContentView(ent, ent.notification);
1434            }
1435            ent.row.setSensitive(sensitive, hideSensitive);
1436            if (ent.autoRedacted && ent.legacy) {
1437                // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
1438                // for legacy auto redacted notifications.
1439                if (showingPublic) {
1440                    ent.row.setShowingLegacyBackground(false);
1441                } else {
1442                    ent.row.setShowingLegacyBackground(true);
1443                }
1444            }
1445            if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
1446                ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
1447                        ent.row.getStatusBarNotification());
1448                List<ExpandableNotificationRow> orderedChildren =
1449                        mTmpChildOrderMap.get(summary);
1450                if (orderedChildren == null) {
1451                    orderedChildren = new ArrayList<>();
1452                    mTmpChildOrderMap.put(summary, orderedChildren);
1453                }
1454                orderedChildren.add(ent.row);
1455            } else {
1456                toShow.add(ent.row);
1457            }
1458
1459        }
1460
1461        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
1462        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1463            View child = mStackScroller.getChildAt(i);
1464            if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1465                toRemove.add((ExpandableNotificationRow) child);
1466            }
1467        }
1468
1469        for (ExpandableNotificationRow remove : toRemove) {
1470            if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
1471                // we are only transfering this notification to its parent, don't generate an animation
1472                mStackScroller.setChildTransferInProgress(true);
1473            }
1474            mStackScroller.removeView(remove);
1475            mStackScroller.setChildTransferInProgress(false);
1476        }
1477
1478        removeNotificationChildren();
1479
1480        for (int i=0; i<toShow.size(); i++) {
1481            View v = toShow.get(i);
1482            if (v.getParent() == null) {
1483                mStackScroller.addView(v);
1484            }
1485        }
1486
1487        addNotificationChildrenAndSort();
1488
1489        // So after all this work notifications still aren't sorted correctly.
1490        // Let's do that now by advancing through toShow and mStackScroller in
1491        // lock-step, making sure mStackScroller matches what we see in toShow.
1492        int j = 0;
1493        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1494            View child = mStackScroller.getChildAt(i);
1495            if (!(child instanceof ExpandableNotificationRow)) {
1496                // We don't care about non-notification views.
1497                continue;
1498            }
1499
1500            ExpandableNotificationRow targetChild = toShow.get(j);
1501            if (child != targetChild) {
1502                // Oops, wrong notification at this position. Put the right one
1503                // here and advance both lists.
1504                mStackScroller.changeViewPosition(targetChild, i);
1505            }
1506            j++;
1507
1508        }
1509
1510        // clear the map again for the next usage
1511        mTmpChildOrderMap.clear();
1512
1513        updateRowStates();
1514        updateSpeedbump();
1515        updateClearAll();
1516        updateEmptyShadeView();
1517
1518        updateQsExpansionEnabled();
1519        mShadeUpdates.check();
1520    }
1521
1522    /**
1523     * Disable QS if device not provisioned.
1524     * If the user switcher is simple then disable QS during setup because
1525     * the user intends to use the lock screen user switcher, QS in not needed.
1526     */
1527    private void updateQsExpansionEnabled() {
1528        mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
1529                && (mUserSetup || mUserSwitcherController == null
1530                        || !mUserSwitcherController.isSimpleUserSwitcher())
1531                && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
1532                && !ONLY_CORE_APPS);
1533    }
1534
1535    private void addNotificationChildrenAndSort() {
1536        // Let's now add all notification children which are missing
1537        boolean orderChanged = false;
1538        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1539            View view = mStackScroller.getChildAt(i);
1540            if (!(view instanceof ExpandableNotificationRow)) {
1541                // We don't care about non-notification views.
1542                continue;
1543            }
1544
1545            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
1546            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
1547            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
1548
1549            for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
1550                    childIndex++) {
1551                ExpandableNotificationRow childView = orderedChildren.get(childIndex);
1552                if (children == null || !children.contains(childView)) {
1553                    parent.addChildNotification(childView, childIndex);
1554                    mStackScroller.notifyGroupChildAdded(childView);
1555                }
1556            }
1557
1558            // Finally after removing and adding has been beformed we can apply the order.
1559            orderChanged |= parent.applyChildOrder(orderedChildren);
1560        }
1561        if (orderChanged) {
1562            mStackScroller.generateChildOrderChangedEvent();
1563        }
1564    }
1565
1566    private void removeNotificationChildren() {
1567        // First let's remove all children which don't belong in the parents
1568        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
1569        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1570            View view = mStackScroller.getChildAt(i);
1571            if (!(view instanceof ExpandableNotificationRow)) {
1572                // We don't care about non-notification views.
1573                continue;
1574            }
1575
1576            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
1577            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
1578            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
1579
1580            if (children != null) {
1581                toRemove.clear();
1582                for (ExpandableNotificationRow childRow : children) {
1583                    if (orderedChildren == null || !orderedChildren.contains(childRow)) {
1584                        toRemove.add(childRow);
1585                    }
1586                }
1587                for (ExpandableNotificationRow remove : toRemove) {
1588                    parent.removeChildNotification(remove);
1589                    if (mNotificationData.get(remove.getStatusBarNotification().getKey()) == null) {
1590                        // We only want to add an animation if the view is completely removed
1591                        // otherwise it's just a transfer
1592                        mStackScroller.notifyGroupChildRemoved(remove);
1593                    }
1594                }
1595            }
1596        }
1597    }
1598
1599    @Override
1600    public void addQsTile(ComponentName tile) {
1601        mQSPanel.getHost().addTile(tile);
1602    }
1603
1604    @Override
1605    public void remQsTile(ComponentName tile) {
1606        mQSPanel.getHost().removeTile(tile);
1607    }
1608
1609    @Override
1610    public void clickTile(ComponentName tile) {
1611        mQSPanel.clickTile(tile);
1612    }
1613
1614    private boolean packageHasVisibilityOverride(String key) {
1615        return mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_PRIVATE;
1616    }
1617
1618    private void updateClearAll() {
1619        boolean showDismissView =
1620                mState != StatusBarState.KEYGUARD &&
1621                mNotificationData.hasActiveClearableNotifications();
1622        mStackScroller.updateDismissView(showDismissView);
1623    }
1624
1625    private void updateEmptyShadeView() {
1626        boolean showEmptyShade =
1627                mState != StatusBarState.KEYGUARD &&
1628                        mNotificationData.getActiveNotifications().size() == 0;
1629        mNotificationPanel.setShadeEmpty(showEmptyShade);
1630    }
1631
1632    private void updateSpeedbump() {
1633        int speedbumpIndex = -1;
1634        int currentIndex = 0;
1635        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1636        final int N = activeNotifications.size();
1637        for (int i = 0; i < N; i++) {
1638            Entry entry = activeNotifications.get(i);
1639            boolean isChild = !isTopLevelChild(entry);
1640            if (isChild) {
1641                continue;
1642            }
1643            if (entry.row.getVisibility() != View.GONE &&
1644                    mNotificationData.isAmbient(entry.key)) {
1645                speedbumpIndex = currentIndex;
1646                break;
1647            }
1648            currentIndex++;
1649        }
1650        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
1651    }
1652
1653    public static boolean isTopLevelChild(Entry entry) {
1654        return entry.row.getParent() instanceof NotificationStackScrollLayout;
1655    }
1656
1657    @Override
1658    protected void updateNotifications() {
1659        mNotificationData.filterAndSort();
1660
1661        updateNotificationShade();
1662        mIconController.updateNotificationIcons(mNotificationData);
1663    }
1664
1665    public void requestNotificationUpdate() {
1666        updateNotifications();
1667    }
1668
1669    @Override
1670    protected void setAreThereNotifications() {
1671
1672        if (SPEW) {
1673            final boolean clearable = hasActiveNotifications() &&
1674                    mNotificationData.hasActiveClearableNotifications();
1675            Log.d(TAG, "setAreThereNotifications: N=" +
1676                    mNotificationData.getActiveNotifications().size() + " any=" +
1677                    hasActiveNotifications() + " clearable=" + clearable);
1678        }
1679
1680        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1681        final boolean showDot = hasActiveNotifications() && !areLightsOn();
1682        if (showDot != (nlo.getAlpha() == 1.0f)) {
1683            if (showDot) {
1684                nlo.setAlpha(0f);
1685                nlo.setVisibility(View.VISIBLE);
1686            }
1687            nlo.animate()
1688                .alpha(showDot?1:0)
1689                .setDuration(showDot?750:250)
1690                .setInterpolator(new AccelerateInterpolator(2.0f))
1691                .setListener(showDot ? null : new AnimatorListenerAdapter() {
1692                    @Override
1693                    public void onAnimationEnd(Animator _a) {
1694                        nlo.setVisibility(View.GONE);
1695                    }
1696                })
1697                .start();
1698        }
1699
1700        findAndUpdateMediaNotifications();
1701    }
1702
1703    public void findAndUpdateMediaNotifications() {
1704        boolean metaDataChanged = false;
1705
1706        synchronized (mNotificationData) {
1707            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1708            final int N = activeNotifications.size();
1709
1710            // Promote the media notification with a controller in 'playing' state, if any.
1711            Entry mediaNotification = null;
1712            MediaController controller = null;
1713            for (int i = 0; i < N; i++) {
1714                final Entry entry = activeNotifications.get(i);
1715                if (isMediaNotification(entry)) {
1716                    final MediaSession.Token token =
1717                            entry.notification.getNotification().extras
1718                            .getParcelable(Notification.EXTRA_MEDIA_SESSION);
1719                    if (token != null) {
1720                        MediaController aController = new MediaController(mContext, token);
1721                        if (PlaybackState.STATE_PLAYING ==
1722                                getMediaControllerPlaybackState(aController)) {
1723                            if (DEBUG_MEDIA) {
1724                                Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
1725                                        + entry.notification.getKey());
1726                            }
1727                            mediaNotification = entry;
1728                            controller = aController;
1729                            break;
1730                        }
1731                    }
1732                }
1733            }
1734            if (mediaNotification == null) {
1735                // Still nothing? OK, let's just look for live media sessions and see if they match
1736                // one of our notifications. This will catch apps that aren't (yet!) using media
1737                // notifications.
1738
1739                if (mMediaSessionManager != null) {
1740                    final List<MediaController> sessions
1741                            = mMediaSessionManager.getActiveSessionsForUser(
1742                                    null,
1743                                    UserHandle.USER_ALL);
1744
1745                    for (MediaController aController : sessions) {
1746                        if (PlaybackState.STATE_PLAYING ==
1747                                getMediaControllerPlaybackState(aController)) {
1748                            // now to see if we have one like this
1749                            final String pkg = aController.getPackageName();
1750
1751                            for (int i = 0; i < N; i++) {
1752                                final Entry entry = activeNotifications.get(i);
1753                                if (entry.notification.getPackageName().equals(pkg)) {
1754                                    if (DEBUG_MEDIA) {
1755                                        Log.v(TAG, "DEBUG_MEDIA: found controller matching "
1756                                            + entry.notification.getKey());
1757                                    }
1758                                    controller = aController;
1759                                    mediaNotification = entry;
1760                                    break;
1761                                }
1762                            }
1763                        }
1764                    }
1765                }
1766            }
1767
1768            if (controller != null && !sameSessions(mMediaController, controller)) {
1769                // We have a new media session
1770                clearCurrentMediaNotification();
1771                mMediaController = controller;
1772                mMediaController.registerCallback(mMediaListener);
1773                mMediaMetadata = mMediaController.getMetadata();
1774                if (DEBUG_MEDIA) {
1775                    Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
1776                            + mMediaMetadata);
1777                }
1778
1779                if (mediaNotification != null) {
1780                    mMediaNotificationKey = mediaNotification.notification.getKey();
1781                    if (DEBUG_MEDIA) {
1782                        Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
1783                                + mMediaNotificationKey + " controller=" + mMediaController);
1784                    }
1785                }
1786                metaDataChanged = true;
1787            }
1788        }
1789
1790        if (metaDataChanged) {
1791            updateNotifications();
1792        }
1793        updateMediaMetaData(metaDataChanged, true);
1794    }
1795
1796    private int getMediaControllerPlaybackState(MediaController controller) {
1797        if (controller != null) {
1798            final PlaybackState playbackState = controller.getPlaybackState();
1799            if (playbackState != null) {
1800                return playbackState.getState();
1801            }
1802        }
1803        return PlaybackState.STATE_NONE;
1804    }
1805
1806    private boolean isPlaybackActive(int state) {
1807        if (state != PlaybackState.STATE_STOPPED
1808                && state != PlaybackState.STATE_ERROR
1809                && state != PlaybackState.STATE_NONE) {
1810            return true;
1811        }
1812        return false;
1813    }
1814
1815    private void clearCurrentMediaNotification() {
1816        mMediaNotificationKey = null;
1817        mMediaMetadata = null;
1818        if (mMediaController != null) {
1819            if (DEBUG_MEDIA) {
1820                Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
1821                        + mMediaController.getPackageName());
1822            }
1823            mMediaController.unregisterCallback(mMediaListener);
1824        }
1825        mMediaController = null;
1826    }
1827
1828    private boolean sameSessions(MediaController a, MediaController b) {
1829        if (a == b) return true;
1830        if (a == null) return false;
1831        return a.controlsSameSession(b);
1832    }
1833
1834    /**
1835     * Hide the album artwork that is fading out and release its bitmap.
1836     */
1837    private Runnable mHideBackdropFront = new Runnable() {
1838        @Override
1839        public void run() {
1840            if (DEBUG_MEDIA) {
1841                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
1842            }
1843            mBackdropFront.setVisibility(View.INVISIBLE);
1844            mBackdropFront.animate().cancel();
1845            mBackdropFront.setImageDrawable(null);
1846        }
1847    };
1848
1849    /**
1850     * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
1851     */
1852    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
1853        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
1854
1855        if (mBackdrop == null) return; // called too early
1856
1857        if (mLaunchTransitionFadingAway) {
1858            mBackdrop.setVisibility(View.INVISIBLE);
1859            return;
1860        }
1861
1862        if (DEBUG_MEDIA) {
1863            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
1864                    + " metadata=" + mMediaMetadata
1865                    + " metaDataChanged=" + metaDataChanged
1866                    + " state=" + mState);
1867        }
1868
1869        Bitmap artworkBitmap = null;
1870        if (mMediaMetadata != null) {
1871            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
1872            if (artworkBitmap == null) {
1873                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
1874                // might still be null
1875            }
1876        }
1877        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkBitmap == null) {
1878            artworkBitmap = mLockscreenWallpaper.getBitmap();
1879        }
1880
1881        final boolean hasArtwork = artworkBitmap != null;
1882
1883        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE
1884                && mFingerprintUnlockController.getMode()
1885                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
1886            // time to show some art!
1887            if (mBackdrop.getVisibility() != View.VISIBLE) {
1888                mBackdrop.setVisibility(View.VISIBLE);
1889                if (allowEnterAnimation) {
1890                    mBackdrop.animate().alpha(1f);
1891                } else {
1892                    mBackdrop.animate().cancel();
1893                    mBackdrop.setAlpha(1f);
1894                }
1895                metaDataChanged = true;
1896                if (DEBUG_MEDIA) {
1897                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
1898                }
1899            }
1900            if (metaDataChanged) {
1901                if (mBackdropBack.getDrawable() != null) {
1902                    Drawable drawable =
1903                            mBackdropBack.getDrawable().getConstantState().newDrawable().mutate();
1904                    mBackdropFront.setImageDrawable(drawable);
1905                    if (mScrimSrcModeEnabled) {
1906                        mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
1907                    }
1908                    mBackdropFront.setAlpha(1f);
1909                    mBackdropFront.setVisibility(View.VISIBLE);
1910                } else {
1911                    mBackdropFront.setVisibility(View.INVISIBLE);
1912                }
1913
1914                if (DEBUG_MEDIA_FAKE_ARTWORK) {
1915                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
1916                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
1917                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
1918                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
1919                } else {
1920                    mBackdropBack.setImageBitmap(artworkBitmap);
1921                }
1922                if (mScrimSrcModeEnabled) {
1923                    mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
1924                }
1925
1926                if (mBackdropFront.getVisibility() == View.VISIBLE) {
1927                    if (DEBUG_MEDIA) {
1928                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
1929                                + mBackdropFront.getDrawable()
1930                                + " to "
1931                                + mBackdropBack.getDrawable());
1932                    }
1933                    mBackdropFront.animate()
1934                            .setDuration(250)
1935                            .alpha(0f).withEndAction(mHideBackdropFront);
1936                }
1937            }
1938        } else {
1939            // need to hide the album art, either because we are unlocked or because
1940            // the metadata isn't there to support it
1941            if (mBackdrop.getVisibility() != View.GONE) {
1942                if (DEBUG_MEDIA) {
1943                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
1944                }
1945                if (mFingerprintUnlockController.getMode()
1946                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
1947
1948                    // We are unlocking directly - no animation!
1949                    mBackdrop.setVisibility(View.GONE);
1950                    mBackdropBack.setImageDrawable(null);
1951                } else {
1952                    mBackdrop.animate()
1953                            // Never let the alpha become zero - otherwise the RenderNode
1954                            // won't draw anything and uninitialized memory will show through
1955                            // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
1956                            // libhwui.
1957                            .alpha(0.002f)
1958                            .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
1959                            .setDuration(300)
1960                            .setStartDelay(0)
1961                            .withEndAction(new Runnable() {
1962                                @Override
1963                                public void run() {
1964                                    mBackdrop.setVisibility(View.GONE);
1965                                    mBackdropFront.animate().cancel();
1966                                    mBackdropBack.setImageDrawable(null);
1967                                    mHandler.post(mHideBackdropFront);
1968                                }
1969                            });
1970                    if (mKeyguardFadingAway) {
1971                        mBackdrop.animate()
1972
1973                                // Make it disappear faster, as the focus should be on the activity
1974                                // behind.
1975                                .setDuration(mKeyguardFadingAwayDuration / 2)
1976                                .setStartDelay(mKeyguardFadingAwayDelay)
1977                                .setInterpolator(Interpolators.LINEAR)
1978                                .start();
1979                    }
1980                }
1981            }
1982        }
1983    }
1984
1985    private int adjustDisableFlags(int state) {
1986        if (!mLaunchTransitionFadingAway && !mKeyguardFadingAway
1987                && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
1988            state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
1989            state |= StatusBarManager.DISABLE_SYSTEM_INFO;
1990        }
1991        return state;
1992    }
1993
1994    /**
1995     * State is one or more of the DISABLE constants from StatusBarManager.
1996     */
1997    public void disable(int state1, int state2, boolean animate) {
1998        animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
1999        mDisabledUnmodified1 = state1;
2000        mDisabledUnmodified2 = state2;
2001        state1 = adjustDisableFlags(state1);
2002        final int old1 = mDisabled1;
2003        final int diff1 = state1 ^ old1;
2004        mDisabled1 = state1;
2005
2006        final int old2 = mDisabled2;
2007        final int diff2 = state2 ^ old2;
2008        mDisabled2 = state2;
2009
2010        if (DEBUG) {
2011            Log.d(TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
2012                old1, state1, diff1));
2013            Log.d(TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
2014                old2, state2, diff2));
2015        }
2016
2017        StringBuilder flagdbg = new StringBuilder();
2018        flagdbg.append("disable: < ");
2019        flagdbg.append(((state1 & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
2020        flagdbg.append(((diff1  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
2021        flagdbg.append(((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
2022        flagdbg.append(((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
2023        flagdbg.append(((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
2024        flagdbg.append(((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
2025        flagdbg.append(((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
2026        flagdbg.append(((diff1  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
2027        flagdbg.append(((state1 & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
2028        flagdbg.append(((diff1  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
2029        flagdbg.append(((state1 & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
2030        flagdbg.append(((diff1  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
2031        flagdbg.append(((state1 & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
2032        flagdbg.append(((diff1  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
2033        flagdbg.append(((state1 & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
2034        flagdbg.append(((diff1  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
2035        flagdbg.append(((state1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
2036        flagdbg.append(((diff1  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
2037        flagdbg.append(((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "QUICK_SETTINGS"
2038                : "quick_settings");
2039        flagdbg.append(((diff2  & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "* " : " ");
2040        flagdbg.append(">");
2041        Log.d(TAG, flagdbg.toString());
2042
2043        if ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
2044            if ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
2045                mIconController.hideSystemIconArea(animate);
2046            } else {
2047                mIconController.showSystemIconArea(animate);
2048            }
2049        }
2050
2051        if ((diff1 & StatusBarManager.DISABLE_CLOCK) != 0) {
2052            boolean visible = (state1 & StatusBarManager.DISABLE_CLOCK) == 0;
2053            mIconController.setClockVisibility(visible);
2054        }
2055        if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
2056            if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
2057                animateCollapsePanels();
2058            }
2059        }
2060
2061        if ((diff1 & (StatusBarManager.DISABLE_HOME
2062                        | StatusBarManager.DISABLE_RECENT
2063                        | StatusBarManager.DISABLE_BACK
2064                        | StatusBarManager.DISABLE_SEARCH)) != 0) {
2065            // the nav bar will take care of these
2066            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
2067
2068            if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) {
2069                // close recents if it's visible
2070                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2071                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2072            }
2073        }
2074
2075        if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2076            if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2077                mIconController.hideNotificationIconArea(animate);
2078            } else {
2079                mIconController.showNotificationIconArea(animate);
2080            }
2081        }
2082
2083        if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
2084            mDisableNotificationAlerts =
2085                    (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
2086            mHeadsUpObserver.onChange(true);
2087        }
2088
2089        if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
2090            updateQsExpansionEnabled();
2091        }
2092    }
2093
2094    @Override
2095    protected BaseStatusBar.H createHandler() {
2096        return new PhoneStatusBar.H();
2097    }
2098
2099    @Override
2100    public void startActivity(Intent intent, boolean dismissShade) {
2101        startActivityDismissingKeyguard(intent, false, dismissShade);
2102    }
2103
2104    @Override
2105    public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
2106        startActivityDismissingKeyguard(intent, false, dismissShade, callback);
2107    }
2108
2109    @Override
2110    public void preventNextAnimation() {
2111        overrideActivityPendingAppTransition(true /* keyguardShowing */);
2112    }
2113
2114    public void setQsExpanded(boolean expanded) {
2115        mStatusBarWindowManager.setQsExpanded(expanded);
2116        mKeyguardStatusView.setImportantForAccessibility(expanded
2117                ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
2118                : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
2119    }
2120
2121    public boolean isGoingToNotificationShade() {
2122        return mLeaveOpenOnKeyguardHide;
2123    }
2124
2125    public boolean isQsExpanded() {
2126        return mNotificationPanel.isQsExpanded();
2127    }
2128
2129    public boolean isWakeUpComingFromTouch() {
2130        return mWakeUpComingFromTouch;
2131    }
2132
2133    public boolean isFalsingThresholdNeeded() {
2134        return getBarState() == StatusBarState.KEYGUARD;
2135    }
2136
2137    public boolean isDozing() {
2138        return mDozing;
2139    }
2140
2141    @Override  // NotificationData.Environment
2142    public String getCurrentMediaNotificationKey() {
2143        return mMediaNotificationKey;
2144    }
2145
2146    public boolean isScrimSrcModeEnabled() {
2147        return mScrimSrcModeEnabled;
2148    }
2149
2150    /**
2151     * To be called when there's a state change in StatusBarKeyguardViewManager.
2152     */
2153    public void onKeyguardViewManagerStatesUpdated() {
2154        logStateToEventlog();
2155    }
2156
2157    @Override  // UnlockMethodCache.OnUnlockMethodChangedListener
2158    public void onUnlockMethodStateChanged() {
2159        logStateToEventlog();
2160    }
2161
2162    @Override
2163    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
2164        if (inPinnedMode) {
2165            mStatusBarWindowManager.setHeadsUpShowing(true);
2166            mStatusBarWindowManager.setForceStatusBarVisible(true);
2167            if (mNotificationPanel.isFullyCollapsed()) {
2168                // We need to ensure that the touchable region is updated before the window will be
2169                // resized, in order to not catch any touches. A layout will ensure that
2170                // onComputeInternalInsets will be called and after that we can resize the layout. Let's
2171                // make sure that the window stays small for one frame until the touchableRegion is set.
2172                mNotificationPanel.requestLayout();
2173                mStatusBarWindowManager.setForceWindowCollapsed(true);
2174                mNotificationPanel.post(new Runnable() {
2175                    @Override
2176                    public void run() {
2177                        mStatusBarWindowManager.setForceWindowCollapsed(false);
2178                    }
2179                });
2180            }
2181        } else {
2182            if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) {
2183                // We are currently tracking or is open and the shade doesn't need to be kept
2184                // open artificially.
2185                mStatusBarWindowManager.setHeadsUpShowing(false);
2186            } else {
2187                // we need to keep the panel open artificially, let's wait until the animation
2188                // is finished.
2189                mHeadsUpManager.setHeadsUpGoingAway(true);
2190                mStackScroller.runAfterAnimationFinished(new Runnable() {
2191                    @Override
2192                    public void run() {
2193                        if (!mHeadsUpManager.hasPinnedHeadsUp()) {
2194                            mStatusBarWindowManager.setHeadsUpShowing(false);
2195                            mHeadsUpManager.setHeadsUpGoingAway(false);
2196                        }
2197                    }
2198                });
2199            }
2200        }
2201    }
2202
2203    @Override
2204    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
2205        dismissVolumeDialog();
2206    }
2207
2208    @Override
2209    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
2210    }
2211
2212    @Override
2213    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
2214        if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
2215            removeNotification(entry.key, mLatestRankingMap);
2216            mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2217            if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
2218                mLatestRankingMap = null;
2219            }
2220        } else {
2221            updateNotificationRanking(null);
2222        }
2223
2224    }
2225
2226    protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2227            boolean alertAgain) {
2228        final boolean wasHeadsUp = isHeadsUp(key);
2229        if (wasHeadsUp) {
2230            if (!shouldPeek) {
2231                // We don't want this to be interrupting anymore, lets remove it
2232                mHeadsUpManager.removeNotification(key);
2233            } else {
2234                mHeadsUpManager.updateNotification(entry, alertAgain);
2235            }
2236        } else if (shouldPeek && alertAgain) {
2237            // This notification was updated to be a heads-up, show it!
2238            mHeadsUpManager.showNotification(entry);
2239        }
2240    }
2241
2242    protected void setHeadsUpUser(int newUserId) {
2243        if (mHeadsUpManager != null) {
2244            mHeadsUpManager.setUser(newUserId);
2245        }
2246    }
2247
2248    public boolean isHeadsUp(String key) {
2249        return mHeadsUpManager.isHeadsUp(key);
2250    }
2251
2252    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
2253        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
2254    }
2255
2256    public boolean isKeyguardCurrentlySecure() {
2257        return !mUnlockMethodCache.canSkipBouncer();
2258    }
2259
2260    public void setPanelExpanded(boolean isExpanded) {
2261        mStatusBarWindowManager.setPanelExpanded(isExpanded);
2262    }
2263
2264    public void onScreenTurnedOff() {
2265        mFalsingManager.onScreenOff();
2266    }
2267
2268    /**
2269     * All changes to the status bar and notifications funnel through here and are batched.
2270     */
2271    private class H extends BaseStatusBar.H {
2272        public void handleMessage(Message m) {
2273            super.handleMessage(m);
2274            switch (m.what) {
2275                case MSG_OPEN_NOTIFICATION_PANEL:
2276                    animateExpandNotificationsPanel();
2277                    break;
2278                case MSG_OPEN_SETTINGS_PANEL:
2279                    animateExpandSettingsPanel((String) m.obj);
2280                    break;
2281                case MSG_CLOSE_PANELS:
2282                    animateCollapsePanels();
2283                    break;
2284                case MSG_LAUNCH_TRANSITION_TIMEOUT:
2285                    onLaunchTransitionTimeout();
2286                    break;
2287            }
2288        }
2289    }
2290
2291    @Override
2292    public void maybeEscalateHeadsUp() {
2293        Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
2294        for (HeadsUpManager.HeadsUpEntry entry : entries) {
2295            final StatusBarNotification sbn = entry.entry.notification;
2296            final Notification notification = sbn.getNotification();
2297            if (notification.fullScreenIntent != null) {
2298                if (DEBUG) {
2299                    Log.d(TAG, "converting a heads up to fullScreen");
2300                }
2301                try {
2302                    EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
2303                            sbn.getKey());
2304                    notification.fullScreenIntent.send();
2305                    entry.entry.notifyFullScreenIntentLaunched();
2306                } catch (PendingIntent.CanceledException e) {
2307                }
2308            }
2309        }
2310        mHeadsUpManager.releaseAllImmediately();
2311    }
2312
2313    boolean panelsEnabled() {
2314        return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS;
2315    }
2316
2317    void makeExpandedVisible(boolean force) {
2318        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
2319        if (!force && (mExpandedVisible || !panelsEnabled())) {
2320            return;
2321        }
2322
2323        mExpandedVisible = true;
2324        if (mNavigationBarView != null)
2325            mNavigationBarView.setSlippery(true);
2326
2327        // Expand the window to encompass the full screen in anticipation of the drag.
2328        // This is only possible to do atomically because the status bar is at the top of the screen!
2329        mStatusBarWindowManager.setPanelVisible(true);
2330
2331        visibilityChanged(true);
2332        mWaitingForKeyguardExit = false;
2333        disable(mDisabledUnmodified1, mDisabledUnmodified2, !force /* animate */);
2334        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2335    }
2336
2337    public void animateCollapsePanels() {
2338        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2339    }
2340
2341    private final Runnable mAnimateCollapsePanels = new Runnable() {
2342        @Override
2343        public void run() {
2344            animateCollapsePanels();
2345        }
2346    };
2347
2348    public void postAnimateCollapsePanels() {
2349        mHandler.post(mAnimateCollapsePanels);
2350    }
2351
2352    public void postAnimateOpenPanels() {
2353        mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
2354    }
2355
2356    public void animateCollapsePanels(int flags) {
2357        animateCollapsePanels(flags, false /* force */, false /* delayed */,
2358                1.0f /* speedUpFactor */);
2359    }
2360
2361    public void animateCollapsePanels(int flags, boolean force) {
2362        animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
2363    }
2364
2365    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
2366        animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
2367    }
2368
2369    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
2370            float speedUpFactor) {
2371        if (!force && mState != StatusBarState.SHADE) {
2372            runPostCollapseRunnables();
2373            return;
2374        }
2375        if (SPEW) {
2376            Log.d(TAG, "animateCollapse():"
2377                    + " mExpandedVisible=" + mExpandedVisible
2378                    + " flags=" + flags);
2379        }
2380
2381        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
2382            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
2383                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2384                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2385            }
2386        }
2387
2388        if (mStatusBarWindow != null) {
2389            // release focus immediately to kick off focus change transition
2390            mStatusBarWindowManager.setStatusBarFocusable(false);
2391
2392            mStatusBarWindow.cancelExpandHelper();
2393            mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
2394        }
2395    }
2396
2397    private void runPostCollapseRunnables() {
2398        ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables);
2399        mPostCollapseRunnables.clear();
2400        int size = clonedList.size();
2401        for (int i = 0; i < size; i++) {
2402            clonedList.get(i).run();
2403        }
2404
2405    }
2406
2407    @Override
2408    public void animateExpandNotificationsPanel() {
2409        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2410        if (!panelsEnabled()) {
2411            return ;
2412        }
2413
2414        mNotificationPanel.expand(true /* animate */);
2415
2416        if (false) postStartTracing();
2417    }
2418
2419    @Override
2420    public void animateExpandSettingsPanel(String subPanel) {
2421        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2422        if (!panelsEnabled()) {
2423            return;
2424        }
2425
2426        // Settings are not available in setup
2427        if (!mUserSetup) return;
2428
2429
2430        if (subPanel != null) {
2431            mQSPanel.openDetails(subPanel);
2432        }
2433        mNotificationPanel.expandWithQs();
2434
2435        if (false) postStartTracing();
2436    }
2437
2438    public void animateCollapseQuickSettings() {
2439        if (mState == StatusBarState.SHADE) {
2440            mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
2441        }
2442    }
2443
2444    void makeExpandedInvisible() {
2445        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
2446                + " mExpandedVisible=" + mExpandedVisible);
2447
2448        if (!mExpandedVisible || mStatusBarWindow == null) {
2449            return;
2450        }
2451
2452        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
2453        mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
2454                1.0f /* speedUpFactor */);
2455
2456        mNotificationPanel.closeQs();
2457
2458        mExpandedVisible = false;
2459        if (mNavigationBarView != null)
2460            mNavigationBarView.setSlippery(false);
2461        visibilityChanged(false);
2462
2463        // Shrink the window to the size of the status bar only
2464        mStatusBarWindowManager.setPanelVisible(false);
2465        mStatusBarWindowManager.setForceStatusBarVisible(false);
2466
2467        // Close any "App info" popups that might have snuck on-screen
2468        dismissPopups();
2469
2470        runPostCollapseRunnables();
2471        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2472        showBouncer();
2473        disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
2474
2475        // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
2476        // the bouncer appear animation.
2477        if (!mStatusBarKeyguardViewManager.isShowing()) {
2478            WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
2479        }
2480    }
2481
2482    public boolean interceptTouchEvent(MotionEvent event) {
2483        if (DEBUG_GESTURES) {
2484            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
2485                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
2486                        event.getActionMasked(), (int) event.getX(), (int) event.getY(),
2487                        mDisabled1, mDisabled2);
2488            }
2489
2490        }
2491
2492        if (SPEW) {
2493            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
2494                + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking);
2495        } else if (CHATTY) {
2496            if (event.getAction() != MotionEvent.ACTION_MOVE) {
2497                Log.d(TAG, String.format(
2498                            "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x",
2499                            MotionEvent.actionToString(event.getAction()),
2500                            event.getRawX(), event.getRawY(), mDisabled1, mDisabled2));
2501            }
2502        }
2503
2504        if (DEBUG_GESTURES) {
2505            mGestureRec.add(event);
2506        }
2507
2508        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
2509            final boolean upOrCancel =
2510                    event.getAction() == MotionEvent.ACTION_UP ||
2511                    event.getAction() == MotionEvent.ACTION_CANCEL;
2512            if (upOrCancel && !mExpandedVisible) {
2513                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2514            } else {
2515                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2516            }
2517        }
2518        return false;
2519    }
2520
2521    public GestureRecorder getGestureRecorder() {
2522        return mGestureRec;
2523    }
2524
2525    private void setNavigationIconHints(int hints) {
2526        if (hints == mNavigationIconHints) return;
2527
2528        mNavigationIconHints = hints;
2529
2530        if (mNavigationBarView != null) {
2531            mNavigationBarView.setNavigationIconHints(hints);
2532        }
2533        checkBarModes();
2534    }
2535
2536    @Override // CommandQueue
2537    public void setWindowState(int window, int state) {
2538        boolean showing = state == WINDOW_STATE_SHOWING;
2539        if (mStatusBarWindow != null
2540                && window == StatusBarManager.WINDOW_STATUS_BAR
2541                && mStatusBarWindowState != state) {
2542            mStatusBarWindowState = state;
2543            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
2544            if (!showing && mState == StatusBarState.SHADE) {
2545                mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
2546                        1.0f /* speedUpFactor */);
2547            }
2548        }
2549        if (mNavigationBarView != null
2550                && window == StatusBarManager.WINDOW_NAVIGATION_BAR
2551                && mNavigationBarWindowState != state) {
2552            mNavigationBarWindowState = state;
2553            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
2554        }
2555    }
2556
2557    @Override // CommandQueue
2558    public void buzzBeepBlinked() {
2559        if (mDozeServiceHost != null) {
2560            mDozeServiceHost.fireBuzzBeepBlinked();
2561        }
2562    }
2563
2564    @Override
2565    public void notificationLightOff() {
2566        if (mDozeServiceHost != null) {
2567            mDozeServiceHost.fireNotificationLight(false);
2568        }
2569    }
2570
2571    @Override
2572    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
2573        if (mDozeServiceHost != null) {
2574            mDozeServiceHost.fireNotificationLight(true);
2575        }
2576    }
2577
2578    @Override // CommandQueue
2579    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
2580            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
2581        final int oldVal = mSystemUiVisibility;
2582        final int newVal = (oldVal&~mask) | (vis&mask);
2583        final int diff = newVal ^ oldVal;
2584        if (DEBUG) Log.d(TAG, String.format(
2585                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
2586                Integer.toHexString(vis), Integer.toHexString(mask),
2587                Integer.toHexString(oldVal), Integer.toHexString(newVal),
2588                Integer.toHexString(diff)));
2589        boolean sbModeChanged = false;
2590        if (diff != 0) {
2591            // we never set the recents bit via this method, so save the prior state to prevent
2592            // clobbering the bit below
2593            final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
2594
2595            mSystemUiVisibility = newVal;
2596
2597            // update low profile
2598            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
2599                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
2600                if (lightsOut) {
2601                    animateCollapsePanels();
2602                }
2603
2604                setAreThereNotifications();
2605            }
2606
2607            // ready to unhide
2608            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
2609                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
2610                mNoAnimationOnNextBarModeChange = true;
2611            }
2612
2613            // update status bar mode
2614            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
2615                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT,
2616                    View.STATUS_BAR_TRANSPARENT);
2617
2618            // update navigation bar mode
2619            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
2620                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
2621                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
2622                    View.NAVIGATION_BAR_TRANSPARENT);
2623            sbModeChanged = sbMode != -1;
2624            final boolean nbModeChanged = nbMode != -1;
2625            boolean checkBarModes = false;
2626            if (sbModeChanged && sbMode != mStatusBarMode) {
2627                mStatusBarMode = sbMode;
2628                checkBarModes = true;
2629            }
2630            if (nbModeChanged && nbMode != mNavigationBarMode) {
2631                mNavigationBarMode = nbMode;
2632                checkBarModes = true;
2633            }
2634            if (checkBarModes) {
2635                checkBarModes();
2636            }
2637            if (sbModeChanged || nbModeChanged) {
2638                // update transient bar autohide
2639                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
2640                    scheduleAutohide();
2641                } else {
2642                    cancelAutohide();
2643                }
2644            }
2645
2646            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
2647                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
2648            }
2649
2650            // restore the recents bit
2651            if (wasRecentsVisible) {
2652                mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
2653            }
2654
2655            // send updated sysui visibility to window manager
2656            notifyUiVisibilityChanged(mSystemUiVisibility);
2657        }
2658
2659        mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
2660                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
2661    }
2662
2663    private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
2664            int transientFlag, int translucentFlag, int transparentFlag) {
2665        final int oldMode = barMode(oldVis, transientFlag, translucentFlag, transparentFlag);
2666        final int newMode = barMode(newVis, transientFlag, translucentFlag, transparentFlag);
2667        if (oldMode == newMode) {
2668            return -1; // no mode change
2669        }
2670        return newMode;
2671    }
2672
2673    private int barMode(int vis, int transientFlag, int translucentFlag, int transparentFlag) {
2674        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | transparentFlag;
2675        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
2676                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
2677                : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
2678                : (vis & transparentFlag) != 0 ? MODE_TRANSPARENT
2679                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
2680                : MODE_OPAQUE;
2681    }
2682
2683    private void checkBarModes() {
2684        if (mDemoMode) return;
2685        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions(),
2686                mNoAnimationOnNextBarModeChange);
2687        if (mNavigationBarView != null) {
2688            checkBarMode(mNavigationBarMode,
2689                    mNavigationBarWindowState, mNavigationBarView.getBarTransitions(),
2690                    mNoAnimationOnNextBarModeChange);
2691        }
2692        mNoAnimationOnNextBarModeChange = false;
2693    }
2694
2695    private void checkBarMode(int mode, int windowState, BarTransitions transitions,
2696            boolean noAnimation) {
2697        final boolean powerSave = mBatteryController.isPowerSave();
2698        final boolean anim = !noAnimation && mDeviceInteractive
2699                && windowState != WINDOW_STATE_HIDDEN && !powerSave;
2700        if (powerSave && getBarState() == StatusBarState.SHADE) {
2701            mode = MODE_WARNING;
2702        }
2703        transitions.transitionTo(mode, anim);
2704    }
2705
2706    private void finishBarAnimations() {
2707        mStatusBarView.getBarTransitions().finishAnimations();
2708        if (mNavigationBarView != null) {
2709            mNavigationBarView.getBarTransitions().finishAnimations();
2710        }
2711    }
2712
2713    private final Runnable mCheckBarModes = new Runnable() {
2714        @Override
2715        public void run() {
2716            checkBarModes();
2717        }
2718    };
2719
2720    @Override
2721    public void setInteracting(int barWindow, boolean interacting) {
2722        final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
2723        mInteractingWindows = interacting
2724                ? (mInteractingWindows | barWindow)
2725                : (mInteractingWindows & ~barWindow);
2726        if (mInteractingWindows != 0) {
2727            suspendAutohide();
2728        } else {
2729            resumeSuspendedAutohide();
2730        }
2731        // manually dismiss the volume panel when interacting with the nav bar
2732        if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
2733            dismissVolumeDialog();
2734        }
2735        checkBarModes();
2736    }
2737
2738    private void dismissVolumeDialog() {
2739        if (mVolumeComponent != null) {
2740            mVolumeComponent.dismissNow();
2741        }
2742    }
2743
2744    private void resumeSuspendedAutohide() {
2745        if (mAutohideSuspended) {
2746            scheduleAutohide();
2747            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
2748        }
2749    }
2750
2751    private void suspendAutohide() {
2752        mHandler.removeCallbacks(mAutohide);
2753        mHandler.removeCallbacks(mCheckBarModes);
2754        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
2755    }
2756
2757    private void cancelAutohide() {
2758        mAutohideSuspended = false;
2759        mHandler.removeCallbacks(mAutohide);
2760    }
2761
2762    private void scheduleAutohide() {
2763        cancelAutohide();
2764        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2765    }
2766
2767    private void checkUserAutohide(View v, MotionEvent event) {
2768        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
2769                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2770                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
2771                ) {
2772            userAutohide();
2773        }
2774    }
2775
2776    private void userAutohide() {
2777        cancelAutohide();
2778        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2779    }
2780
2781    private boolean areLightsOn() {
2782        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2783    }
2784
2785    public void setLightsOn(boolean on) {
2786        Log.v(TAG, "setLightsOn(" + on + ")");
2787        if (on) {
2788            setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
2789                    mLastFullscreenStackBounds, mLastDockedStackBounds);
2790        } else {
2791            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
2792                    View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
2793                    mLastDockedStackBounds);
2794        }
2795    }
2796
2797    private void notifyUiVisibilityChanged(int vis) {
2798        try {
2799            if (mLastDispatchedSystemUiVisibility != vis) {
2800                mWindowManagerService.statusBarVisibilityChanged(vis);
2801                mLastDispatchedSystemUiVisibility = vis;
2802            }
2803        } catch (RemoteException ex) {
2804        }
2805    }
2806
2807    public void topAppWindowChanged(boolean showMenu) {
2808        if (DEBUG) {
2809            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2810        }
2811        if (mNavigationBarView != null) {
2812            mNavigationBarView.setMenuVisibility(showMenu);
2813        }
2814
2815        // See above re: lights-out policy for legacy apps.
2816        if (showMenu) setLightsOn(true);
2817    }
2818
2819    @Override
2820    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
2821            boolean showImeSwitcher) {
2822        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2823        int flags = mNavigationIconHints;
2824        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2825            flags |= NAVIGATION_HINT_BACK_ALT;
2826        } else {
2827            flags &= ~NAVIGATION_HINT_BACK_ALT;
2828        }
2829        if (showImeSwitcher) {
2830            flags |= NAVIGATION_HINT_IME_SHOWN;
2831        } else {
2832            flags &= ~NAVIGATION_HINT_IME_SHOWN;
2833        }
2834
2835        setNavigationIconHints(flags);
2836    }
2837
2838    public static String viewInfo(View v) {
2839        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2840                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2841    }
2842
2843    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2844        synchronized (mQueueLock) {
2845            pw.println("Current Status Bar state:");
2846            pw.println("  mExpandedVisible=" + mExpandedVisible
2847                    + ", mTrackingPosition=" + mTrackingPosition);
2848            pw.println("  mTracking=" + mTracking);
2849            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2850            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2851            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2852                    + " scroll " + mStackScroller.getScrollX()
2853                    + "," + mStackScroller.getScrollY());
2854        }
2855
2856        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2857        pw.print("  mStatusBarWindowState=");
2858        pw.println(windowStateToString(mStatusBarWindowState));
2859        pw.print("  mStatusBarMode=");
2860        pw.println(BarTransitions.modeToString(mStatusBarMode));
2861        pw.print("  mDozing="); pw.println(mDozing);
2862        pw.print("  mZenMode=");
2863        pw.println(Settings.Global.zenModeToString(mZenMode));
2864        pw.print("  mUseHeadsUp=");
2865        pw.println(mUseHeadsUp);
2866        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2867        if (mNavigationBarView != null) {
2868            pw.print("  mNavigationBarWindowState=");
2869            pw.println(windowStateToString(mNavigationBarWindowState));
2870            pw.print("  mNavigationBarMode=");
2871            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2872            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2873        }
2874
2875        pw.print("  mNavigationBarView=");
2876        if (mNavigationBarView == null) {
2877            pw.println("null");
2878        } else {
2879            mNavigationBarView.dump(fd, pw, args);
2880        }
2881
2882        pw.print("  mMediaSessionManager=");
2883        pw.println(mMediaSessionManager);
2884        pw.print("  mMediaNotificationKey=");
2885        pw.println(mMediaNotificationKey);
2886        pw.print("  mMediaController=");
2887        pw.print(mMediaController);
2888        if (mMediaController != null) {
2889            pw.print(" state=" + mMediaController.getPlaybackState());
2890        }
2891        pw.println();
2892        pw.print("  mMediaMetadata=");
2893        pw.print(mMediaMetadata);
2894        if (mMediaMetadata != null) {
2895            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
2896        }
2897        pw.println();
2898
2899        pw.println("  Panels: ");
2900        if (mNotificationPanel != null) {
2901            pw.println("    mNotificationPanel=" +
2902                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2903            pw.print  ("      ");
2904            mNotificationPanel.dump(fd, pw, args);
2905        }
2906
2907        DozeLog.dump(pw);
2908
2909        if (DUMPTRUCK) {
2910            synchronized (mNotificationData) {
2911                mNotificationData.dump(pw, "  ");
2912            }
2913
2914            mIconController.dump(pw);
2915
2916            if (false) {
2917                pw.println("see the logcat for a dump of the views we have created.");
2918                // must happen on ui thread
2919                mHandler.post(new Runnable() {
2920                        public void run() {
2921                            mStatusBarView.getLocationOnScreen(mAbsPos);
2922                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2923                                    + ") " + mStatusBarView.getWidth() + "x"
2924                                    + getStatusBarHeight());
2925                            mStatusBarView.debug();
2926                        }
2927                    });
2928            }
2929        }
2930
2931        if (DEBUG_GESTURES) {
2932            pw.print("  status bar gestures: ");
2933            mGestureRec.dump(fd, pw, args);
2934        }
2935        if (mStatusBarWindowManager != null) {
2936            mStatusBarWindowManager.dump(fd, pw, args);
2937        }
2938        if (mNetworkController != null) {
2939            mNetworkController.dump(fd, pw, args);
2940        }
2941        if (mBluetoothController != null) {
2942            mBluetoothController.dump(fd, pw, args);
2943        }
2944        if (mHotspotController != null) {
2945            mHotspotController.dump(fd, pw, args);
2946        }
2947        if (mCastController != null) {
2948            mCastController.dump(fd, pw, args);
2949        }
2950        if (mUserSwitcherController != null) {
2951            mUserSwitcherController.dump(fd, pw, args);
2952        }
2953        if (mBatteryController != null) {
2954            mBatteryController.dump(fd, pw, args);
2955        }
2956        if (mNextAlarmController != null) {
2957            mNextAlarmController.dump(fd, pw, args);
2958        }
2959        if (mSecurityController != null) {
2960            mSecurityController.dump(fd, pw, args);
2961        }
2962        if (mHeadsUpManager != null) {
2963            mHeadsUpManager.dump(fd, pw, args);
2964        } else {
2965            pw.println("  mHeadsUpManager: null");
2966        }
2967        if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
2968            KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
2969        }
2970
2971        FalsingManager.getInstance(mContext).dump(pw);
2972        FalsingLog.dump(pw);
2973
2974        pw.println("SharedPreferences:");
2975        for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
2976            pw.print("  "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
2977        }
2978    }
2979
2980    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2981        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
2982        pw.println(BarTransitions.modeToString(transitions.getMode()));
2983    }
2984
2985    @Override
2986    public void createAndAddWindows() {
2987        addStatusBarWindow();
2988    }
2989
2990    private void addStatusBarWindow() {
2991        makeStatusBarView();
2992        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2993        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
2994                mHeadsUpManager);
2995        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2996    }
2997
2998    // called by makeStatusbar and also by PhoneStatusBarView
2999    void updateDisplaySize() {
3000        mDisplay.getMetrics(mDisplayMetrics);
3001        mDisplay.getSize(mCurrentDisplaySize);
3002        if (DEBUG_GESTURES) {
3003            mGestureRec.tag("display",
3004                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
3005        }
3006    }
3007
3008    float getDisplayDensity() {
3009        return mDisplayMetrics.density;
3010    }
3011
3012    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
3013            boolean dismissShade) {
3014        startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, null /* callback */);
3015    }
3016
3017    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
3018            final boolean dismissShade, final Callback callback) {
3019        if (onlyProvisioned && !isDeviceProvisioned()) return;
3020
3021        final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
3022                mContext, intent, mCurrentUserId);
3023        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
3024        Runnable runnable = new Runnable() {
3025            public void run() {
3026                mAssistManager.hideAssist();
3027                intent.setFlags(
3028                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
3029                int result = ActivityManager.START_CANCELED;
3030                try {
3031                    result = ActivityManagerNative.getDefault().startActivityAsUser(
3032                            null, mContext.getBasePackageName(),
3033                            intent,
3034                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
3035                            null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
3036                            UserHandle.CURRENT.getIdentifier());
3037                } catch (RemoteException e) {
3038                    Log.w(TAG, "Unable to start activity", e);
3039                }
3040                overrideActivityPendingAppTransition(
3041                        keyguardShowing && !afterKeyguardGone);
3042                if (callback != null) {
3043                    callback.onActivityStarted(result);
3044                }
3045            }
3046        };
3047        Runnable cancelRunnable = new Runnable() {
3048            @Override
3049            public void run() {
3050                if (callback != null) {
3051                    callback.onActivityStarted(ActivityManager.START_CANCELED);
3052                }
3053            }
3054        };
3055        executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
3056                afterKeyguardGone);
3057    }
3058
3059    public void executeRunnableDismissingKeyguard(final Runnable runnable,
3060            final Runnable cancelAction,
3061            final boolean dismissShade,
3062            final boolean afterKeyguardGone) {
3063        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
3064        dismissKeyguardThenExecute(new OnDismissAction() {
3065            @Override
3066            public boolean onDismiss() {
3067                AsyncTask.execute(new Runnable() {
3068                    public void run() {
3069                        try {
3070                            if (keyguardShowing && !afterKeyguardGone) {
3071                                ActivityManagerNative.getDefault()
3072                                        .keyguardWaitingForActivityDrawn();
3073                            }
3074                            if (runnable != null) {
3075                                runnable.run();
3076                            }
3077                        } catch (RemoteException e) {
3078                        }
3079                    }
3080                });
3081                if (dismissShade) {
3082                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
3083                            true /* delayed*/);
3084                }
3085                return true;
3086            }
3087        }, cancelAction, afterKeyguardGone);
3088    }
3089
3090    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
3091        public void onReceive(Context context, Intent intent) {
3092            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3093            String action = intent.getAction();
3094            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
3095                getKeyboardShortcuts().dismissKeyboardShortcutsDialog();
3096                if (isCurrentProfile(getSendingUserId())) {
3097                    int flags = CommandQueue.FLAG_EXCLUDE_NONE;
3098                    String reason = intent.getStringExtra("reason");
3099                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
3100                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
3101                    }
3102                    animateCollapsePanels(flags);
3103                }
3104            }
3105            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
3106                notifyNavigationBarScreenOn(false);
3107                notifyHeadsUpScreenOff();
3108                finishBarAnimations();
3109                resetUserExpandedStates();
3110            }
3111            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
3112                notifyNavigationBarScreenOn(true);
3113            }
3114        }
3115    };
3116
3117    private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
3118        public void onReceive(Context context, Intent intent) {
3119            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3120            String action = intent.getAction();
3121            if (ACTION_DEMO.equals(action)) {
3122                Bundle bundle = intent.getExtras();
3123                if (bundle != null) {
3124                    String command = bundle.getString("command", "").trim().toLowerCase();
3125                    if (command.length() > 0) {
3126                        try {
3127                            dispatchDemoCommand(command, bundle);
3128                        } catch (Throwable t) {
3129                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
3130                        }
3131                    }
3132                }
3133            } else if (ACTION_FAKE_ARTWORK.equals(action)) {
3134                if (DEBUG_MEDIA_FAKE_ARTWORK) {
3135                    updateMediaMetaData(true, true);
3136                }
3137            }
3138        }
3139    };
3140
3141    public void resetUserExpandedStates() {
3142        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3143        final int notificationCount = activeNotifications.size();
3144        for (int i = 0; i < notificationCount; i++) {
3145            NotificationData.Entry entry = activeNotifications.get(i);
3146            if (entry.row != null) {
3147                entry.row.resetUserExpansion();
3148            }
3149        }
3150    }
3151
3152    @Override
3153    protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
3154        dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
3155    }
3156
3157    public void dismissKeyguard() {
3158        mStatusBarKeyguardViewManager.dismiss();
3159    }
3160
3161    private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
3162            boolean afterKeyguardGone) {
3163        if (mStatusBarKeyguardViewManager.isShowing()) {
3164            mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
3165                    afterKeyguardGone);
3166        } else {
3167            action.onDismiss();
3168        }
3169    }
3170
3171    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
3172    @Override
3173    protected void onConfigurationChanged(Configuration newConfig) {
3174        super.onConfigurationChanged(newConfig); // calls refreshLayout
3175
3176        if (DEBUG) {
3177            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
3178        }
3179        updateDisplaySize(); // populates mDisplayMetrics
3180
3181        updateResources();
3182        repositionNavigationBar();
3183        updateRowStates();
3184        mIconController.updateResources();
3185        mScreenPinningRequest.onConfigurationChanged();
3186        mNetworkController.onConfigurationChanged();
3187    }
3188
3189    @Override
3190    public void userSwitched(int newUserId) {
3191        super.userSwitched(newUserId);
3192        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
3193        animateCollapsePanels();
3194        updatePublicMode();
3195        updateNotifications();
3196        resetUserSetupObserver();
3197        setControllerUsers();
3198        clearCurrentMediaNotification();
3199        mLockscreenWallpaper.setCurrentUser(newUserId);
3200        updateMediaMetaData(true, false);
3201    }
3202
3203    private void setControllerUsers() {
3204        if (mZenModeController != null) {
3205            mZenModeController.setUserId(mCurrentUserId);
3206        }
3207        if (mSecurityController != null) {
3208            mSecurityController.onUserSwitched(mCurrentUserId);
3209        }
3210    }
3211
3212    private void resetUserSetupObserver() {
3213        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
3214        mUserSetupObserver.onChange(false);
3215        mContext.getContentResolver().registerContentObserver(
3216                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
3217                mUserSetupObserver, mCurrentUserId);
3218    }
3219
3220    /**
3221     * Reload some of our resources when the configuration changes.
3222     *
3223     * We don't reload everything when the configuration changes -- we probably
3224     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
3225     * meantime, just update the things that we know change.
3226     */
3227    void updateResources() {
3228        // Update the quick setting tiles
3229        if (mQSPanel != null) {
3230            mQSPanel.updateResources();
3231        }
3232
3233        loadDimens();
3234
3235        if (mNotificationPanel != null) {
3236            mNotificationPanel.updateResources();
3237        }
3238        if (mBrightnessMirrorController != null) {
3239            mBrightnessMirrorController.updateResources();
3240        }
3241    }
3242
3243    protected void loadDimens() {
3244        final Resources res = mContext.getResources();
3245
3246        mNaturalBarHeight = res.getDimensionPixelSize(
3247                com.android.internal.R.dimen.status_bar_height);
3248
3249        mMaxAllowedKeyguardNotifications = res.getInteger(R.integer.keyguard_max_notification_count);
3250
3251        if (DEBUG) Log.v(TAG, "updateResources");
3252    }
3253
3254    // Visibility reporting
3255
3256    @Override
3257    protected void handleVisibleToUserChanged(boolean visibleToUser) {
3258        if (visibleToUser) {
3259            super.handleVisibleToUserChanged(visibleToUser);
3260            startNotificationLogging();
3261        } else {
3262            stopNotificationLogging();
3263            super.handleVisibleToUserChanged(visibleToUser);
3264        }
3265    }
3266
3267    private void stopNotificationLogging() {
3268        // Report all notifications as invisible and turn down the
3269        // reporter.
3270        if (!mCurrentlyVisibleNotifications.isEmpty()) {
3271            logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(),
3272                    mCurrentlyVisibleNotifications);
3273            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
3274        }
3275        mHandler.removeCallbacks(mVisibilityReporter);
3276        mStackScroller.setChildLocationsChangedListener(null);
3277    }
3278
3279    private void startNotificationLogging() {
3280        mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
3281        // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
3282        // cause the scroller to emit child location events. Hence generate
3283        // one ourselves to guarantee that we're reporting visible
3284        // notifications.
3285        // (Note that in cases where the scroller does emit events, this
3286        // additional event doesn't break anything.)
3287        mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
3288    }
3289
3290    private void logNotificationVisibilityChanges(
3291            Collection<NotificationVisibility> newlyVisible,
3292            Collection<NotificationVisibility> noLongerVisible) {
3293        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
3294            return;
3295        }
3296        NotificationVisibility[] newlyVisibleAr =
3297                newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]);
3298        NotificationVisibility[] noLongerVisibleAr =
3299                noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]);
3300        try {
3301            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
3302        } catch (RemoteException e) {
3303            // Ignore.
3304        }
3305
3306        final int N = newlyVisible.size();
3307        if (N > 0) {
3308            String[] newlyVisibleKeyAr = new String[N];
3309            for (int i = 0; i < N; i++) {
3310                newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
3311            }
3312
3313            setNotificationsShown(newlyVisibleKeyAr);
3314        }
3315    }
3316
3317    // State logging
3318
3319    private void logStateToEventlog() {
3320        boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
3321        boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
3322        boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
3323        boolean isSecure = mUnlockMethodCache.isMethodSecure();
3324        boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer();
3325        int stateFingerprint = getLoggingFingerprint(mState,
3326                isShowing,
3327                isOccluded,
3328                isBouncerShowing,
3329                isSecure,
3330                canSkipBouncer);
3331        if (stateFingerprint != mLastLoggedStateFingerprint) {
3332            EventLogTags.writeSysuiStatusBarState(mState,
3333                    isShowing ? 1 : 0,
3334                    isOccluded ? 1 : 0,
3335                    isBouncerShowing ? 1 : 0,
3336                    isSecure ? 1 : 0,
3337                    canSkipBouncer ? 1 : 0);
3338            mLastLoggedStateFingerprint = stateFingerprint;
3339        }
3340    }
3341
3342    /**
3343     * Returns a fingerprint of fields logged to eventlog
3344     */
3345    private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing,
3346            boolean keyguardOccluded, boolean bouncerShowing, boolean secure,
3347            boolean currentlyInsecure) {
3348        // Reserve 8 bits for statusBarState. We'll never go higher than
3349        // that, right? Riiiight.
3350        return (statusBarState & 0xFF)
3351                | ((keyguardShowing   ? 1 : 0) <<  8)
3352                | ((keyguardOccluded  ? 1 : 0) <<  9)
3353                | ((bouncerShowing    ? 1 : 0) << 10)
3354                | ((secure            ? 1 : 0) << 11)
3355                | ((currentlyInsecure ? 1 : 0) << 12);
3356    }
3357
3358    //
3359    // tracing
3360    //
3361
3362    void postStartTracing() {
3363        mHandler.postDelayed(mStartTracing, 3000);
3364    }
3365
3366    void vibrate() {
3367        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
3368                Context.VIBRATOR_SERVICE);
3369        vib.vibrate(250, VIBRATION_ATTRIBUTES);
3370    }
3371
3372    Runnable mStartTracing = new Runnable() {
3373        public void run() {
3374            vibrate();
3375            SystemClock.sleep(250);
3376            Log.d(TAG, "startTracing");
3377            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
3378            mHandler.postDelayed(mStopTracing, 10000);
3379        }
3380    };
3381
3382    Runnable mStopTracing = new Runnable() {
3383        public void run() {
3384            android.os.Debug.stopMethodTracing();
3385            Log.d(TAG, "stopTracing");
3386            vibrate();
3387        }
3388    };
3389
3390    @Override
3391    public boolean shouldDisableNavbarGestures() {
3392        return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0;
3393    }
3394
3395    public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
3396        mHandler.post(new Runnable() {
3397            @Override
3398            public void run() {
3399                mLeaveOpenOnKeyguardHide = true;
3400                executeRunnableDismissingKeyguard(runnable, null, false, true);
3401            }
3402        });
3403    }
3404
3405    public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
3406        mHandler.post(new Runnable() {
3407            @Override
3408            public void run() {
3409                startPendingIntentDismissingKeyguard(intent);
3410            }
3411        });
3412    }
3413
3414    public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
3415        mHandler.postDelayed(new Runnable() {
3416            @Override
3417            public void run() {
3418                handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/);
3419            }
3420        }, delay);
3421    }
3422
3423    private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
3424        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
3425    }
3426
3427    private static class FastColorDrawable extends Drawable {
3428        private final int mColor;
3429
3430        public FastColorDrawable(int color) {
3431            mColor = 0xff000000 | color;
3432        }
3433
3434        @Override
3435        public void draw(Canvas canvas) {
3436            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
3437        }
3438
3439        @Override
3440        public void setAlpha(int alpha) {
3441        }
3442
3443        @Override
3444        public void setColorFilter(ColorFilter colorFilter) {
3445        }
3446
3447        @Override
3448        public int getOpacity() {
3449            return PixelFormat.OPAQUE;
3450        }
3451
3452        @Override
3453        public void setBounds(int left, int top, int right, int bottom) {
3454        }
3455
3456        @Override
3457        public void setBounds(Rect bounds) {
3458        }
3459    }
3460
3461    @Override
3462    public void destroy() {
3463        super.destroy();
3464        if (mStatusBarWindow != null) {
3465            mWindowManager.removeViewImmediate(mStatusBarWindow);
3466            mStatusBarWindow = null;
3467        }
3468        if (mNavigationBarView != null) {
3469            mWindowManager.removeViewImmediate(mNavigationBarView);
3470            mNavigationBarView = null;
3471        }
3472        if (mHandlerThread != null) {
3473            mHandlerThread.quitSafely();
3474            mHandlerThread = null;
3475        }
3476        mContext.unregisterReceiver(mBroadcastReceiver);
3477        mContext.unregisterReceiver(mDemoReceiver);
3478        mAssistManager.destroy();
3479
3480        final SignalClusterView signalCluster =
3481                (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
3482        final SignalClusterView signalClusterKeyguard =
3483                (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
3484        final SignalClusterView signalClusterQs =
3485                (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
3486        mNetworkController.removeSignalCallback(signalCluster);
3487        mNetworkController.removeSignalCallback(signalClusterKeyguard);
3488        mNetworkController.removeSignalCallback(signalClusterQs);
3489        if (mQSPanel != null && mQSPanel.getHost() != null) {
3490            mQSPanel.getHost().destroy();
3491        }
3492    }
3493
3494    private boolean mDemoModeAllowed;
3495    private boolean mDemoMode;
3496
3497    @Override
3498    public void dispatchDemoCommand(String command, Bundle args) {
3499        if (!mDemoModeAllowed) {
3500            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
3501                    DEMO_MODE_ALLOWED, 0) != 0;
3502        }
3503        if (!mDemoModeAllowed) return;
3504        if (command.equals(COMMAND_ENTER)) {
3505            mDemoMode = true;
3506        } else if (command.equals(COMMAND_EXIT)) {
3507            mDemoMode = false;
3508            checkBarModes();
3509        } else if (!mDemoMode) {
3510            // automatically enter demo mode on first demo command
3511            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
3512        }
3513        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
3514        if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) {
3515            mVolumeComponent.dispatchDemoCommand(command, args);
3516        }
3517        if (modeChange || command.equals(COMMAND_CLOCK)) {
3518            dispatchDemoCommandToView(command, args, R.id.clock);
3519        }
3520        if (modeChange || command.equals(COMMAND_BATTERY)) {
3521            dispatchDemoCommandToView(command, args, R.id.battery);
3522        }
3523        if (modeChange || command.equals(COMMAND_STATUS)) {
3524            mIconController.dispatchDemoCommand(command, args);
3525
3526        }
3527        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
3528            mNetworkController.dispatchDemoCommand(command, args);
3529        }
3530        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
3531            View notifications = mStatusBarView == null ? null
3532                    : mStatusBarView.findViewById(R.id.notification_icon_area);
3533            if (notifications != null) {
3534                String visible = args.getString("visible");
3535                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
3536                notifications.setVisibility(vis);
3537            }
3538        }
3539        if (command.equals(COMMAND_BARS)) {
3540            String mode = args.getString("mode");
3541            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
3542                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
3543                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
3544                    "transparent".equals(mode) ? MODE_TRANSPARENT :
3545                    "warning".equals(mode) ? MODE_WARNING :
3546                    -1;
3547            if (barMode != -1) {
3548                boolean animate = true;
3549                if (mStatusBarView != null) {
3550                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
3551                }
3552                if (mNavigationBarView != null) {
3553                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
3554                }
3555            }
3556        }
3557    }
3558
3559    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
3560        if (mStatusBarView == null) return;
3561        View v = mStatusBarView.findViewById(id);
3562        if (v instanceof DemoMode) {
3563            ((DemoMode)v).dispatchDemoCommand(command, args);
3564        }
3565    }
3566
3567    /**
3568     * @return The {@link StatusBarState} the status bar is in.
3569     */
3570    public int getBarState() {
3571        return mState;
3572    }
3573
3574    @Override
3575    public boolean isPanelFullyCollapsed() {
3576        return mNotificationPanel.isFullyCollapsed();
3577    }
3578
3579    public void showKeyguard() {
3580        if (mLaunchTransitionFadingAway) {
3581            mNotificationPanel.animate().cancel();
3582            onLaunchTransitionFadingEnded();
3583        }
3584        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
3585        if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
3586            setBarState(StatusBarState.FULLSCREEN_USER_SWITCHER);
3587        } else {
3588            setBarState(StatusBarState.KEYGUARD);
3589        }
3590        updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3591        if (!mDeviceInteractive) {
3592
3593            // If the screen is off already, we need to disable touch events because these might
3594            // collapse the panel after we expanded it, and thus we would end up with a blank
3595            // Keyguard.
3596            mNotificationPanel.setTouchDisabled(true);
3597        }
3598        if (mState == StatusBarState.KEYGUARD) {
3599            instantExpandNotificationsPanel();
3600        } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
3601            instantCollapseNotificationPanel();
3602        }
3603        mLeaveOpenOnKeyguardHide = false;
3604        if (mDraggedDownRow != null) {
3605            mDraggedDownRow.setUserLocked(false);
3606            mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
3607            mDraggedDownRow = null;
3608        }
3609        mPendingRemoteInputView = null;
3610        mAssistManager.onLockscreenShown();
3611    }
3612
3613    private void onLaunchTransitionFadingEnded() {
3614        mNotificationPanel.setAlpha(1.0f);
3615        mNotificationPanel.onAffordanceLaunchEnded();
3616        releaseGestureWakeLock();
3617        runLaunchTransitionEndRunnable();
3618        mLaunchTransitionFadingAway = false;
3619        mScrimController.forceHideScrims(false /* hide */);
3620        updateMediaMetaData(true /* metaDataChanged */, true);
3621    }
3622
3623    public boolean isCollapsing() {
3624        return mNotificationPanel.isCollapsing();
3625    }
3626
3627    public void addPostCollapseAction(Runnable r) {
3628        mPostCollapseRunnables.add(r);
3629    }
3630
3631    public boolean isInLaunchTransition() {
3632        return mNotificationPanel.isLaunchTransitionRunning()
3633                || mNotificationPanel.isLaunchTransitionFinished();
3634    }
3635
3636    /**
3637     * Fades the content of the keyguard away after the launch transition is done.
3638     *
3639     * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
3640     *                     starts
3641     * @param endRunnable the runnable to be run when the transition is done
3642     */
3643    public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
3644            Runnable endRunnable) {
3645        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
3646        mLaunchTransitionEndRunnable = endRunnable;
3647        Runnable hideRunnable = new Runnable() {
3648            @Override
3649            public void run() {
3650                mLaunchTransitionFadingAway = true;
3651                if (beforeFading != null) {
3652                    beforeFading.run();
3653                }
3654                mScrimController.forceHideScrims(true /* hide */);
3655                updateMediaMetaData(false, true);
3656                mNotificationPanel.setAlpha(1);
3657                mNotificationPanel.animate()
3658                        .alpha(0)
3659                        .setStartDelay(FADE_KEYGUARD_START_DELAY)
3660                        .setDuration(FADE_KEYGUARD_DURATION)
3661                        .withLayer()
3662                        .withEndAction(new Runnable() {
3663                            @Override
3664                            public void run() {
3665                                onLaunchTransitionFadingEnded();
3666                            }
3667                        });
3668                mIconController.appTransitionStarting(SystemClock.uptimeMillis(),
3669                        StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION);
3670            }
3671        };
3672        if (mNotificationPanel.isLaunchTransitionRunning()) {
3673            mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
3674        } else {
3675            hideRunnable.run();
3676        }
3677    }
3678
3679    /**
3680     * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
3681     * fading.
3682     */
3683    public void fadeKeyguardWhilePulsing() {
3684        mNotificationPanel.animate()
3685                .alpha(0f)
3686                .setStartDelay(0)
3687                .setDuration(FADE_KEYGUARD_DURATION_PULSING)
3688                .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR)
3689                .start();
3690    }
3691
3692    /**
3693     * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
3694     * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
3695     * because the launched app crashed or something else went wrong.
3696     */
3697    public void startLaunchTransitionTimeout() {
3698        mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT,
3699                LAUNCH_TRANSITION_TIMEOUT_MS);
3700    }
3701
3702    private void onLaunchTransitionTimeout() {
3703        Log.w(TAG, "Launch transition: Timeout!");
3704        mNotificationPanel.onAffordanceLaunchEnded();
3705        releaseGestureWakeLock();
3706        mNotificationPanel.resetViews();
3707    }
3708
3709    private void runLaunchTransitionEndRunnable() {
3710        if (mLaunchTransitionEndRunnable != null) {
3711            Runnable r = mLaunchTransitionEndRunnable;
3712
3713            // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again,
3714            // which would lead to infinite recursion. Protect against it.
3715            mLaunchTransitionEndRunnable = null;
3716            r.run();
3717        }
3718    }
3719
3720    /**
3721     * @return true if we would like to stay in the shade, false if it should go away entirely
3722     */
3723    public boolean hideKeyguard() {
3724        boolean staying = mLeaveOpenOnKeyguardHide;
3725        setBarState(StatusBarState.SHADE);
3726        View viewToClick = null;
3727        if (mLeaveOpenOnKeyguardHide) {
3728            mLeaveOpenOnKeyguardHide = false;
3729            long delay = calculateGoingToFullShadeDelay();
3730            mNotificationPanel.animateToFullShade(delay);
3731            if (mDraggedDownRow != null) {
3732                mDraggedDownRow.setUserLocked(false);
3733                mDraggedDownRow = null;
3734            }
3735            viewToClick = mPendingRemoteInputView;
3736            mPendingRemoteInputView = null;
3737
3738            // Disable layout transitions in navbar for this transition because the load is just
3739            // too heavy for the CPU and GPU on any device.
3740            if (mNavigationBarView != null) {
3741                mNavigationBarView.setLayoutTransitionsEnabled(false);
3742                mNavigationBarView.postDelayed(new Runnable() {
3743                    @Override
3744                    public void run() {
3745                        mNavigationBarView.setLayoutTransitionsEnabled(true);
3746                    }
3747                }, delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
3748            }
3749        } else {
3750            instantCollapseNotificationPanel();
3751        }
3752        updateKeyguardState(staying, false /* fromShadeLocked */);
3753
3754        if (viewToClick != null) {
3755            viewToClick.callOnClick();
3756        }
3757
3758        // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
3759        // visibilities so next time we open the panel we know the correct height already.
3760        if (mQSPanel != null) {
3761            mQSPanel.refreshAllTiles();
3762        }
3763        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
3764        releaseGestureWakeLock();
3765        mNotificationPanel.onAffordanceLaunchEnded();
3766        mNotificationPanel.animate().cancel();
3767        mNotificationPanel.setAlpha(1f);
3768        return staying;
3769    }
3770
3771    private void releaseGestureWakeLock() {
3772        if (mGestureWakeLock.isHeld()) {
3773            mGestureWakeLock.release();
3774        }
3775    }
3776
3777    public long calculateGoingToFullShadeDelay() {
3778        return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
3779    }
3780
3781    /**
3782     * Notifies the status bar that Keyguard is going away very soon.
3783     */
3784    public void keyguardGoingAway() {
3785
3786        // Treat Keyguard exit animation as an app transition to achieve nice transition for status
3787        // bar.
3788        mKeyguardGoingAway = true;
3789        mIconController.appTransitionPending();
3790    }
3791
3792    /**
3793     * Notifies the status bar the Keyguard is fading away with the specified timings.
3794     *
3795     * @param startTime the start time of the animations in uptime millis
3796     * @param delay the precalculated animation delay in miliseconds
3797     * @param fadeoutDuration the duration of the exit animation, in milliseconds
3798     */
3799    public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
3800        mKeyguardFadingAway = true;
3801        mKeyguardFadingAwayDelay = delay;
3802        mKeyguardFadingAwayDuration = fadeoutDuration;
3803        mWaitingForKeyguardExit = false;
3804        mIconController.appTransitionStarting(
3805                startTime + fadeoutDuration
3806                        - StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION,
3807                StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION);
3808        disable(mDisabledUnmodified1, mDisabledUnmodified2, fadeoutDuration > 0 /* animate */);
3809    }
3810
3811    public boolean isKeyguardFadingAway() {
3812        return mKeyguardFadingAway;
3813    }
3814
3815    /**
3816     * Notifies that the Keyguard fading away animation is done.
3817     */
3818    public void finishKeyguardFadingAway() {
3819        mKeyguardFadingAway = false;
3820        mKeyguardGoingAway = false;
3821    }
3822
3823    public void stopWaitingForKeyguardExit() {
3824        mWaitingForKeyguardExit = false;
3825    }
3826
3827    private void updatePublicMode() {
3828        setLockscreenPublicMode(
3829                mStatusBarKeyguardViewManager.isShowing() && mStatusBarKeyguardViewManager
3830                        .isSecure(mCurrentUserId));
3831    }
3832
3833    protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
3834        if (mState == StatusBarState.KEYGUARD) {
3835            mKeyguardIndicationController.setVisible(true);
3836            mNotificationPanel.resetViews();
3837            if (mKeyguardUserSwitcher != null) {
3838                mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
3839            }
3840            mStatusBarView.removePendingHideExpandedRunnables();
3841        } else {
3842            mKeyguardIndicationController.setVisible(false);
3843            if (mKeyguardUserSwitcher != null) {
3844                mKeyguardUserSwitcher.setKeyguard(false,
3845                        goingToFullShade ||
3846                        mState == StatusBarState.SHADE_LOCKED ||
3847                        fromShadeLocked);
3848            }
3849        }
3850        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3851            mScrimController.setKeyguardShowing(true);
3852            mIconPolicy.setKeyguardShowing(true);
3853        } else {
3854            mScrimController.setKeyguardShowing(false);
3855            mIconPolicy.setKeyguardShowing(false);
3856        }
3857
3858        mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
3859        updateDozingState();
3860        updatePublicMode();
3861        updateStackScrollerState(goingToFullShade, fromShadeLocked);
3862        updateNotifications();
3863        checkBarModes();
3864        updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
3865        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
3866                mStatusBarKeyguardViewManager.isSecure());
3867    }
3868
3869    private void updateDozingState() {
3870        boolean animate = !mDozing && mDozeScrimController.isPulsing();
3871        mNotificationPanel.setDozing(mDozing, animate);
3872        mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
3873        mScrimController.setDozing(mDozing);
3874
3875        // Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
3876        // for pulsing so the Keyguard fade-out animation scrim can take over.
3877        mDozeScrimController.setDozing(mDozing &&
3878                mFingerprintUnlockController.getMode()
3879                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
3880    }
3881
3882    public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
3883        if (mStackScroller == null) return;
3884        boolean onKeyguard = mState == StatusBarState.KEYGUARD;
3885        mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
3886        mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
3887        mStackScroller.setExpandingEnabled(!onKeyguard);
3888        ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
3889        mStackScroller.setActivatedChild(null);
3890        if (activatedChild != null) {
3891            activatedChild.makeInactive(false /* animate */);
3892        }
3893    }
3894
3895    public void userActivity() {
3896        if (mState == StatusBarState.KEYGUARD) {
3897            mKeyguardViewMediatorCallback.userActivity();
3898        }
3899    }
3900
3901    public boolean interceptMediaKey(KeyEvent event) {
3902        return mState == StatusBarState.KEYGUARD
3903                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
3904    }
3905
3906    public boolean onMenuPressed() {
3907        if (mDeviceInteractive && mState != StatusBarState.SHADE
3908                && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed()) {
3909            animateCollapsePanels(
3910                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
3911            return true;
3912        }
3913        return false;
3914    }
3915
3916    public void endAffordanceLaunch() {
3917        releaseGestureWakeLock();
3918        mNotificationPanel.onAffordanceLaunchEnded();
3919    }
3920
3921    public boolean onBackPressed() {
3922        if (mStatusBarKeyguardViewManager.onBackPressed()) {
3923            return true;
3924        }
3925        if (mNotificationPanel.isQsExpanded()) {
3926            if (mNotificationPanel.isQsDetailShowing()) {
3927                mNotificationPanel.closeQsDetail();
3928            } else {
3929                mNotificationPanel.animateCloseQs();
3930            }
3931            return true;
3932        }
3933        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
3934            animateCollapsePanels();
3935            return true;
3936        }
3937        return false;
3938    }
3939
3940    public boolean onSpacePressed() {
3941        if (mDeviceInteractive && mState != StatusBarState.SHADE) {
3942            animateCollapsePanels(
3943                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
3944            return true;
3945        }
3946        return false;
3947    }
3948
3949    private void showBouncer() {
3950        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3951            mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
3952            mStatusBarKeyguardViewManager.dismiss();
3953        }
3954    }
3955
3956    private void instantExpandNotificationsPanel() {
3957
3958        // Make our window larger and the panel expanded.
3959        makeExpandedVisible(true);
3960        mNotificationPanel.expand(false /* animate */);
3961    }
3962
3963    private void instantCollapseNotificationPanel() {
3964        mNotificationPanel.instantCollapse();
3965    }
3966
3967    @Override
3968    public void onActivated(ActivatableNotificationView view) {
3969        EventLogTags.writeSysuiLockscreenGesture(
3970                EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE,
3971                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
3972        mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
3973        ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
3974        if (previousView != null) {
3975            previousView.makeInactive(true /* animate */);
3976        }
3977        mStackScroller.setActivatedChild(view);
3978    }
3979
3980    /**
3981     * @param state The {@link StatusBarState} to set.
3982     */
3983    public void setBarState(int state) {
3984        // If we're visible and switched to SHADE_LOCKED (the user dragged
3985        // down on the lockscreen), clear notification LED, vibration,
3986        // ringing.
3987        // Other transitions are covered in handleVisibleToUserChanged().
3988        if (state != mState && mVisible && (state == StatusBarState.SHADE_LOCKED
3989                || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
3990            clearNotificationEffects();
3991        }
3992        mState = state;
3993        mGroupManager.setStatusBarState(state);
3994        mFalsingManager.setStatusBarState(state);
3995        mStatusBarWindowManager.setStatusBarState(state);
3996        updateDozing();
3997    }
3998
3999    @Override
4000    public void onActivationReset(ActivatableNotificationView view) {
4001        if (view == mStackScroller.getActivatedChild()) {
4002            mKeyguardIndicationController.hideTransientIndication();
4003            mStackScroller.setActivatedChild(null);
4004        }
4005    }
4006
4007    public void onTrackingStarted() {
4008        runPostCollapseRunnables();
4009    }
4010
4011    public void onClosingFinished() {
4012        runPostCollapseRunnables();
4013    }
4014
4015    public void onUnlockHintStarted() {
4016        mFalsingManager.onUnlockHintStarted();
4017        mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
4018    }
4019
4020    public void onHintFinished() {
4021        // Delay the reset a bit so the user can read the text.
4022        mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
4023    }
4024
4025    public void onCameraHintStarted() {
4026        mFalsingManager.onCameraHintStarted();
4027        mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
4028    }
4029
4030    public void onVoiceAssistHintStarted() {
4031        mFalsingManager.onLeftAffordanceHintStarted();
4032        mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
4033    }
4034
4035    public void onPhoneHintStarted() {
4036        mFalsingManager.onLeftAffordanceHintStarted();
4037        mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
4038    }
4039
4040    public void onTrackingStopped(boolean expand) {
4041        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
4042            if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
4043                showBouncer();
4044            }
4045        }
4046    }
4047
4048    @Override
4049    protected int getMaxKeyguardNotifications(boolean recompute) {
4050        if (recompute) {
4051            mMaxKeyguardNotifications = Math.max(1,
4052                    mNotificationPanel.computeMaxKeyguardNotifications(
4053                            mMaxAllowedKeyguardNotifications));
4054            return mMaxKeyguardNotifications;
4055        }
4056        return mMaxKeyguardNotifications;
4057    }
4058
4059    public int getMaxKeyguardNotifications() {
4060        return getMaxKeyguardNotifications(false /* recompute */);
4061    }
4062
4063    public NavigationBarView getNavigationBarView() {
4064        return mNavigationBarView;
4065    }
4066
4067    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
4068
4069    @Override
4070    public boolean onDraggedDown(View startingChild, int dragLengthY) {
4071        if (hasActiveNotifications()) {
4072            EventLogTags.writeSysuiLockscreenGesture(
4073                    EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_FULL_SHADE,
4074                    (int) (dragLengthY / mDisplayMetrics.density),
4075                    0 /* velocityDp - N/A */);
4076
4077            // We have notifications, go to locked shade.
4078            goToLockedShade(startingChild);
4079            return true;
4080        } else {
4081
4082            // No notifications - abort gesture.
4083            return false;
4084        }
4085    }
4086
4087    @Override
4088    public void onDragDownReset() {
4089        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
4090        mStackScroller.resetScrollPosition();
4091    }
4092
4093    @Override
4094    public void onCrossedThreshold(boolean above) {
4095        mStackScroller.setDimmed(!above /* dimmed */, true /* animate */);
4096    }
4097
4098    @Override
4099    public void onTouchSlopExceeded() {
4100        mStackScroller.removeLongPressCallback();
4101    }
4102
4103    @Override
4104    public void setEmptyDragAmount(float amount) {
4105        mNotificationPanel.setEmptyDragAmount(amount);
4106    }
4107
4108    /**
4109     * If secure with redaction: Show bouncer, go to unlocked shade.
4110     *
4111     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
4112     *
4113     * @param expandView The view to expand after going to the shade.
4114     */
4115    public void goToLockedShade(View expandView) {
4116        ExpandableNotificationRow row = null;
4117        if (expandView instanceof ExpandableNotificationRow) {
4118            row = (ExpandableNotificationRow) expandView;
4119            row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
4120        }
4121        boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
4122                || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
4123        if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
4124            mLeaveOpenOnKeyguardHide = true;
4125            showBouncer();
4126            mDraggedDownRow = row;
4127            mPendingRemoteInputView = null;
4128        } else {
4129            mNotificationPanel.animateToFullShade(0 /* delay */);
4130            setBarState(StatusBarState.SHADE_LOCKED);
4131            updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
4132        }
4133    }
4134
4135    @Override
4136    public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
4137        mLeaveOpenOnKeyguardHide = true;
4138        dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
4139    }
4140
4141    @Override
4142    protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
4143        mLeaveOpenOnKeyguardHide = true;
4144        showBouncer();
4145        mPendingRemoteInputView = clicked;
4146    }
4147
4148    @Override
4149    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
4150        mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
4151        if (mState == StatusBarState.KEYGUARD && nowExpanded) {
4152            goToLockedShade(clickedEntry.row);
4153        }
4154    }
4155
4156    /**
4157     * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
4158     */
4159    public void goToKeyguard() {
4160        if (mState == StatusBarState.SHADE_LOCKED) {
4161            mStackScroller.onGoToKeyguard();
4162            setBarState(StatusBarState.KEYGUARD);
4163            updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
4164        }
4165    }
4166
4167    public long getKeyguardFadingAwayDelay() {
4168        return mKeyguardFadingAwayDelay;
4169    }
4170
4171    public long getKeyguardFadingAwayDuration() {
4172        return mKeyguardFadingAwayDuration;
4173    }
4174
4175    @Override
4176    public void setBouncerShowing(boolean bouncerShowing) {
4177        super.setBouncerShowing(bouncerShowing);
4178        mStatusBarView.setBouncerShowing(bouncerShowing);
4179        disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
4180    }
4181
4182    public void onStartedGoingToSleep() {
4183        mStartedGoingToSleep = true;
4184    }
4185
4186    public void onFinishedGoingToSleep() {
4187        mNotificationPanel.onAffordanceLaunchEnded();
4188        releaseGestureWakeLock();
4189        mLaunchCameraOnScreenTurningOn = false;
4190        mStartedGoingToSleep = false;
4191        mDeviceInteractive = false;
4192        mWakeUpComingFromTouch = false;
4193        mWakeUpTouchLocation = null;
4194        mStackScroller.setAnimationsEnabled(false);
4195        updateVisibleToUser();
4196        if (mLaunchCameraOnFinishedGoingToSleep) {
4197            mLaunchCameraOnFinishedGoingToSleep = false;
4198
4199            // This gets executed before we will show Keyguard, so post it in order that the state
4200            // is correct.
4201            mHandler.post(new Runnable() {
4202                @Override
4203                public void run() {
4204                    onCameraLaunchGestureDetected(mLastCameraLaunchSource);
4205                }
4206            });
4207        }
4208    }
4209
4210    public void onStartedWakingUp() {
4211        mDeviceInteractive = true;
4212        mStackScroller.setAnimationsEnabled(true);
4213        mNotificationPanel.setTouchDisabled(false);
4214        updateVisibleToUser();
4215    }
4216
4217    public void onScreenTurningOn() {
4218        mScreenTurningOn = true;
4219        mFalsingManager.onScreenTurningOn();
4220        mNotificationPanel.onScreenTurningOn();
4221        if (mLaunchCameraOnScreenTurningOn) {
4222            mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
4223            mLaunchCameraOnScreenTurningOn = false;
4224        }
4225    }
4226
4227    private void vibrateForCameraGesture() {
4228        // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
4229        mVibrator.vibrate(new long[]{0, 750L}, -1 /* repeat */);
4230    }
4231
4232    public void onScreenTurnedOn() {
4233        mScreenTurningOn = false;
4234        mDozeScrimController.onScreenTurnedOn();
4235    }
4236
4237    /**
4238     * Handles long press for back button. This exits screen pinning.
4239     */
4240    private boolean handleLongPressBack() {
4241        try {
4242            IActivityManager activityManager = ActivityManagerNative.getDefault();
4243            if (activityManager.isInLockTaskMode()) {
4244                activityManager.stopLockTaskModeOnCurrent();
4245
4246                // When exiting refresh disabled flags.
4247                mNavigationBarView.setDisabledFlags(mDisabled1, true);
4248                return true;
4249            }
4250        } catch (RemoteException e) {
4251            Log.d(TAG, "Unable to reach activity manager", e);
4252        }
4253        return false;
4254    }
4255
4256    public void updateRecentsVisibility(boolean visible) {
4257        // Update the recents visibility flag
4258        if (visible) {
4259            mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
4260        } else {
4261            mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
4262        }
4263        notifyUiVisibilityChanged(mSystemUiVisibility);
4264    }
4265
4266    @Override
4267    public void showScreenPinningRequest() {
4268        if (mKeyguardMonitor.isShowing()) {
4269            // Don't allow apps to trigger this from keyguard.
4270            return;
4271        }
4272        // Show screen pinning request, since this comes from an app, show 'no thanks', button.
4273        showScreenPinningRequest(true);
4274    }
4275
4276    public void showScreenPinningRequest(boolean allowCancel) {
4277        mScreenPinningRequest.showPrompt(allowCancel);
4278    }
4279
4280    public boolean hasActiveNotifications() {
4281        return !mNotificationData.getActiveNotifications().isEmpty();
4282    }
4283
4284    public void wakeUpIfDozing(long time, MotionEvent event) {
4285        if (mDozing && mDozeScrimController.isPulsing()) {
4286            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
4287            pm.wakeUp(time, "com.android.systemui:NODOZE");
4288            mWakeUpComingFromTouch = true;
4289            mWakeUpTouchLocation = new PointF(event.getX(), event.getY());
4290            mNotificationPanel.setTouchDisabled(false);
4291            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
4292            mFalsingManager.onScreenOnFromTouch();
4293        }
4294    }
4295
4296    @Override
4297    public void appTransitionPending() {
4298
4299        // Use own timings when Keyguard is going away, see keyguardGoingAway and
4300        // setKeyguardFadingAway
4301        if (!mKeyguardFadingAway) {
4302            mIconController.appTransitionPending();
4303        }
4304    }
4305
4306    @Override
4307    public void appTransitionCancelled() {
4308        mIconController.appTransitionCancelled();
4309    }
4310
4311    @Override
4312    public void appTransitionStarting(long startTime, long duration) {
4313
4314        // Use own timings when Keyguard is going away, see keyguardGoingAway and
4315        // setKeyguardFadingAway.
4316        if (!mKeyguardGoingAway) {
4317            mIconController.appTransitionStarting(startTime, duration);
4318        }
4319        if (mIconPolicy != null) {
4320            mIconPolicy.appTransitionStarting(startTime, duration);
4321        }
4322    }
4323
4324    @Override
4325    public void onCameraLaunchGestureDetected(int source) {
4326        mLastCameraLaunchSource = source;
4327        if (mStartedGoingToSleep) {
4328            mLaunchCameraOnFinishedGoingToSleep = true;
4329            return;
4330        }
4331        if (!mNotificationPanel.canCameraGestureBeLaunched(
4332                mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
4333            return;
4334        }
4335        if (!mDeviceInteractive) {
4336            PowerManager pm = mContext.getSystemService(PowerManager.class);
4337            pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
4338            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
4339        }
4340        vibrateForCameraGesture();
4341        if (!mStatusBarKeyguardViewManager.isShowing()) {
4342            startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
4343                    true /* dismissShade */);
4344        } else {
4345            if (!mDeviceInteractive) {
4346                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
4347                // comes on.
4348                mScrimController.dontAnimateBouncerChangesUntilNextFrame();
4349                mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
4350            }
4351            if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
4352                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
4353            } else {
4354                // We need to defer the camera launch until the screen comes on, since otherwise
4355                // we will dismiss us too early since we are waiting on an activity to be drawn and
4356                // incorrectly get notified because of the screen on event (which resumes and pauses
4357                // some activities)
4358                mLaunchCameraOnScreenTurningOn = true;
4359            }
4360        }
4361    }
4362
4363    @Override
4364    public void requestTvPictureInPicture() {
4365        // no-op.
4366    }
4367
4368    public void notifyFpAuthModeChanged() {
4369        updateDozing();
4370    }
4371
4372    private void updateDozing() {
4373        // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
4374        mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
4375                || mFingerprintUnlockController.getMode()
4376                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
4377        updateDozingState();
4378    }
4379
4380    private final class ShadeUpdates {
4381        private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
4382        private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
4383
4384        public void check() {
4385            mNewVisibleNotifications.clear();
4386            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
4387            for (int i = 0; i < activeNotifications.size(); i++) {
4388                final Entry entry = activeNotifications.get(i);
4389                final boolean visible = entry.row != null
4390                        && entry.row.getVisibility() == View.VISIBLE;
4391                if (visible) {
4392                    mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
4393                }
4394            }
4395            final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
4396            mVisibleNotifications.clear();
4397            mVisibleNotifications.addAll(mNewVisibleNotifications);
4398
4399            // We have new notifications
4400            if (updates && mDozeServiceHost != null) {
4401                mDozeServiceHost.fireNewNotifications();
4402            }
4403        }
4404    }
4405
4406    private final class DozeServiceHost extends KeyguardUpdateMonitorCallback implements DozeHost  {
4407        // Amount of time to allow to update the time shown on the screen before releasing
4408        // the wakelock.  This timeout is design to compensate for the fact that we don't
4409        // currently have a way to know when time display contents have actually been
4410        // refreshed once we've finished rendering a new frame.
4411        private static final long PROCESSING_TIME = 500;
4412
4413        private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
4414        private final H mHandler = new H();
4415
4416        // Keeps the last reported state by fireNotificationLight.
4417        private boolean mNotificationLightOn;
4418
4419        @Override
4420        public String toString() {
4421            return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
4422        }
4423
4424        public void firePowerSaveChanged(boolean active) {
4425            for (Callback callback : mCallbacks) {
4426                callback.onPowerSaveChanged(active);
4427            }
4428        }
4429
4430        public void fireBuzzBeepBlinked() {
4431            for (Callback callback : mCallbacks) {
4432                callback.onBuzzBeepBlinked();
4433            }
4434        }
4435
4436        public void fireNotificationLight(boolean on) {
4437            mNotificationLightOn = on;
4438            for (Callback callback : mCallbacks) {
4439                callback.onNotificationLight(on);
4440            }
4441        }
4442
4443        public void fireNewNotifications() {
4444            for (Callback callback : mCallbacks) {
4445                callback.onNewNotifications();
4446            }
4447        }
4448
4449        @Override
4450        public void addCallback(@NonNull Callback callback) {
4451            mCallbacks.add(callback);
4452        }
4453
4454        @Override
4455        public void removeCallback(@NonNull Callback callback) {
4456            mCallbacks.remove(callback);
4457        }
4458
4459        @Override
4460        public void startDozing(@NonNull Runnable ready) {
4461            mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget();
4462        }
4463
4464        @Override
4465        public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
4466            mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, reason, 0, callback).sendToTarget();
4467        }
4468
4469        @Override
4470        public void stopDozing() {
4471            mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget();
4472        }
4473
4474        @Override
4475        public boolean isPowerSaveActive() {
4476            return mBatteryController != null && mBatteryController.isPowerSave();
4477        }
4478
4479        @Override
4480        public boolean isPulsingBlocked() {
4481            return mFingerprintUnlockController.getMode()
4482                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
4483        }
4484
4485        @Override
4486        public boolean isNotificationLightOn() {
4487            return mNotificationLightOn;
4488        }
4489
4490        private void handleStartDozing(@NonNull Runnable ready) {
4491            if (!mDozingRequested) {
4492                mDozingRequested = true;
4493                DozeLog.traceDozing(mContext, mDozing);
4494                updateDozing();
4495            }
4496            ready.run();
4497        }
4498
4499        private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) {
4500            mDozeScrimController.pulse(new PulseCallback() {
4501
4502                @Override
4503                public void onPulseStarted() {
4504                    callback.onPulseStarted();
4505                    mStackScroller.setPulsing(true);
4506                }
4507
4508                @Override
4509                public void onPulseFinished() {
4510                    callback.onPulseFinished();
4511                    mStackScroller.setPulsing(false);
4512                }
4513            }, reason);
4514        }
4515
4516        private void handleStopDozing() {
4517            if (mDozingRequested) {
4518                mDozingRequested = false;
4519                DozeLog.traceDozing(mContext, mDozing);
4520                updateDozing();
4521            }
4522        }
4523
4524        private final class H extends Handler {
4525            private static final int MSG_START_DOZING = 1;
4526            private static final int MSG_PULSE_WHILE_DOZING = 2;
4527            private static final int MSG_STOP_DOZING = 3;
4528
4529            @Override
4530            public void handleMessage(Message msg) {
4531                switch (msg.what) {
4532                    case MSG_START_DOZING:
4533                        handleStartDozing((Runnable) msg.obj);
4534                        break;
4535                    case MSG_PULSE_WHILE_DOZING:
4536                        handlePulseWhileDozing((PulseCallback) msg.obj, msg.arg1);
4537                        break;
4538                    case MSG_STOP_DOZING:
4539                        handleStopDozing();
4540                        break;
4541                }
4542            }
4543        }
4544    }
4545}
4546