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