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