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