OSDN Git Service

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