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