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