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