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