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