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