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