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