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