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