OSDN Git Service

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