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