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