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