2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar.phone;
20 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
21 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
22 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
23 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
24 import static android.app.StatusBarManager.windowStateToString;
25 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
26 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
27 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
28 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
29 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
30 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
32 import android.animation.Animator;
33 import android.animation.AnimatorListenerAdapter;
34 import android.animation.TimeInterpolator;
35 import android.annotation.NonNull;
36 import android.app.ActivityManager;
37 import android.app.ActivityManagerNative;
38 import android.app.IActivityManager;
39 import android.app.Notification;
40 import android.app.PendingIntent;
41 import android.app.StatusBarManager;
42 import android.content.BroadcastReceiver;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.content.res.Configuration;
47 import android.content.res.Resources;
48 import android.database.ContentObserver;
49 import android.graphics.Bitmap;
50 import android.graphics.Canvas;
51 import android.graphics.ColorFilter;
52 import android.graphics.PixelFormat;
53 import android.graphics.Point;
54 import android.graphics.PorterDuff;
55 import android.graphics.PorterDuffXfermode;
56 import android.graphics.Rect;
57 import android.graphics.Xfermode;
58 import android.graphics.drawable.ColorDrawable;
59 import android.graphics.drawable.Drawable;
60 import android.inputmethodservice.InputMethodService;
61 import android.media.AudioAttributes;
62 import android.media.MediaMetadata;
63 import android.media.session.MediaController;
64 import android.media.session.MediaSession;
65 import android.media.session.MediaSessionManager;
66 import android.media.session.PlaybackState;
67 import android.os.AsyncTask;
68 import android.os.Bundle;
69 import android.os.Handler;
70 import android.os.IBinder;
71 import android.os.Message;
72 import android.os.PowerManager;
73 import android.os.RemoteException;
74 import android.os.SystemClock;
75 import android.os.UserHandle;
76 import android.provider.Settings;
77 import android.service.notification.NotificationListenerService;
78 import android.service.notification.NotificationListenerService.RankingMap;
79 import android.service.notification.StatusBarNotification;
80 import android.util.ArraySet;
81 import android.util.DisplayMetrics;
82 import android.util.EventLog;
83 import android.util.Log;
84 import android.view.Display;
85 import android.view.Gravity;
86 import android.view.HardwareCanvas;
87 import android.view.KeyEvent;
88 import android.view.LayoutInflater;
89 import android.view.MotionEvent;
90 import android.view.VelocityTracker;
91 import android.view.View;
92 import android.view.ViewGroup;
93 import android.view.ViewGroup.LayoutParams;
94 import android.view.ViewPropertyAnimator;
95 import android.view.ViewStub;
96 import android.view.ViewTreeObserver;
97 import android.view.WindowManager;
98 import android.view.accessibility.AccessibilityEvent;
99 import android.view.accessibility.AccessibilityManager;
100 import android.view.animation.AccelerateDecelerateInterpolator;
101 import android.view.animation.AccelerateInterpolator;
102 import android.view.animation.Animation;
103 import android.view.animation.AnimationUtils;
104 import android.view.animation.DecelerateInterpolator;
105 import android.view.animation.Interpolator;
106 import android.view.animation.LinearInterpolator;
107 import android.view.animation.PathInterpolator;
108 import android.widget.FrameLayout;
109 import android.widget.ImageView;
110 import android.widget.LinearLayout;
111 import android.widget.TextView;
113 import com.android.internal.statusbar.StatusBarIcon;
114 import com.android.keyguard.KeyguardHostView.OnDismissAction;
115 import com.android.keyguard.ViewMediatorCallback;
116 import com.android.systemui.BatteryMeterView;
117 import com.android.systemui.DemoMode;
118 import com.android.systemui.EventLogTags;
119 import com.android.systemui.FontSizeUtils;
120 import com.android.systemui.R;
121 import com.android.systemui.doze.DozeHost;
122 import com.android.systemui.doze.DozeLog;
123 import com.android.systemui.keyguard.KeyguardViewMediator;
124 import com.android.systemui.qs.QSPanel;
125 import com.android.systemui.statusbar.ActivatableNotificationView;
126 import com.android.systemui.statusbar.BackDropView;
127 import com.android.systemui.statusbar.BaseStatusBar;
128 import com.android.systemui.statusbar.CommandQueue;
129 import com.android.systemui.statusbar.DismissView;
130 import com.android.systemui.statusbar.DragDownHelper;
131 import com.android.systemui.statusbar.EmptyShadeView;
132 import com.android.systemui.statusbar.ExpandableNotificationRow;
133 import com.android.systemui.statusbar.GestureRecorder;
134 import com.android.systemui.statusbar.KeyguardIndicationController;
135 import com.android.systemui.statusbar.NotificationData;
136 import com.android.systemui.statusbar.NotificationData.Entry;
137 import com.android.systemui.statusbar.NotificationOverflowContainer;
138 import com.android.systemui.statusbar.ScrimView;
139 import com.android.systemui.statusbar.SignalClusterView;
140 import com.android.systemui.statusbar.SpeedBumpView;
141 import com.android.systemui.statusbar.StatusBarIconView;
142 import com.android.systemui.statusbar.StatusBarState;
143 import com.android.systemui.statusbar.policy.AccessibilityController;
144 import com.android.systemui.statusbar.policy.BatteryController;
145 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
146 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
147 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
148 import com.android.systemui.statusbar.policy.CastControllerImpl;
149 import com.android.systemui.statusbar.policy.FlashlightController;
150 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
151 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
152 import com.android.systemui.statusbar.policy.KeyButtonView;
153 import com.android.systemui.statusbar.policy.KeyguardMonitor;
154 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
155 import com.android.systemui.statusbar.policy.LocationControllerImpl;
156 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
157 import com.android.systemui.statusbar.policy.NextAlarmController;
158 import com.android.systemui.statusbar.policy.PreviewInflater;
159 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
160 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
161 import com.android.systemui.statusbar.policy.UserInfoController;
162 import com.android.systemui.statusbar.policy.UserSwitcherController;
163 import com.android.systemui.statusbar.policy.ZenModeController;
164 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
165 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
166 import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
167 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
168 import com.android.systemui.volume.VolumeComponent;
170 import java.io.FileDescriptor;
171 import java.io.PrintWriter;
172 import java.util.ArrayList;
173 import java.util.Collection;
174 import java.util.Collections;
175 import java.util.List;
177 public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
178 DragDownHelper.DragDownCallback, ActivityStarter {
179 static final String TAG = "PhoneStatusBar";
180 public static final boolean DEBUG = BaseStatusBar.DEBUG;
181 public static final boolean SPEW = false;
182 public static final boolean DUMPTRUCK = true; // extra dumpsys info
183 public static final boolean DEBUG_GESTURES = false;
184 public static final boolean DEBUG_MEDIA = false;
185 public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
187 public static final boolean DEBUG_WINDOW_STATE = false;
189 // additional instrumentation for testing purposes; intended to be left on during development
190 public static final boolean CHATTY = DEBUG;
192 public static final String ACTION_STATUSBAR_START
193 = "com.android.internal.policy.statusbar.START";
195 public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
197 private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
198 private static final int MSG_CLOSE_PANELS = 1001;
199 private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
200 // 1020-1040 reserved for BaseStatusBar
202 private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
204 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
205 private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
207 private static final int STATUS_OR_NAV_TRANSIENT =
208 View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
209 private static final long AUTOHIDE_TIMEOUT_MS = 3000;
211 /** The minimum delay in ms between reports of notification visibility. */
212 private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
215 * The delay to reset the hint text when the hint animation is finished running.
217 private static final int HINT_RESET_DELAY_MS = 1200;
219 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
220 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
221 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
224 public static final int FADE_KEYGUARD_START_DELAY = 100;
225 public static final int FADE_KEYGUARD_DURATION = 300;
227 /** Allow some time inbetween the long press for back and recents. */
228 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
230 PhoneStatusBarPolicy mIconPolicy;
232 // These are no longer handled by the policy, because we need custom strategies for them
233 BluetoothControllerImpl mBluetoothController;
234 SecurityControllerImpl mSecurityController;
235 BatteryController mBatteryController;
236 LocationControllerImpl mLocationController;
237 NetworkControllerImpl mNetworkController;
238 HotspotControllerImpl mHotspotController;
239 RotationLockControllerImpl mRotationLockController;
240 UserInfoController mUserInfoController;
241 ZenModeController mZenModeController;
242 CastControllerImpl mCastController;
243 VolumeComponent mVolumeComponent;
244 KeyguardUserSwitcher mKeyguardUserSwitcher;
245 FlashlightController mFlashlightController;
246 UserSwitcherController mUserSwitcherController;
247 NextAlarmController mNextAlarmController;
248 KeyguardMonitor mKeyguardMonitor;
249 BrightnessMirrorController mBrightnessMirrorController;
250 AccessibilityController mAccessibilityController;
252 int mNaturalBarHeight = -1;
254 int mIconHPadding = -1;
256 Point mCurrentDisplaySize = new Point();
258 StatusBarWindowView mStatusBarWindow;
259 PhoneStatusBarView mStatusBarView;
260 private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
261 private StatusBarWindowManager mStatusBarWindowManager;
262 private UnlockMethodCache mUnlockMethodCache;
263 private DozeServiceHost mDozeServiceHost;
264 private boolean mScreenOnComingFromTouch;
267 Object mQueueLock = new Object();
269 // viewgroup containing the normal contents of the statusbar
270 LinearLayout mStatusBarContents;
273 LinearLayout mSystemIconArea;
274 LinearLayout mSystemIcons;
277 LinearLayout mStatusIcons;
278 LinearLayout mStatusIconsKeyguard;
280 // the icons themselves
281 IconMerger mNotificationIcons;
282 View mNotificationIconArea;
287 // expanded notifications
288 NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
289 View mExpandedContents;
290 int mNotificationPanelGravity;
291 int mNotificationPanelMarginBottomPx;
292 float mNotificationPanelMinHeightFrac;
293 TextView mNotificationPanelDebugText;
296 View mFlipSettingsView;
297 private QSPanel mQSPanel;
300 StatusBarHeaderView mHeader;
301 KeyguardStatusBarView mKeyguardStatusBar;
302 View mKeyguardStatusView;
303 KeyguardBottomAreaView mKeyguardBottomArea;
304 boolean mLeaveOpenOnKeyguardHide;
305 KeyguardIndicationController mKeyguardIndicationController;
307 private boolean mKeyguardFadingAway;
308 private long mKeyguardFadingAwayDelay;
309 private long mKeyguardFadingAwayDuration;
311 int mKeyguardMaxNotificationCount;
313 // carrier/wifi label
314 private TextView mCarrierLabel;
315 private boolean mCarrierLabelVisible = false;
316 private int mCarrierLabelHeight;
317 private int mStatusBarHeaderHeight;
319 private boolean mShowCarrierInPanel = false;
322 int[] mPositionTmp = new int[2];
323 boolean mExpandedVisible;
325 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
328 int mTrackingPosition; // the position of the top of the tracking view.
331 private boolean mTickerEnabled;
332 private Ticker mTicker;
333 private View mTickerView;
334 private boolean mTicking;
336 // Tracking finger for opening/closing.
337 int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
339 VelocityTracker mVelocityTracker;
341 int[] mAbsPos = new int[2];
342 ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
344 // for disabling the status bar
347 // tracking calls to View.setSystemUiVisibility()
348 int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
350 DisplayMetrics mDisplayMetrics = new DisplayMetrics();
352 // XXX: gesture research
353 private final GestureRecorder mGestureRec = DEBUG_GESTURES
354 ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
357 private int mNavigationIconHints = 0;
359 // ensure quick settings is disabled until the current user makes it through the setup wizard
360 private boolean mUserSetup = false;
361 private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
363 public void onChange(boolean selfChange) {
364 final boolean userSetup = 0 != Settings.Secure.getIntForUser(
365 mContext.getContentResolver(),
366 Settings.Secure.USER_SETUP_COMPLETE,
369 if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
370 "selfChange=%s userSetup=%s mUserSetup=%s",
371 selfChange, userSetup, mUserSetup));
373 if (userSetup != mUserSetup) {
374 mUserSetup = userSetup;
375 if (!mUserSetup && mStatusBarView != null)
376 animateCollapseQuickSettings();
381 final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
383 public void onChange(boolean selfChange) {
384 boolean wasUsing = mUseHeadsUp;
385 mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
386 && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
387 mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
388 Settings.Global.HEADS_UP_OFF);
389 mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
390 mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
391 Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
392 if (wasUsing != mUseHeadsUp) {
394 Log.d(TAG, "dismissing any existing heads up notification on disable event");
395 setHeadsUpVisibility(false);
396 mHeadsUpNotificationView.release();
405 private int mInteractingWindows;
406 private boolean mAutohideSuspended;
407 private int mStatusBarMode;
408 private int mNavigationBarMode;
409 private Boolean mScreenOn;
411 // The second field is a bit different from the first one because it only listens to screen on/
412 // screen of events from Keyguard. We need this so we don't have a race condition with the
413 // broadcast. In the future, we should remove the first field altogether and rename the second
415 private boolean mScreenOnFromKeyguard;
417 private ViewMediatorCallback mKeyguardViewMediatorCallback;
418 private ScrimController mScrimController;
420 private final Runnable mAutohide = new Runnable() {
423 int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
424 if (mSystemUiVisibility != requested) {
425 notifyUiVisibilityChanged(requested);
429 private boolean mVisible;
430 private boolean mWaitingForKeyguardExit;
431 private boolean mDozing;
432 private boolean mScrimSrcModeEnabled;
434 private Interpolator mLinearOutSlowIn;
435 private Interpolator mLinearInterpolator = new LinearInterpolator();
436 private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
437 public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
438 public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
440 private BackDropView mBackdrop;
441 private ImageView mBackdropFront, mBackdropBack;
442 private PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
443 private PorterDuffXfermode mSrcOverXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
445 private MediaSessionManager mMediaSessionManager;
446 private MediaController mMediaController;
447 private String mMediaNotificationKey;
448 private MediaMetadata mMediaMetadata;
449 private MediaController.Callback mMediaListener
450 = new MediaController.Callback() {
452 public void onPlaybackStateChanged(PlaybackState state) {
453 super.onPlaybackStateChanged(state);
454 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
458 public void onMetadataChanged(MediaMetadata metadata) {
459 super.onMetadataChanged(metadata);
460 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
461 mMediaMetadata = metadata;
462 updateMediaMetaData(true);
466 private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
467 new OnChildLocationsChangedListener() {
469 public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
474 private int mDisabledUnmodified;
476 /** Keys of notifications currently visible to the user. */
477 private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
478 private long mLastVisibilityReportUptimeMs;
480 private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
482 private int mDrawCount;
483 private Runnable mLaunchTransitionEndRunnable;
484 private boolean mLaunchTransitionFadingAway;
485 private ExpandableNotificationRow mDraggedDownRow;
487 private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
488 | ViewState.LOCATION_TOP_STACK_PEEKING
489 | ViewState.LOCATION_MAIN_AREA
490 | ViewState.LOCATION_BOTTOM_STACK_PEEKING;
492 private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
493 new OnChildLocationsChangedListener() {
495 public void onChildLocationsChanged(
496 NotificationStackScrollLayout stackScrollLayout) {
497 if (mHandler.hasCallbacks(mVisibilityReporter)) {
498 // Visibilities will be reported when the existing
499 // callback is executed.
502 // Calculate when we're allowed to run the visibility
503 // reporter. Note that this timestamp might already have
504 // passed. That's OK, the callback will just be executed
506 long nextReportUptimeMs =
507 mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
508 mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
512 // Tracks notifications currently visible in mNotificationStackScroller and
513 // emits visibility events via NoMan on changes.
514 private final Runnable mVisibilityReporter = new Runnable() {
515 private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>();
516 private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>();
520 mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
522 // 1. Loop over mNotificationData entries:
523 // A. Keep list of visible notifications.
524 // B. Keep list of previously hidden, now visible notifications.
525 // 2. Compute no-longer visible notifications by removing currently
526 // visible notifications from the set of previously visible
528 // 3. Report newly visible and no-longer visible notifications.
529 // 4. Keep currently visible notifications for next report.
530 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
531 int N = activeNotifications.size();
532 for (int i = 0; i < N; i++) {
533 Entry entry = activeNotifications.get(i);
534 String key = entry.notification.getKey();
535 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key);
536 boolean currentlyVisible =
537 (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0;
538 if (currentlyVisible) {
539 // Build new set of visible notifications.
540 mTmpCurrentlyVisibleNotifications.add(key);
542 if (!previouslyVisible && currentlyVisible) {
543 mTmpNewlyVisibleNotifications.add(key);
546 ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications;
547 noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
549 logNotificationVisibilityChanges(
550 mTmpNewlyVisibleNotifications, noLongerVisibleNotifications);
552 mCurrentlyVisibleNotifications.clear();
553 mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
555 mTmpNewlyVisibleNotifications.clear();
556 mTmpCurrentlyVisibleNotifications.clear();
560 private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
562 public void onClick(View v) {
563 goToLockedShade(null);
568 public void start() {
569 mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
570 .getDefaultDisplay();
572 mScrimSrcModeEnabled = mContext.getResources().getBoolean(
573 R.bool.config_status_bar_scrim_behind_use_src);
574 super.start(); // calls createAndAddWindows()
577 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
578 // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
583 // Lastly, call to the icon policy to install/update all the icons.
584 mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController);
585 mSettingsObserver.onChange(false); // set up
587 mHeadsUpObserver.onChange(true); // set up
588 if (ENABLE_HEADS_UP) {
589 mContext.getContentResolver().registerContentObserver(
590 Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
592 mContext.getContentResolver().registerContentObserver(
593 Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
596 mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
599 mDozeServiceHost = new DozeServiceHost();
600 putComponent(DozeHost.class, mDozeServiceHost);
601 putComponent(PhoneStatusBar.class, this);
603 setControllerUsers();
605 notifyUserAboutHiddenNotifications();
608 // ================================================================================
609 // Constructing the view
610 // ================================================================================
611 protected PhoneStatusBarView makeStatusBarView() {
612 final Context context = mContext;
614 Resources res = context.getResources();
616 updateDisplaySize(); // populates mDisplayMetrics
619 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
621 mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
622 R.layout.super_status_bar, null);
623 mStatusBarWindow.mService = this;
624 mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
626 public boolean onTouch(View v, MotionEvent event) {
627 checkUserAutohide(v, event);
628 if (event.getAction() == MotionEvent.ACTION_DOWN) {
629 if (mExpandedVisible) {
630 animateCollapsePanels();
633 return mStatusBarWindow.onTouchEvent(event);
636 mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
637 mStatusBarView.setBar(this);
639 PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
640 mStatusBarView.setPanelHolder(holder);
642 mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
643 R.id.notification_panel);
644 mNotificationPanel.setStatusBar(this);
646 if (!ActivityManager.isHighEndGfx()) {
647 mStatusBarWindow.setBackground(null);
648 mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
649 R.color.notification_panel_solid_background)));
651 if (ENABLE_HEADS_UP) {
652 mHeadsUpNotificationView =
653 (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
654 mHeadsUpNotificationView.setVisibility(View.GONE);
655 mHeadsUpNotificationView.setBar(this);
657 if (MULTIUSER_DEBUG) {
658 mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
659 R.id.header_debug_info);
660 mNotificationPanelDebugText.setVisibility(View.VISIBLE);
663 updateShowSearchHoldoff();
666 boolean showNav = mWindowManagerService.hasNavigationBar();
667 if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
670 (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
672 mNavigationBarView.setDisabledFlags(mDisabled);
673 mNavigationBarView.setBar(this);
674 mNavigationBarView.setOnVerticalChangedListener(
675 new NavigationBarView.OnVerticalChangedListener() {
677 public void onVerticalChanged(boolean isVertical) {
678 if (mSearchPanelView != null) {
679 mSearchPanelView.setHorizontal(isVertical);
681 mNotificationPanel.setQsScrimEnabled(!isVertical);
684 mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
686 public boolean onTouch(View v, MotionEvent event) {
687 checkUserAutohide(v, event);
691 } catch (RemoteException ex) {
692 // no window manager? good luck with that
695 // figure out which pixel-format to use for the status bar.
696 mPixelFormat = PixelFormat.OPAQUE;
698 mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
699 mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
700 mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
701 mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
702 mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
703 mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
704 mNotificationIcons.setOverflowIndicator(mMoreIcon);
705 mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
707 mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
708 R.id.notification_stack_scroller);
709 mStackScroller.setLongPressListener(getNotificationLongClicker());
710 mStackScroller.setPhoneStatusBar(this);
712 mKeyguardIconOverflowContainer =
713 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
714 R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
715 mKeyguardIconOverflowContainer.setOnActivatedListener(this);
716 mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
717 mStackScroller.addView(mKeyguardIconOverflowContainer);
719 SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
720 R.layout.status_bar_notification_speed_bump, mStackScroller, false);
721 mStackScroller.setSpeedBumpView(speedBump);
722 mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
723 R.layout.status_bar_no_notifications, mStackScroller, false);
724 mStackScroller.setEmptyShadeView(mEmptyShadeView);
725 mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
726 R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
727 mDismissView.setOnButtonClickListener(new View.OnClickListener() {
729 public void onClick(View v) {
730 clearAllNotifications();
733 mStackScroller.setDismissView(mDismissView);
734 mExpandedContents = mStackScroller;
736 mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
737 mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
738 mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
740 ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
741 ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
742 mScrimController = new ScrimController(scrimBehind, scrimInFront, mScrimSrcModeEnabled);
743 mScrimController.setBackDropView(mBackdrop);
744 mStatusBarView.setScrimController(mScrimController);
746 mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
747 mHeader.setActivityStarter(this);
748 mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
749 mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
750 mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
751 mKeyguardBottomArea =
752 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
753 mKeyguardBottomArea.setActivityStarter(this);
754 mKeyguardIndicationController = new KeyguardIndicationController(mContext,
755 (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
756 R.id.keyguard_indication_text));
757 mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
759 mTickerEnabled = res.getBoolean(R.bool.enable_ticker);
760 if (mTickerEnabled) {
761 final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub);
762 if (tickerStub != null) {
763 mTickerView = tickerStub.inflate();
764 mTicker = new MyTicker(context, mStatusBarView);
766 TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText);
767 tickerView.mTicker = mTicker;
771 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
773 // set the inital view visibility
774 setAreThereNotifications();
777 mLocationController = new LocationControllerImpl(mContext); // will post a notification
778 mBatteryController = new BatteryController(mContext);
779 mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
781 public void onPowerSaveChanged() {
782 mHandler.post(mCheckBarModes);
783 if (mDozeServiceHost != null) {
784 mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
788 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
792 mNetworkController = new NetworkControllerImpl(mContext);
793 mHotspotController = new HotspotControllerImpl(mContext);
794 mBluetoothController = new BluetoothControllerImpl(mContext);
795 mSecurityController = new SecurityControllerImpl(mContext);
796 if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
797 mRotationLockController = new RotationLockControllerImpl(mContext);
799 mUserInfoController = new UserInfoController(mContext);
800 mVolumeComponent = getComponent(VolumeComponent.class);
801 mZenModeController = mVolumeComponent.getZenController();
802 mCastController = new CastControllerImpl(mContext);
803 final SignalClusterView signalCluster =
804 (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
805 final SignalClusterView signalClusterKeyguard =
806 (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
807 final SignalClusterView signalClusterQs =
808 (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
809 mNetworkController.addSignalCluster(signalCluster);
810 mNetworkController.addSignalCluster(signalClusterKeyguard);
811 mNetworkController.addSignalCluster(signalClusterQs);
812 signalCluster.setSecurityController(mSecurityController);
813 signalCluster.setNetworkController(mNetworkController);
814 signalClusterKeyguard.setSecurityController(mSecurityController);
815 signalClusterKeyguard.setNetworkController(mNetworkController);
816 signalClusterQs.setSecurityController(mSecurityController);
817 signalClusterQs.setNetworkController(mNetworkController);
818 final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
820 mNetworkController.addEmergencyLabelView(mHeader);
823 mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
824 mShowCarrierInPanel = (mCarrierLabel != null);
825 if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
826 if (mShowCarrierInPanel) {
827 mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
829 // for mobile devices, we always show mobile connection info here (SPN/PLMN)
830 // for other devices, we show whatever network is connected
831 if (mNetworkController.hasMobileDataFeature()) {
832 mNetworkController.addMobileLabelView(mCarrierLabel);
834 mNetworkController.addCombinedLabelView(mCarrierLabel);
837 // set up the dynamic hide/show of the label
838 // TODO: uncomment, handle this for the Stack scroller aswell
839 // ((NotificationRowLayout) mStackScroller)
840 // .setOnSizeChangedListener(new OnSizeChangedListener() {
842 // public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
843 // updateCarrierLabelVisibility(false);
846 mFlashlightController = new FlashlightController(mContext);
847 mKeyguardBottomArea.setFlashlightController(mFlashlightController);
848 mKeyguardBottomArea.setPhoneStatusBar(this);
849 mAccessibilityController = new AccessibilityController(mContext);
850 mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
851 mNextAlarmController = new NextAlarmController(mContext);
852 mKeyguardMonitor = new KeyguardMonitor();
853 mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
855 mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
856 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
857 mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
860 // Set up the quick settings tile panel
861 mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
862 if (mQSPanel != null) {
863 final QSTileHost qsh = new QSTileHost(mContext, this,
864 mBluetoothController, mLocationController, mRotationLockController,
865 mNetworkController, mZenModeController, mHotspotController,
866 mCastController, mFlashlightController,
867 mUserSwitcherController, mKeyguardMonitor,
868 mSecurityController);
869 mQSPanel.setHost(qsh);
870 mQSPanel.setTiles(qsh.getTiles());
871 mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
872 mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
873 mHeader.setQSPanel(mQSPanel);
874 qsh.setCallback(new QSTileHost.Callback() {
876 public void onTilesChanged() {
877 mQSPanel.setTiles(qsh.getTiles());
882 // User info. Trigger first load.
883 mHeader.setUserInfoController(mUserInfoController);
884 mKeyguardStatusBar.setUserInfoController(mUserInfoController);
885 mUserInfoController.reloadUserInfo();
887 mHeader.setBatteryController(mBatteryController);
888 ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
890 mKeyguardStatusBar.setBatteryController(mBatteryController);
891 mHeader.setNextAlarmController(mNextAlarmController);
893 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
894 mBroadcastReceiver.onReceive(mContext,
895 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
897 // receive broadcasts
898 IntentFilter filter = new IntentFilter();
899 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
900 filter.addAction(Intent.ACTION_SCREEN_OFF);
901 filter.addAction(Intent.ACTION_SCREEN_ON);
902 if (DEBUG_MEDIA_FAKE_ARTWORK) {
903 filter.addAction("fake_artwork");
905 filter.addAction(ACTION_DEMO);
906 context.registerReceiver(mBroadcastReceiver, filter);
908 // listen for USER_SETUP_COMPLETE setting (per-user)
909 resetUserSetupObserver();
911 startGlyphRasterizeHack();
912 return mStatusBarView;
915 private void clearAllNotifications() {
917 // animate-swipe all dismissable notifications, then animate the shade closed
918 int numChildren = mStackScroller.getChildCount();
920 final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
921 for (int i = 0; i < numChildren; i++) {
922 final View child = mStackScroller.getChildAt(i);
923 if (mStackScroller.canChildBeDismissed(child)) {
924 if (child.getVisibility() == View.VISIBLE) {
925 viewsToHide.add(child);
929 if (viewsToHide.isEmpty()) {
930 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
934 addPostCollapseAction(new Runnable() {
938 mBarService.onClearAllNotifications(mCurrentUserId);
939 } catch (Exception ex) { }
943 performDismissAllAnimations(viewsToHide);
947 private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
948 Runnable animationFinishAction = new Runnable() {
951 mStackScroller.post(new Runnable() {
954 mStackScroller.setDismissAllInProgress(false);
957 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
961 // let's disable our normal animations
962 mStackScroller.setDismissAllInProgress(true);
964 // Decrease the delay for every row we animate to give the sense of
965 // accelerating the swipes
966 int rowDelayDecrement = 10;
967 int currentDelay = 140;
969 int numItems = hideAnimatedList.size();
970 for (int i = 0; i < numItems; i++) {
971 View view = hideAnimatedList.get(i);
972 Runnable endRunnable = null;
973 if (i == numItems - 1) {
974 endRunnable = animationFinishAction;
976 mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260);
977 currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
978 totalDelay += currentDelay;
983 * Hack to improve glyph rasterization for scaled text views.
985 private void startGlyphRasterizeHack() {
986 mStatusBarView.getViewTreeObserver().addOnPreDrawListener(
987 new ViewTreeObserver.OnPreDrawListener() {
989 public boolean onPreDraw() {
990 if (mDrawCount == 1) {
991 mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this);
992 HardwareCanvas.setProperty("extraRasterBucket",
993 Float.toString(StackScrollAlgorithm.DIMMED_SCALE));
994 HardwareCanvas.setProperty("extraRasterBucket", Float.toString(
995 mContext.getResources().getDimensionPixelSize(
996 R.dimen.qs_time_collapsed_size)
997 / mContext.getResources().getDimensionPixelSize(
998 R.dimen.qs_time_expanded_size)));
1007 protected void setZenMode(int mode) {
1008 super.setZenMode(mode);
1009 if (mIconPolicy != null) {
1010 mIconPolicy.setZenMode(mode);
1014 private void startKeyguard() {
1015 KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
1016 mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
1017 mStatusBarWindow, mStatusBarWindowManager, mScrimController);
1018 mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
1022 protected View getStatusBarView() {
1023 return mStatusBarView;
1026 public StatusBarWindowView getStatusBarWindow() {
1027 return mStatusBarWindow;
1031 protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
1032 boolean opaque = false;
1033 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1034 LayoutParams.MATCH_PARENT,
1035 LayoutParams.MATCH_PARENT,
1036 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
1037 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1038 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1039 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1040 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
1041 if (ActivityManager.isHighEndGfx()) {
1042 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1044 lp.gravity = Gravity.BOTTOM | Gravity.START;
1045 lp.setTitle("SearchPanel");
1046 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
1047 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
1052 protected void updateSearchPanel() {
1053 super.updateSearchPanel();
1054 if (mNavigationBarView != null) {
1055 mNavigationBarView.setDelegateView(mSearchPanelView);
1060 public void showSearchPanel() {
1061 super.showSearchPanel();
1062 mHandler.removeCallbacks(mShowSearchPanel);
1064 // we want to freeze the sysui state wherever it is
1065 mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
1067 if (mNavigationBarView != null) {
1068 WindowManager.LayoutParams lp =
1069 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1070 lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1071 mWindowManager.updateViewLayout(mNavigationBarView, lp);
1076 public void hideSearchPanel() {
1077 super.hideSearchPanel();
1078 if (mNavigationBarView != null) {
1079 WindowManager.LayoutParams lp =
1080 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1081 lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1082 mWindowManager.updateViewLayout(mNavigationBarView, lp);
1086 public int getStatusBarHeight() {
1087 if (mNaturalBarHeight < 0) {
1088 final Resources res = mContext.getResources();
1090 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
1092 return mNaturalBarHeight;
1095 private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
1096 public void onClick(View v) {
1102 private long mLastLockToAppLongPress;
1103 private View.OnLongClickListener mLongPressBackRecentsListener =
1104 new View.OnLongClickListener() {
1106 public boolean onLongClick(View v) {
1107 handleLongPressBackRecents(v);
1112 private int mShowSearchHoldoff = 0;
1113 private Runnable mShowSearchPanel = new Runnable() {
1120 View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
1121 public boolean onTouch(View v, MotionEvent event) {
1122 switch(event.getAction()) {
1123 case MotionEvent.ACTION_DOWN:
1124 if (!shouldDisableNavbarGestures()) {
1125 mHandler.removeCallbacks(mShowSearchPanel);
1126 mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
1130 case MotionEvent.ACTION_UP:
1131 case MotionEvent.ACTION_CANCEL:
1132 mHandler.removeCallbacks(mShowSearchPanel);
1140 private void awakenDreams() {
1141 if (mDreamManager != null) {
1143 mDreamManager.awaken();
1144 } catch (RemoteException e) {
1145 // fine, stay asleep then
1150 private void prepareNavigationBarView() {
1151 mNavigationBarView.reorient();
1153 mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
1154 mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
1155 mNavigationBarView.getRecentsButton().setLongClickable(true);
1156 mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
1157 mNavigationBarView.getBackButton().setLongClickable(true);
1158 mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
1159 mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
1160 updateSearchPanel();
1163 // For small-screen devices (read: phones) that lack hardware navigation buttons
1164 private void addNavigationBar() {
1165 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
1166 if (mNavigationBarView == null) return;
1168 prepareNavigationBarView();
1170 mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
1173 private void repositionNavigationBar() {
1174 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
1176 prepareNavigationBarView();
1178 mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
1181 private void notifyNavigationBarScreenOn(boolean screenOn) {
1182 if (mNavigationBarView == null) return;
1183 mNavigationBarView.notifyScreenOn(screenOn);
1186 private WindowManager.LayoutParams getNavigationBarLayoutParams() {
1187 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1188 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
1189 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
1191 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1192 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1193 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1194 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1195 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1196 PixelFormat.TRANSLUCENT);
1197 // this will allow the navbar to run in an overlay on devices that support this
1198 if (ActivityManager.isHighEndGfx()) {
1199 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1202 lp.setTitle("NavigationBar");
1203 lp.windowAnimations = 0;
1207 private void addHeadsUpView() {
1208 int headsUpHeight = mContext.getResources()
1209 .getDimensionPixelSize(R.dimen.heads_up_window_height);
1210 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1211 LayoutParams.MATCH_PARENT, headsUpHeight,
1212 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
1213 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1214 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1215 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1216 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1217 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1218 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1219 PixelFormat.TRANSLUCENT);
1220 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1221 lp.gravity = Gravity.TOP;
1222 lp.setTitle("Heads Up");
1223 lp.packageName = mContext.getPackageName();
1224 lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
1226 mWindowManager.addView(mHeadsUpNotificationView, lp);
1229 private void removeHeadsUpView() {
1230 mWindowManager.removeView(mHeadsUpNotificationView);
1233 public void refreshAllStatusBarIcons() {
1234 refreshAllIconsForLayout(mStatusIcons);
1235 refreshAllIconsForLayout(mStatusIconsKeyguard);
1236 refreshAllIconsForLayout(mNotificationIcons);
1239 private void refreshAllIconsForLayout(LinearLayout ll) {
1240 final int count = ll.getChildCount();
1241 for (int n = 0; n < count; n++) {
1242 View child = ll.getChildAt(n);
1243 if (child instanceof StatusBarIconView) {
1244 ((StatusBarIconView) child).updateDrawable();
1249 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
1250 if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1252 StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
1254 mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
1255 LayoutParams.WRAP_CONTENT, mIconSize));
1256 view = new StatusBarIconView(mContext, slot, null);
1258 mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
1259 LayoutParams.WRAP_CONTENT, mIconSize));
1262 public void updateIcon(String slot, int index, int viewIndex,
1263 StatusBarIcon old, StatusBarIcon icon) {
1264 if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1265 + " old=" + old + " icon=" + icon);
1266 StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
1268 view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
1272 public void removeIcon(String slot, int index, int viewIndex) {
1273 if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
1274 mStatusIcons.removeViewAt(viewIndex);
1275 mStatusIconsKeyguard.removeViewAt(viewIndex);
1278 public UserHandle getCurrentUserHandle() {
1279 return new UserHandle(mCurrentUserId);
1283 public void addNotification(StatusBarNotification notification, RankingMap ranking) {
1284 if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
1285 if (mUseHeadsUp && shouldInterrupt(notification)) {
1286 if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
1287 Entry interruptionCandidate = new Entry(notification, null);
1288 ViewGroup holder = mHeadsUpNotificationView.getHolder();
1289 if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
1290 // 1. Populate mHeadsUpNotificationView
1291 mHeadsUpNotificationView.showNotification(interruptionCandidate);
1293 // do not show the notification in the shade, yet.
1298 Entry shadeEntry = createNotificationViews(notification);
1299 if (shadeEntry == null) {
1303 if (notification.getNotification().fullScreenIntent != null) {
1304 // Stop screensaver if the notification has a full-screen intent.
1305 // (like an incoming phone call)
1308 // not immersive & a full-screen alert should be shown
1309 if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1311 notification.getNotification().fullScreenIntent.send();
1312 } catch (PendingIntent.CanceledException e) {
1315 // usual case: status bar visible & not immersive
1317 // show the ticker if there isn't already a heads up
1318 if (mHeadsUpNotificationView.getEntry() == null) {
1319 tick(notification, true);
1322 addNotificationViews(shadeEntry, ranking);
1323 // Recalculate the position of the sliding windows and the titles.
1324 setAreThereNotifications();
1325 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1328 public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
1329 NotificationData.Entry shadeEntry = createNotificationViews(notification);
1330 if (shadeEntry == null) {
1333 shadeEntry.setInterruption();
1335 addNotificationViews(shadeEntry, null);
1336 // Recalculate the position of the sliding windows and the titles.
1337 setAreThereNotifications();
1338 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1342 public void resetHeadsUpDecayTimer() {
1343 mHandler.removeMessages(MSG_DECAY_HEADS_UP);
1344 if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
1345 && mHeadsUpNotificationView.isClearable()) {
1346 mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
1351 public void scheduleHeadsUpOpen() {
1352 mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
1356 public void scheduleHeadsUpClose() {
1357 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1361 public void scheduleHeadsUpEscalation() {
1362 mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1366 protected void updateNotificationRanking(RankingMap ranking) {
1367 mNotificationData.updateRanking(ranking);
1368 updateNotifications();
1372 public void removeNotification(String key, RankingMap ranking) {
1373 if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
1374 && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
1375 mHeadsUpNotificationView.clear();
1378 StatusBarNotification old = removeNotificationViews(key, ranking);
1379 if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1382 // Cancel the ticker if it's still running
1383 if (mTickerEnabled) {
1384 mTicker.removeEntry(old);
1387 // Recalculate the position of the sliding windows and the titles.
1388 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1390 if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
1391 && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
1392 if (mState == StatusBarState.SHADE) {
1393 animateCollapsePanels();
1394 } else if (mState == StatusBarState.SHADE_LOCKED) {
1399 setAreThereNotifications();
1403 protected void refreshLayout(int layoutDirection) {
1404 if (mNavigationBarView != null) {
1405 mNavigationBarView.setLayoutDirection(layoutDirection);
1407 refreshAllStatusBarIcons();
1410 private void updateShowSearchHoldoff() {
1411 mShowSearchHoldoff = mContext.getResources().getInteger(
1412 R.integer.config_show_search_delay);
1415 private void updateNotificationShade() {
1416 if (mStackScroller == null) return;
1418 // Do not modify the notifications during collapse.
1419 if (isCollapsing()) {
1420 addPostCollapseAction(new Runnable() {
1423 updateNotificationShade();
1429 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1430 ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
1431 final int N = activeNotifications.size();
1432 for (int i=0; i<N; i++) {
1433 Entry ent = activeNotifications.get(i);
1434 int vis = ent.notification.getNotification().visibility;
1436 // Display public version of the notification if we need to redact.
1437 final boolean hideSensitive =
1438 !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
1439 boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
1440 boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
1441 boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
1442 boolean showingPublic = sensitive && isLockscreenPublicMode();
1443 ent.row.setSensitive(sensitive);
1444 if (ent.autoRedacted && ent.legacy) {
1445 // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
1446 // for legacy auto redacted notifications.
1447 if (showingPublic) {
1448 ent.row.setShowingLegacyBackground(false);
1450 ent.row.setShowingLegacyBackground(true);
1453 toShow.add(ent.row);
1456 ArrayList<View> toRemove = new ArrayList<View>();
1457 for (int i=0; i< mStackScroller.getChildCount(); i++) {
1458 View child = mStackScroller.getChildAt(i);
1459 if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1460 toRemove.add(child);
1464 for (View remove : toRemove) {
1465 mStackScroller.removeView(remove);
1467 for (int i=0; i<toShow.size(); i++) {
1468 View v = toShow.get(i);
1469 if (v.getParent() == null) {
1470 mStackScroller.addView(v);
1474 // So after all this work notifications still aren't sorted correctly.
1475 // Let's do that now by advancing through toShow and mStackScroller in
1476 // lock-step, making sure mStackScroller matches what we see in toShow.
1478 for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1479 View child = mStackScroller.getChildAt(i);
1480 if (!(child instanceof ExpandableNotificationRow)) {
1481 // We don't care about non-notification views.
1485 if (child == toShow.get(j)) {
1486 // Everything is well, advance both lists.
1491 // Oops, wrong notification at this position. Put the right one
1492 // here and advance both lists.
1493 mStackScroller.changeViewPosition(toShow.get(j), i);
1499 updateEmptyShadeView();
1501 // Disable QS if device not provisioned.
1502 // If the user switcher is simple then disable QS during setup because
1503 // the user intends to use the lock screen user switcher, QS in not needed.
1504 mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
1505 && (!mUserSwitcherController.isSimpleUserSwitcher() || mUserSetup));
1506 mShadeUpdates.check();
1509 private boolean packageHasVisibilityOverride(String key) {
1510 return mNotificationData.getVisibilityOverride(key)
1511 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
1514 private void updateClearAll() {
1515 boolean showDismissView =
1516 mState != StatusBarState.KEYGUARD &&
1517 mNotificationData.hasActiveClearableNotifications();
1518 mStackScroller.updateDismissView(showDismissView);
1521 private void updateEmptyShadeView() {
1522 boolean showEmptyShade =
1523 mState != StatusBarState.KEYGUARD &&
1524 mNotificationData.getActiveNotifications().size() == 0;
1525 mNotificationPanel.setShadeEmpty(showEmptyShade);
1528 private void updateSpeedbump() {
1529 int speedbumpIndex = -1;
1530 int currentIndex = 0;
1531 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1532 final int N = activeNotifications.size();
1533 for (int i = 0; i < N; i++) {
1534 Entry entry = activeNotifications.get(i);
1535 if (entry.row.getVisibility() != View.GONE &&
1536 mNotificationData.isAmbient(entry.key)) {
1537 speedbumpIndex = currentIndex;
1542 mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
1546 protected void updateNotifications() {
1547 // TODO: Move this into updateNotificationIcons()?
1548 if (mNotificationIcons == null) return;
1550 mNotificationData.filterAndSort();
1552 updateNotificationShade();
1553 updateNotificationIcons();
1556 private void updateNotificationIcons() {
1557 final LinearLayout.LayoutParams params
1558 = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1560 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1561 final int N = activeNotifications.size();
1562 ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
1564 // Filter out notifications with low scores.
1565 for (int i = 0; i < N; i++) {
1566 Entry ent = activeNotifications.get(i);
1567 if (ent.notification.getScore() < HIDE_ICONS_BELOW_SCORE &&
1568 !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
1571 toShow.add(ent.icon);
1575 Log.d(TAG, "refreshing icons: " + toShow.size() +
1576 " notifications, mNotificationIcons=" + mNotificationIcons);
1579 ArrayList<View> toRemove = new ArrayList<View>();
1580 for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1581 View child = mNotificationIcons.getChildAt(i);
1582 if (!toShow.contains(child)) {
1583 toRemove.add(child);
1587 final int toRemoveCount = toRemove.size();
1588 for (int i = 0; i < toRemoveCount; i++) {
1589 mNotificationIcons.removeView(toRemove.get(i));
1592 for (int i=0; i<toShow.size(); i++) {
1593 View v = toShow.get(i);
1594 if (v.getParent() == null) {
1595 mNotificationIcons.addView(v, i, params);
1599 // Resort notification icons
1600 final int childCount = mNotificationIcons.getChildCount();
1601 for (int i = 0; i < childCount; i++) {
1602 View actual = mNotificationIcons.getChildAt(i);
1603 StatusBarIconView expected = toShow.get(i);
1604 if (actual == expected) {
1607 mNotificationIcons.removeView(expected);
1608 mNotificationIcons.addView(expected, i);
1613 protected void updateRowStates() {
1614 super.updateRowStates();
1615 mNotificationPanel.notifyVisibleChildrenChanged();
1618 protected void updateCarrierLabelVisibility(boolean force) {
1619 // TODO: Handle this for the notification stack scroller as well
1620 if (!mShowCarrierInPanel) return;
1621 // The idea here is to only show the carrier label when there is enough room to see it,
1622 // i.e. when there aren't enough notifications to fill the panel.
1624 Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
1625 mStackScroller.getHeight(), mStackScroller.getHeight(),
1626 mCarrierLabelHeight));
1629 // Emergency calls only is shown in the expanded header now.
1630 final boolean emergencyCallsShownElsewhere = true;
1631 final boolean makeVisible =
1632 !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1633 && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
1634 - mCarrierLabelHeight - mStatusBarHeaderHeight)
1635 && mStackScroller.getVisibility() == View.VISIBLE
1636 && mState != StatusBarState.KEYGUARD;
1638 if (force || mCarrierLabelVisible != makeVisible) {
1639 mCarrierLabelVisible = makeVisible;
1641 Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1643 mCarrierLabel.animate().cancel();
1645 mCarrierLabel.setVisibility(View.VISIBLE);
1647 mCarrierLabel.animate()
1648 .alpha(makeVisible ? 1f : 0f)
1649 //.setStartDelay(makeVisible ? 500 : 0)
1650 //.setDuration(makeVisible ? 750 : 100)
1652 .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1654 public void onAnimationEnd(Animator animation) {
1655 if (!mCarrierLabelVisible) { // race
1656 mCarrierLabel.setVisibility(View.INVISIBLE);
1657 mCarrierLabel.setAlpha(0f);
1666 protected void setAreThereNotifications() {
1669 final boolean clearable = hasActiveNotifications() &&
1670 mNotificationData.hasActiveClearableNotifications();
1671 Log.d(TAG, "setAreThereNotifications: N=" +
1672 mNotificationData.getActiveNotifications().size() + " any=" +
1673 hasActiveNotifications() + " clearable=" + clearable);
1676 final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1677 final boolean showDot = hasActiveNotifications() && !areLightsOn();
1678 if (showDot != (nlo.getAlpha() == 1.0f)) {
1681 nlo.setVisibility(View.VISIBLE);
1685 .setDuration(showDot?750:250)
1686 .setInterpolator(new AccelerateInterpolator(2.0f))
1687 .setListener(showDot ? null : new AnimatorListenerAdapter() {
1689 public void onAnimationEnd(Animator _a) {
1690 nlo.setVisibility(View.GONE);
1696 findAndUpdateMediaNotifications();
1698 updateCarrierLabelVisibility(false);
1701 public void findAndUpdateMediaNotifications() {
1702 boolean metaDataChanged = false;
1704 synchronized (mNotificationData) {
1705 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1706 final int N = activeNotifications.size();
1707 Entry mediaNotification = null;
1708 MediaController controller = null;
1709 for (int i = 0; i < N; i++) {
1710 final Entry entry = activeNotifications.get(i);
1711 if (isMediaNotification(entry)) {
1712 final MediaSession.Token token = entry.notification.getNotification().extras
1713 .getParcelable(Notification.EXTRA_MEDIA_SESSION);
1714 if (token != null) {
1715 controller = new MediaController(mContext, token);
1716 if (controller != null) {
1717 // we've got a live one, here
1718 mediaNotification = entry;
1724 if (mediaNotification == null) {
1725 // Still nothing? OK, let's just look for live media sessions and see if they match
1726 // one of our notifications. This will catch apps that aren't (yet!) using media
1729 if (mMediaSessionManager != null) {
1730 final List<MediaController> sessions
1731 = mMediaSessionManager.getActiveSessionsForUser(
1733 UserHandle.USER_ALL);
1735 for (MediaController aController : sessions) {
1736 if (aController == null) continue;
1737 final PlaybackState state = aController.getPlaybackState();
1738 if (state == null) continue;
1739 switch (state.getState()) {
1740 case PlaybackState.STATE_STOPPED:
1741 case PlaybackState.STATE_ERROR:
1744 // now to see if we have one like this
1745 final String pkg = aController.getPackageName();
1747 for (int i = 0; i < N; i++) {
1748 final Entry entry = activeNotifications.get(i);
1749 if (entry.notification.getPackageName().equals(pkg)) {
1751 Log.v(TAG, "DEBUG_MEDIA: found controller matching "
1752 + entry.notification.getKey());
1754 controller = aController;
1755 mediaNotification = entry;
1764 if (!sameSessions(mMediaController, controller)) {
1765 // We have a new media session
1767 if (mMediaController != null) {
1768 // something old was playing
1769 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
1770 + mMediaController);
1771 mMediaController.unregisterCallback(mMediaListener);
1773 mMediaController = controller;
1775 if (mMediaController != null) {
1776 mMediaController.registerCallback(mMediaListener);
1777 mMediaMetadata = mMediaController.getMetadata();
1779 Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
1783 final String notificationKey = mediaNotification == null
1785 : mediaNotification.notification.getKey();
1787 if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
1788 // we have a new notification!
1790 Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
1791 + notificationKey + " controller=" + controller);
1793 mMediaNotificationKey = notificationKey;
1796 mMediaMetadata = null;
1797 mMediaNotificationKey = null;
1800 metaDataChanged = true;
1802 // Media session unchanged
1805 Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
1810 updateMediaMetaData(metaDataChanged);
1813 private boolean sameSessions(MediaController a, MediaController b) {
1814 if (a == b) return true;
1815 if (a == null) return false;
1816 return a.controlsSameSession(b);
1820 * Hide the album artwork that is fading out and release its bitmap.
1822 private Runnable mHideBackdropFront = new Runnable() {
1826 Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
1828 mBackdropFront.setVisibility(View.INVISIBLE);
1829 mBackdropFront.animate().cancel();
1830 mBackdropFront.setImageDrawable(null);
1835 * Refresh or remove lockscreen artwork from media metadata.
1837 public void updateMediaMetaData(boolean metaDataChanged) {
1838 if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
1840 if (mBackdrop == null) return; // called too early
1843 Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
1844 + " metadata=" + mMediaMetadata
1845 + " metaDataChanged=" + metaDataChanged
1846 + " state=" + mState);
1849 Bitmap artworkBitmap = null;
1850 if (mMediaMetadata != null) {
1851 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
1852 if (artworkBitmap == null) {
1853 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
1854 // might still be null
1858 final boolean hasArtwork = artworkBitmap != null;
1860 if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
1861 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
1862 // time to show some art!
1863 if (mBackdrop.getVisibility() != View.VISIBLE) {
1864 mBackdrop.setVisibility(View.VISIBLE);
1865 mBackdrop.animate().alpha(1f);
1866 metaDataChanged = true;
1868 Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
1871 if (metaDataChanged) {
1872 if (mBackdropBack.getDrawable() != null) {
1873 Drawable drawable = mBackdropBack.getDrawable();
1874 mBackdropFront.setImageDrawable(drawable);
1875 if (mScrimSrcModeEnabled) {
1876 mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
1878 mBackdropFront.setAlpha(1f);
1879 mBackdropFront.setVisibility(View.VISIBLE);
1881 mBackdropFront.setVisibility(View.INVISIBLE);
1884 if (DEBUG_MEDIA_FAKE_ARTWORK) {
1885 final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
1886 Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
1887 mBackdropBack.setBackgroundColor(0xFFFFFFFF);
1888 mBackdropBack.setImageDrawable(new ColorDrawable(c));
1890 mBackdropBack.setImageBitmap(artworkBitmap);
1892 if (mScrimSrcModeEnabled) {
1893 mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
1896 if (mBackdropFront.getVisibility() == View.VISIBLE) {
1898 Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
1899 + mBackdropFront.getDrawable()
1901 + mBackdropBack.getDrawable());
1903 mBackdropFront.animate()
1905 .alpha(0f).withEndAction(mHideBackdropFront);
1909 // need to hide the album art, either because we are unlocked or because
1910 // the metadata isn't there to support it
1911 if (mBackdrop.getVisibility() != View.GONE) {
1913 Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
1917 .setInterpolator(mBackdropInterpolator)
1920 .withEndAction(new Runnable() {
1923 mBackdrop.setVisibility(View.GONE);
1924 mBackdropFront.animate().cancel();
1925 mBackdropBack.animate().cancel();
1926 mHandler.post(mHideBackdropFront);
1929 if (mKeyguardFadingAway) {
1932 // Make it disappear faster, as the focus should be on the activity behind.
1933 .setDuration(mKeyguardFadingAwayDuration / 2)
1934 .setStartDelay(mKeyguardFadingAwayDelay)
1935 .setInterpolator(mLinearInterpolator)
1942 public void showClock(boolean show) {
1943 if (mStatusBarView == null) return;
1944 View clock = mStatusBarView.findViewById(R.id.clock);
1945 if (clock != null) {
1946 clock.setVisibility(show ? View.VISIBLE : View.GONE);
1950 private int adjustDisableFlags(int state) {
1951 if (!mLaunchTransitionFadingAway
1952 && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
1953 state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
1954 state |= StatusBarManager.DISABLE_SYSTEM_INFO;
1960 * State is one or more of the DISABLE constants from StatusBarManager.
1962 public void disable(int state, boolean animate) {
1963 mDisabledUnmodified = state;
1964 state = adjustDisableFlags(state);
1965 final int old = mDisabled;
1966 final int diff = state ^ old;
1970 Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1974 StringBuilder flagdbg = new StringBuilder();
1975 flagdbg.append("disable: < ");
1976 flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1977 flagdbg.append(((diff & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1978 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1979 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1980 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1981 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1982 flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1983 flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1984 flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1985 flagdbg.append(((diff & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1986 flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1987 flagdbg.append(((diff & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1988 flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1989 flagdbg.append(((diff & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1990 flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1991 flagdbg.append(((diff & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1992 flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
1993 flagdbg.append(((diff & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
1994 flagdbg.append(">");
1995 Log.d(TAG, flagdbg.toString());
1997 if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1998 mSystemIconArea.animate().cancel();
1999 if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
2000 animateStatusBarHide(mSystemIconArea, animate);
2002 animateStatusBarShow(mSystemIconArea, animate);
2006 if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
2007 boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
2010 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
2011 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
2012 animateCollapsePanels();
2016 if ((diff & (StatusBarManager.DISABLE_HOME
2017 | StatusBarManager.DISABLE_RECENT
2018 | StatusBarManager.DISABLE_BACK
2019 | StatusBarManager.DISABLE_SEARCH)) != 0) {
2020 // the nav bar will take care of these
2021 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
2023 if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
2024 // close recents if it's visible
2025 mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2026 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2030 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2031 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2035 animateStatusBarHide(mNotificationIconArea, animate);
2037 animateStatusBarShow(mNotificationIconArea, animate);
2041 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
2042 mDisableNotificationAlerts =
2043 (state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
2044 mHeadsUpObserver.onChange(true);
2049 * Animates {@code v}, a view that is part of the status bar, out.
2051 private void animateStatusBarHide(final View v, boolean animate) {
2052 v.animate().cancel();
2055 v.setVisibility(View.INVISIBLE);
2062 .setInterpolator(ALPHA_OUT)
2063 .withEndAction(new Runnable() {
2066 v.setVisibility(View.INVISIBLE);
2072 * Animates {@code v}, a view that is part of the status bar, in.
2074 private void animateStatusBarShow(View v, boolean animate) {
2075 v.animate().cancel();
2076 v.setVisibility(View.VISIBLE);
2084 .setInterpolator(ALPHA_IN)
2087 // We need to clean up any pending end action from animateStatusBarHide if we call
2088 // both hide and show in the same frame before the animation actually gets started.
2089 // cancel() doesn't really remove the end action.
2090 .withEndAction(null);
2092 // Synchronize the motion with the Keyguard fading if necessary.
2093 if (mKeyguardFadingAway) {
2095 .setDuration(mKeyguardFadingAwayDuration)
2096 .setInterpolator(mLinearOutSlowIn)
2097 .setStartDelay(mKeyguardFadingAwayDelay)
2103 protected BaseStatusBar.H createHandler() {
2104 return new PhoneStatusBar.H();
2108 public void startActivity(Intent intent, boolean dismissShade) {
2109 startActivityDismissingKeyguard(intent, false, dismissShade);
2112 public ScrimController getScrimController() {
2113 return mScrimController;
2116 public void setQsExpanded(boolean expanded) {
2117 mStatusBarWindowManager.setQsExpanded(expanded);
2120 public boolean isGoingToNotificationShade() {
2121 return mLeaveOpenOnKeyguardHide;
2124 public boolean isQsExpanded() {
2125 return mNotificationPanel.isQsExpanded();
2128 public boolean isScreenOnComingFromTouch() {
2129 return mScreenOnComingFromTouch;
2132 public boolean isFalsingThresholdNeeded() {
2133 boolean onKeyguard = getBarState() == StatusBarState.KEYGUARD;
2134 boolean isMethodInsecure = mUnlockMethodCache.isMethodInsecure();
2135 return onKeyguard && (isMethodInsecure || mDozing || mScreenOnComingFromTouch);
2138 public boolean isDozing() {
2142 @Override // NotificationData.Environment
2143 public String getCurrentMediaNotificationKey() {
2144 return mMediaNotificationKey;
2147 public boolean isScrimSrcModeEnabled() {
2148 return mScrimSrcModeEnabled;
2152 * All changes to the status bar and notifications funnel through here and are batched.
2154 private class H extends BaseStatusBar.H {
2155 public void handleMessage(Message m) {
2156 super.handleMessage(m);
2158 case MSG_OPEN_NOTIFICATION_PANEL:
2159 animateExpandNotificationsPanel();
2161 case MSG_OPEN_SETTINGS_PANEL:
2162 animateExpandSettingsPanel();
2164 case MSG_CLOSE_PANELS:
2165 animateCollapsePanels();
2167 case MSG_SHOW_HEADS_UP:
2168 setHeadsUpVisibility(true);
2170 case MSG_DECAY_HEADS_UP:
2171 mHeadsUpNotificationView.release();
2172 setHeadsUpVisibility(false);
2174 case MSG_HIDE_HEADS_UP:
2175 mHeadsUpNotificationView.release();
2176 setHeadsUpVisibility(false);
2178 case MSG_ESCALATE_HEADS_UP:
2180 setHeadsUpVisibility(false);
2186 /** if the interrupting notification had a fullscreen intent, fire it now. */
2187 private void escalateHeadsUp() {
2188 if (mHeadsUpNotificationView.getEntry() != null) {
2189 final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
2190 mHeadsUpNotificationView.release();
2191 final Notification notification = sbn.getNotification();
2192 if (notification.fullScreenIntent != null) {
2194 Log.d(TAG, "converting a heads up to fullScreen");
2196 notification.fullScreenIntent.send();
2197 } catch (PendingIntent.CanceledException e) {
2203 View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
2204 public void onFocusChange(View v, boolean hasFocus) {
2205 // Because 'v' is a ViewGroup, all its children will be (un)selected
2206 // too, which allows marqueeing to work.
2207 v.setSelected(hasFocus);
2211 boolean panelsEnabled() {
2212 return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
2215 void makeExpandedVisible(boolean force) {
2216 if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
2217 if (!force && (mExpandedVisible || !panelsEnabled())) {
2221 mExpandedVisible = true;
2222 if (mNavigationBarView != null)
2223 mNavigationBarView.setSlippery(true);
2225 updateCarrierLabelVisibility(true);
2227 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2229 // Expand the window to encompass the full screen in anticipation of the drag.
2230 // This is only possible to do atomically because the status bar is at the top of the screen!
2231 mStatusBarWindowManager.setStatusBarExpanded(true);
2232 mStatusBarView.setFocusable(false);
2234 visibilityChanged(true);
2235 mWaitingForKeyguardExit = false;
2236 disable(mDisabledUnmodified, !force /* animate */);
2237 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2240 public void animateCollapsePanels() {
2241 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2244 private final Runnable mAnimateCollapsePanels = new Runnable() {
2247 animateCollapsePanels();
2251 public void postAnimateCollapsePanels() {
2252 mHandler.post(mAnimateCollapsePanels);
2255 public void animateCollapsePanels(int flags) {
2256 animateCollapsePanels(flags, false /* force */);
2259 public void animateCollapsePanels(int flags, boolean force) {
2261 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
2262 runPostCollapseRunnables();
2266 Log.d(TAG, "animateCollapse():"
2267 + " mExpandedVisible=" + mExpandedVisible
2268 + " flags=" + flags);
2271 if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
2272 if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
2273 mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2274 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2278 if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
2279 mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
2280 mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
2283 if (mStatusBarWindow != null) {
2284 // release focus immediately to kick off focus change transition
2285 mStatusBarWindowManager.setStatusBarFocusable(false);
2287 mStatusBarWindow.cancelExpandHelper();
2288 mStatusBarView.collapseAllPanels(true);
2292 private void runPostCollapseRunnables() {
2293 int size = mPostCollapseRunnables.size();
2294 for (int i = 0; i < size; i++) {
2295 mPostCollapseRunnables.get(i).run();
2297 mPostCollapseRunnables.clear();
2300 public ViewPropertyAnimator setVisibilityWhenDone(
2301 final ViewPropertyAnimator a, final View v, final int vis) {
2302 a.setListener(new AnimatorListenerAdapter() {
2304 public void onAnimationEnd(Animator animation) {
2305 v.setVisibility(vis);
2306 a.setListener(null); // oneshot
2312 public Animator setVisibilityWhenDone(
2313 final Animator a, final View v, final int vis) {
2314 a.addListener(new AnimatorListenerAdapter() {
2316 public void onAnimationEnd(Animator animation) {
2317 v.setVisibility(vis);
2323 public Animator interpolator(TimeInterpolator ti, Animator a) {
2324 a.setInterpolator(ti);
2328 public Animator startDelay(int d, Animator a) {
2333 public Animator start(Animator a) {
2338 final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
2339 final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
2340 final int FLIP_DURATION_OUT = 125;
2341 final int FLIP_DURATION_IN = 225;
2342 final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
2344 Animator mScrollViewAnim, mClearButtonAnim;
2347 public void animateExpandNotificationsPanel() {
2348 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2349 if (!panelsEnabled()) {
2353 mNotificationPanel.expand();
2355 if (false) postStartTracing();
2359 public void animateExpandSettingsPanel() {
2360 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2361 if (!panelsEnabled()) {
2365 // Settings are not available in setup
2366 if (!mUserSetup) return;
2368 mNotificationPanel.expand();
2369 mNotificationPanel.openQs();
2371 if (false) postStartTracing();
2374 public void animateCollapseQuickSettings() {
2375 mStatusBarView.collapseAllPanels(true);
2378 void makeExpandedInvisible() {
2379 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
2380 + " mExpandedVisible=" + mExpandedVisible);
2382 if (!mExpandedVisible || mStatusBarWindow == null) {
2386 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
2387 mStatusBarView.collapseAllPanels(/*animate=*/ false);
2389 // reset things to their proper state
2390 if (mScrollViewAnim != null) mScrollViewAnim.cancel();
2391 if (mClearButtonAnim != null) mClearButtonAnim.cancel();
2393 mStackScroller.setVisibility(View.VISIBLE);
2394 mNotificationPanel.setVisibility(View.GONE);
2396 mNotificationPanel.closeQs();
2398 mExpandedVisible = false;
2399 if (mNavigationBarView != null)
2400 mNavigationBarView.setSlippery(false);
2401 visibilityChanged(false);
2403 // Shrink the window to the size of the status bar only
2404 mStatusBarWindowManager.setStatusBarExpanded(false);
2405 mStatusBarView.setFocusable(true);
2407 // Close any "App info" popups that might have snuck on-screen
2410 runPostCollapseRunnables();
2411 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2413 disable(mDisabledUnmodified, true /* animate */);
2416 public boolean interceptTouchEvent(MotionEvent event) {
2417 if (DEBUG_GESTURES) {
2418 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
2419 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
2420 event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
2426 Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
2427 + mDisabled + " mTracking=" + mTracking);
2428 } else if (CHATTY) {
2429 if (event.getAction() != MotionEvent.ACTION_MOVE) {
2430 Log.d(TAG, String.format(
2431 "panel: %s at (%f, %f) mDisabled=0x%08x",
2432 MotionEvent.actionToString(event.getAction()),
2433 event.getRawX(), event.getRawY(), mDisabled));
2437 if (DEBUG_GESTURES) {
2438 mGestureRec.add(event);
2441 if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
2442 final boolean upOrCancel =
2443 event.getAction() == MotionEvent.ACTION_UP ||
2444 event.getAction() == MotionEvent.ACTION_CANCEL;
2445 if (upOrCancel && !mExpandedVisible) {
2446 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2448 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2454 public GestureRecorder getGestureRecorder() {
2458 private void setNavigationIconHints(int hints) {
2459 if (hints == mNavigationIconHints) return;
2461 mNavigationIconHints = hints;
2463 if (mNavigationBarView != null) {
2464 mNavigationBarView.setNavigationIconHints(hints);
2469 @Override // CommandQueue
2470 public void setWindowState(int window, int state) {
2471 boolean showing = state == WINDOW_STATE_SHOWING;
2472 if (mStatusBarWindow != null
2473 && window == StatusBarManager.WINDOW_STATUS_BAR
2474 && mStatusBarWindowState != state) {
2475 mStatusBarWindowState = state;
2476 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
2478 mStatusBarView.collapseAllPanels(false);
2481 if (mNavigationBarView != null
2482 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
2483 && mNavigationBarWindowState != state) {
2484 mNavigationBarWindowState = state;
2485 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
2489 @Override // CommandQueue
2490 public void buzzBeepBlinked() {
2491 if (mDozeServiceHost != null) {
2492 mDozeServiceHost.fireBuzzBeepBlinked();
2497 public void notificationLightOff() {
2498 if (mDozeServiceHost != null) {
2499 mDozeServiceHost.fireNotificationLight(false);
2504 public void notificationLightPulse(int argb, int onMillis, int offMillis) {
2505 if (mDozeServiceHost != null) {
2506 mDozeServiceHost.fireNotificationLight(true);
2510 @Override // CommandQueue
2511 public void setSystemUiVisibility(int vis, int mask) {
2512 final int oldVal = mSystemUiVisibility;
2513 final int newVal = (oldVal&~mask) | (vis&mask);
2514 final int diff = newVal ^ oldVal;
2515 if (DEBUG) Log.d(TAG, String.format(
2516 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
2517 Integer.toHexString(vis), Integer.toHexString(mask),
2518 Integer.toHexString(oldVal), Integer.toHexString(newVal),
2519 Integer.toHexString(diff)));
2521 // we never set the recents bit via this method, so save the prior state to prevent
2522 // clobbering the bit below
2523 final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
2525 mSystemUiVisibility = newVal;
2527 // update low profile
2528 if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
2529 final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
2531 animateCollapsePanels();
2537 setAreThereNotifications();
2540 // update status bar mode
2541 final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
2542 View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
2544 // update navigation bar mode
2545 final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
2546 oldVal, newVal, mNavigationBarView.getBarTransitions(),
2547 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
2548 final boolean sbModeChanged = sbMode != -1;
2549 final boolean nbModeChanged = nbMode != -1;
2550 boolean checkBarModes = false;
2551 if (sbModeChanged && sbMode != mStatusBarMode) {
2552 mStatusBarMode = sbMode;
2553 checkBarModes = true;
2555 if (nbModeChanged && nbMode != mNavigationBarMode) {
2556 mNavigationBarMode = nbMode;
2557 checkBarModes = true;
2559 if (checkBarModes) {
2562 if (sbModeChanged || nbModeChanged) {
2563 // update transient bar autohide
2564 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
2572 if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
2573 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
2575 if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
2576 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
2579 // restore the recents bit
2580 if (wasRecentsVisible) {
2581 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
2584 // send updated sysui visibility to window manager
2585 notifyUiVisibilityChanged(mSystemUiVisibility);
2589 private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
2590 int transientFlag, int translucentFlag) {
2591 final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
2592 final int newMode = barMode(newVis, transientFlag, translucentFlag);
2593 if (oldMode == newMode) {
2594 return -1; // no mode change
2599 private int barMode(int vis, int transientFlag, int translucentFlag) {
2600 return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
2601 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
2602 : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
2603 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
2607 private void checkBarModes() {
2608 if (mDemoMode) return;
2609 checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
2610 if (mNavigationBarView != null) {
2611 checkBarMode(mNavigationBarMode,
2612 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
2616 private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
2617 final boolean powerSave = mBatteryController.isPowerSave();
2618 final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
2620 if (powerSave && getBarState() == StatusBarState.SHADE) {
2621 mode = MODE_WARNING;
2623 transitions.transitionTo(mode, anim);
2626 private void finishBarAnimations() {
2627 mStatusBarView.getBarTransitions().finishAnimations();
2628 if (mNavigationBarView != null) {
2629 mNavigationBarView.getBarTransitions().finishAnimations();
2633 private final Runnable mCheckBarModes = new Runnable() {
2641 public void setInteracting(int barWindow, boolean interacting) {
2642 mInteractingWindows = interacting
2643 ? (mInteractingWindows | barWindow)
2644 : (mInteractingWindows & ~barWindow);
2645 if (mInteractingWindows != 0) {
2648 resumeSuspendedAutohide();
2653 private void resumeSuspendedAutohide() {
2654 if (mAutohideSuspended) {
2656 mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
2660 private void suspendAutohide() {
2661 mHandler.removeCallbacks(mAutohide);
2662 mHandler.removeCallbacks(mCheckBarModes);
2663 mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
2666 private void cancelAutohide() {
2667 mAutohideSuspended = false;
2668 mHandler.removeCallbacks(mAutohide);
2671 private void scheduleAutohide() {
2673 mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2676 private void checkUserAutohide(View v, MotionEvent event) {
2677 if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed
2678 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2679 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars
2685 private void userAutohide() {
2687 mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2690 private boolean areLightsOn() {
2691 return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2694 public void setLightsOn(boolean on) {
2695 Log.v(TAG, "setLightsOn(" + on + ")");
2697 setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2699 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2703 private void notifyUiVisibilityChanged(int vis) {
2705 mWindowManagerService.statusBarVisibilityChanged(vis);
2706 } catch (RemoteException ex) {
2710 public void topAppWindowChanged(boolean showMenu) {
2712 Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2714 if (mNavigationBarView != null) {
2715 mNavigationBarView.setMenuVisibility(showMenu);
2718 // See above re: lights-out policy for legacy apps.
2719 if (showMenu) setLightsOn(true);
2723 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
2724 boolean showImeSwitcher) {
2725 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2726 int flags = mNavigationIconHints;
2727 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2728 flags |= NAVIGATION_HINT_BACK_ALT;
2730 flags &= ~NAVIGATION_HINT_BACK_ALT;
2732 if (showImeSwitcher) {
2733 flags |= NAVIGATION_HINT_IME_SHOWN;
2735 flags &= ~NAVIGATION_HINT_IME_SHOWN;
2738 setNavigationIconHints(flags);
2742 protected void tick(StatusBarNotification n, boolean firstTime) {
2743 if (!mTickerEnabled) return;
2745 // no ticking in lights-out mode
2746 if (!areLightsOn()) return;
2748 // no ticking in Setup
2749 if (!isDeviceProvisioned()) return;
2752 if (!isNotificationForCurrentProfiles(n)) return;
2754 // Show the ticker if one is requested. Also don't do this
2755 // until status bar window is attached to the window manager,
2756 // because... well, what's the point otherwise? And trying to
2757 // run a ticker without being attached will crash!
2758 if (n.getNotification().tickerText != null && mStatusBarWindow != null
2759 && mStatusBarWindow.getWindowToken() != null) {
2760 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2761 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2762 mTicker.addEntry(n);
2767 private class MyTicker extends Ticker {
2768 MyTicker(Context context, View sb) {
2770 if (!mTickerEnabled) {
2771 Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
2776 public void tickerStarting() {
2777 if (!mTickerEnabled) return;
2779 mStatusBarContents.setVisibility(View.GONE);
2780 mTickerView.setVisibility(View.VISIBLE);
2781 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2782 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2786 public void tickerDone() {
2787 if (!mTickerEnabled) return;
2788 mStatusBarContents.setVisibility(View.VISIBLE);
2789 mTickerView.setVisibility(View.GONE);
2790 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2791 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2792 mTickingDoneListener));
2795 public void tickerHalting() {
2796 if (!mTickerEnabled) return;
2797 if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2798 mStatusBarContents.setVisibility(View.VISIBLE);
2800 .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2802 mTickerView.setVisibility(View.GONE);
2803 // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2807 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2808 public void onAnimationEnd(Animation animation) {
2811 public void onAnimationRepeat(Animation animation) {
2813 public void onAnimationStart(Animation animation) {
2817 private Animation loadAnim(int id, Animation.AnimationListener listener) {
2818 Animation anim = AnimationUtils.loadAnimation(mContext, id);
2819 if (listener != null) {
2820 anim.setAnimationListener(listener);
2825 public static String viewInfo(View v) {
2826 return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2827 + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2830 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2831 synchronized (mQueueLock) {
2832 pw.println("Current Status Bar state:");
2833 pw.println(" mExpandedVisible=" + mExpandedVisible
2834 + ", mTrackingPosition=" + mTrackingPosition);
2835 pw.println(" mTickerEnabled=" + mTickerEnabled);
2836 if (mTickerEnabled) {
2837 pw.println(" mTicking=" + mTicking);
2838 pw.println(" mTickerView: " + viewInfo(mTickerView));
2840 pw.println(" mTracking=" + mTracking);
2841 pw.println(" mDisplayMetrics=" + mDisplayMetrics);
2842 pw.println(" mStackScroller: " + viewInfo(mStackScroller));
2843 pw.println(" mStackScroller: " + viewInfo(mStackScroller)
2844 + " scroll " + mStackScroller.getScrollX()
2845 + "," + mStackScroller.getScrollY());
2848 pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
2849 pw.print(" mStatusBarWindowState=");
2850 pw.println(windowStateToString(mStatusBarWindowState));
2851 pw.print(" mStatusBarMode=");
2852 pw.println(BarTransitions.modeToString(mStatusBarMode));
2853 pw.print(" mDozing="); pw.println(mDozing);
2854 pw.print(" mZenMode=");
2855 pw.println(Settings.Global.zenModeToString(mZenMode));
2856 pw.print(" mUseHeadsUp=");
2857 pw.println(mUseHeadsUp);
2858 pw.print(" interrupting package: ");
2859 pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
2860 dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2861 if (mNavigationBarView != null) {
2862 pw.print(" mNavigationBarWindowState=");
2863 pw.println(windowStateToString(mNavigationBarWindowState));
2864 pw.print(" mNavigationBarMode=");
2865 pw.println(BarTransitions.modeToString(mNavigationBarMode));
2866 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2869 pw.print(" mNavigationBarView=");
2870 if (mNavigationBarView == null) {
2873 mNavigationBarView.dump(fd, pw, args);
2876 pw.print(" mMediaSessionManager=");
2877 pw.println(mMediaSessionManager);
2878 pw.print(" mMediaNotificationKey=");
2879 pw.println(mMediaNotificationKey);
2880 pw.print(" mMediaController=");
2881 pw.print(mMediaController);
2882 if (mMediaController != null) {
2883 pw.print(" state=" + mMediaController.getPlaybackState());
2886 pw.print(" mMediaMetadata=");
2887 pw.print(mMediaMetadata);
2888 if (mMediaMetadata != null) {
2889 pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
2893 pw.println(" Panels: ");
2894 if (mNotificationPanel != null) {
2895 pw.println(" mNotificationPanel=" +
2896 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2898 mNotificationPanel.dump(fd, pw, args);
2904 synchronized (mNotificationData) {
2905 mNotificationData.dump(pw, " ");
2908 int N = mStatusIcons.getChildCount();
2909 pw.println(" system icons: " + N);
2910 for (int i=0; i<N; i++) {
2911 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2912 pw.println(" [" + i + "] icon=" + ic);
2916 pw.println("see the logcat for a dump of the views we have created.");
2917 // must happen on ui thread
2918 mHandler.post(new Runnable() {
2920 mStatusBarView.getLocationOnScreen(mAbsPos);
2921 Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2922 + ") " + mStatusBarView.getWidth() + "x"
2923 + getStatusBarHeight());
2924 mStatusBarView.debug();
2930 if (DEBUG_GESTURES) {
2931 pw.print(" status bar gestures: ");
2932 mGestureRec.dump(fd, pw, args);
2935 if (mNetworkController != null) {
2936 mNetworkController.dump(fd, pw, args);
2938 if (mBluetoothController != null) {
2939 mBluetoothController.dump(fd, pw, args);
2941 if (mCastController != null) {
2942 mCastController.dump(fd, pw, args);
2944 if (mUserSwitcherController != null) {
2945 mUserSwitcherController.dump(fd, pw, args);
2947 if (mBatteryController != null) {
2948 mBatteryController.dump(fd, pw, args);
2950 if (mNextAlarmController != null) {
2951 mNextAlarmController.dump(fd, pw, args);
2953 if (mSecurityController != null) {
2954 mSecurityController.dump(fd, pw, args);
2958 private String hunStateToString(Entry entry) {
2959 if (entry == null) return "null";
2960 if (entry.notification == null) return "corrupt";
2961 return entry.notification.getPackageName();
2964 private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2965 pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode=");
2966 pw.println(BarTransitions.modeToString(transitions.getMode()));
2970 public void createAndAddWindows() {
2971 addStatusBarWindow();
2974 private void addStatusBarWindow() {
2975 makeStatusBarView();
2976 mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2977 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2980 static final float saturate(float a) {
2981 return a < 0f ? 0f : (a > 1f ? 1f : a);
2985 public void updateExpandedViewPos(int thingy) {
2986 if (SPEW) Log.v(TAG, "updateExpandedViewPos");
2988 // on larger devices, the notification panel is propped open a bit
2989 mNotificationPanel.setMinimumHeight(
2990 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2992 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2993 lp.gravity = mNotificationPanelGravity;
2994 mNotificationPanel.setLayoutParams(lp);
2996 updateCarrierLabelVisibility(false);
2999 // called by makeStatusbar and also by PhoneStatusBarView
3000 void updateDisplaySize() {
3001 mDisplay.getMetrics(mDisplayMetrics);
3002 mDisplay.getSize(mCurrentDisplaySize);
3003 if (DEBUG_GESTURES) {
3004 mGestureRec.tag("display",
3005 String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
3009 public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
3010 final boolean dismissShade) {
3011 if (onlyProvisioned && !isDeviceProvisioned()) return;
3013 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
3014 mContext, intent, mCurrentUserId);
3015 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
3016 dismissKeyguardThenExecute(new OnDismissAction() {
3018 public boolean onDismiss() {
3019 AsyncTask.execute(new Runnable() {
3022 if (keyguardShowing && !afterKeyguardGone) {
3023 ActivityManagerNative.getDefault()
3024 .keyguardWaitingForActivityDrawn();
3027 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
3028 mContext.startActivityAsUser(
3029 intent, new UserHandle(UserHandle.USER_CURRENT));
3030 overrideActivityPendingAppTransition(
3031 keyguardShowing && !afterKeyguardGone);
3032 } catch (RemoteException e) {
3037 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
3041 }, afterKeyguardGone);
3044 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
3045 public void onReceive(Context context, Intent intent) {
3046 if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3047 String action = intent.getAction();
3048 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
3049 int flags = CommandQueue.FLAG_EXCLUDE_NONE;
3050 String reason = intent.getStringExtra("reason");
3051 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
3052 flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
3054 animateCollapsePanels(flags);
3056 else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
3058 notifyNavigationBarScreenOn(false);
3059 notifyHeadsUpScreenOn(false);
3060 finishBarAnimations();
3061 stopNotificationLogging();
3062 resetUserExpandedStates();
3064 else if (Intent.ACTION_SCREEN_ON.equals(action)) {
3066 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
3067 repositionNavigationBar();
3068 notifyNavigationBarScreenOn(true);
3069 startNotificationLoggingIfScreenOnAndVisible();
3071 else if (ACTION_DEMO.equals(action)) {
3072 Bundle bundle = intent.getExtras();
3073 if (bundle != null) {
3074 String command = bundle.getString("command", "").trim().toLowerCase();
3075 if (command.length() > 0) {
3077 dispatchDemoCommand(command, bundle);
3078 } catch (Throwable t) {
3079 Log.w(TAG, "Error running demo command, intent=" + intent, t);
3083 } else if ("fake_artwork".equals(action)) {
3084 if (DEBUG_MEDIA_FAKE_ARTWORK) {
3085 updateMediaMetaData(true);
3091 private void resetUserExpandedStates() {
3092 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3093 final int notificationCount = activeNotifications.size();
3094 for (int i = 0; i < notificationCount; i++) {
3095 NotificationData.Entry entry = activeNotifications.get(i);
3096 if (entry.row != null) {
3097 entry.row.resetUserExpansion();
3103 protected void dismissKeyguardThenExecute(final OnDismissAction action,
3104 boolean afterKeyguardGone) {
3105 if (mStatusBarKeyguardViewManager.isShowing()) {
3106 if (UnlockMethodCache.getInstance(mContext).isMethodInsecure()
3107 && mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) {
3109 mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
3112 mStatusBarKeyguardViewManager.dismiss();
3116 mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone);
3123 // SystemUIService notifies SystemBars of configuration changes, which then calls down here
3125 protected void onConfigurationChanged(Configuration newConfig) {
3126 super.onConfigurationChanged(newConfig); // calls refreshLayout
3129 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
3131 updateDisplaySize(); // populates mDisplayMetrics
3135 repositionNavigationBar();
3136 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
3137 updateShowSearchHoldoff();
3142 public void userSwitched(int newUserId) {
3143 if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
3144 animateCollapsePanels();
3145 updateNotifications();
3146 resetUserSetupObserver();
3147 setControllerUsers();
3150 private void setControllerUsers() {
3151 if (mZenModeController != null) {
3152 mZenModeController.setUserId(mCurrentUserId);
3156 private void resetUserSetupObserver() {
3157 mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
3158 mUserSetupObserver.onChange(false);
3159 mContext.getContentResolver().registerContentObserver(
3160 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
3165 private void setHeadsUpVisibility(boolean vis) {
3166 if (!ENABLE_HEADS_UP) return;
3167 if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
3168 EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
3169 vis ? mHeadsUpNotificationView.getKey() : "",
3171 mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
3174 public void onHeadsUpDismissed() {
3175 mHeadsUpNotificationView.dismiss();
3179 * Reload some of our resources when the configuration changes.
3181 * We don't reload everything when the configuration changes -- we probably
3182 * should, but getting that smooth is tough. Someday we'll fix that. In the
3183 * meantime, just update the things that we know change.
3185 void updateResources() {
3186 // Update the quick setting tiles
3187 if (mQSPanel != null) {
3188 mQSPanel.updateResources();
3192 mLinearOutSlowIn = AnimationUtils.loadInterpolator(
3193 mContext, android.R.interpolator.linear_out_slow_in);
3195 if (mNotificationPanel != null) {
3196 mNotificationPanel.updateResources();
3198 if (mHeadsUpNotificationView != null) {
3199 mHeadsUpNotificationView.updateResources();
3201 if (mBrightnessMirrorController != null) {
3202 mBrightnessMirrorController.updateResources();
3206 private void updateClockSize() {
3207 if (mStatusBarView == null) return;
3208 TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
3209 if (clock != null) {
3210 FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size);
3213 protected void loadDimens() {
3214 final Resources res = mContext.getResources();
3216 mNaturalBarHeight = res.getDimensionPixelSize(
3217 com.android.internal.R.dimen.status_bar_height);
3219 int newIconSize = res.getDimensionPixelSize(
3220 com.android.internal.R.dimen.status_bar_icon_size);
3221 int newIconHPadding = res.getDimensionPixelSize(
3222 R.dimen.status_bar_icon_padding);
3224 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
3225 // Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
3226 mIconHPadding = newIconHPadding;
3227 mIconSize = newIconSize;
3228 //reloadAllNotificationIcons(); // reload the tray
3231 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
3233 mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
3234 if (mNotificationPanelGravity <= 0) {
3235 mNotificationPanelGravity = Gravity.START | Gravity.TOP;
3238 mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
3239 mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
3241 mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
3242 if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
3243 mNotificationPanelMinHeightFrac = 0f;
3246 mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
3247 mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
3248 mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
3250 mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
3252 if (DEBUG) Log.v(TAG, "updateResources");
3255 // Visibility reporting
3258 protected void visibilityChanged(boolean visible) {
3261 startNotificationLoggingIfScreenOnAndVisible();
3263 stopNotificationLogging();
3265 super.visibilityChanged(visible);
3268 private void stopNotificationLogging() {
3269 // Report all notifications as invisible and turn down the
3271 if (!mCurrentlyVisibleNotifications.isEmpty()) {
3272 logNotificationVisibilityChanges(
3273 Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
3274 mCurrentlyVisibleNotifications.clear();
3276 mHandler.removeCallbacks(mVisibilityReporter);
3277 mStackScroller.setChildLocationsChangedListener(null);
3280 private void startNotificationLoggingIfScreenOnAndVisible() {
3281 if (mVisible && mScreenOn) {
3282 mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
3283 // Some transitions like mScreenOn=false -> mScreenOn=true don't
3284 // cause the scroller to emit child location events. Hence generate
3285 // one ourselves to guarantee that we're reporting visible
3287 // (Note that in cases where the scroller does emit events, this
3288 // additional event doesn't break anything.)
3289 mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
3293 private void logNotificationVisibilityChanges(
3294 Collection<String> newlyVisible, Collection<String> noLongerVisible) {
3295 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
3298 String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
3299 String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
3301 mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
3302 } catch (RemoteException e) {
3311 void postStartTracing() {
3312 mHandler.postDelayed(mStartTracing, 3000);
3316 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
3317 Context.VIBRATOR_SERVICE);
3318 vib.vibrate(250, VIBRATION_ATTRIBUTES);
3321 Runnable mStartTracing = new Runnable() {
3324 SystemClock.sleep(250);
3325 Log.d(TAG, "startTracing");
3326 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
3327 mHandler.postDelayed(mStopTracing, 10000);
3331 Runnable mStopTracing = new Runnable() {
3333 android.os.Debug.stopMethodTracing();
3334 Log.d(TAG, "stopTracing");
3340 protected void haltTicker() {
3341 if (mTickerEnabled) {
3347 protected boolean shouldDisableNavbarGestures() {
3348 return !isDeviceProvisioned()
3350 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
3353 public void postStartSettingsActivity(final Intent intent, int delay) {
3354 mHandler.postDelayed(new Runnable() {
3357 handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
3362 private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
3363 startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
3366 private static class FastColorDrawable extends Drawable {
3367 private final int mColor;
3369 public FastColorDrawable(int color) {
3370 mColor = 0xff000000 | color;
3374 public void draw(Canvas canvas) {
3375 canvas.drawColor(mColor, PorterDuff.Mode.SRC);
3379 public void setAlpha(int alpha) {
3383 public void setColorFilter(ColorFilter cf) {
3387 public int getOpacity() {
3388 return PixelFormat.OPAQUE;
3392 public void setBounds(int left, int top, int right, int bottom) {
3396 public void setBounds(Rect bounds) {
3401 public void destroy() {
3403 if (mStatusBarWindow != null) {
3404 mWindowManager.removeViewImmediate(mStatusBarWindow);
3405 mStatusBarWindow = null;
3407 if (mNavigationBarView != null) {
3408 mWindowManager.removeViewImmediate(mNavigationBarView);
3409 mNavigationBarView = null;
3411 mContext.unregisterReceiver(mBroadcastReceiver);
3414 private boolean mDemoModeAllowed;
3415 private boolean mDemoMode;
3416 private DemoStatusIcons mDemoStatusIcons;
3419 public void dispatchDemoCommand(String command, Bundle args) {
3420 if (!mDemoModeAllowed) {
3421 mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
3422 "sysui_demo_allowed", 0) != 0;
3424 if (!mDemoModeAllowed) return;
3425 if (command.equals(COMMAND_ENTER)) {
3427 } else if (command.equals(COMMAND_EXIT)) {
3430 } else if (!mDemoMode) {
3431 // automatically enter demo mode on first demo command
3432 dispatchDemoCommand(COMMAND_ENTER, new Bundle());
3434 boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
3435 if (modeChange || command.equals(COMMAND_CLOCK)) {
3436 dispatchDemoCommandToView(command, args, R.id.clock);
3438 if (modeChange || command.equals(COMMAND_BATTERY)) {
3439 dispatchDemoCommandToView(command, args, R.id.battery);
3441 if (modeChange || command.equals(COMMAND_STATUS)) {
3442 if (mDemoStatusIcons == null) {
3443 mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
3445 mDemoStatusIcons.dispatchDemoCommand(command, args);
3447 if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
3448 mNetworkController.dispatchDemoCommand(command, args);
3450 if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
3451 View notifications = mStatusBarView == null ? null
3452 : mStatusBarView.findViewById(R.id.notification_icon_area);
3453 if (notifications != null) {
3454 String visible = args.getString("visible");
3455 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
3456 notifications.setVisibility(vis);
3459 if (command.equals(COMMAND_BARS)) {
3460 String mode = args.getString("mode");
3461 int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
3462 "translucent".equals(mode) ? MODE_TRANSLUCENT :
3463 "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
3464 "transparent".equals(mode) ? MODE_TRANSPARENT :
3465 "warning".equals(mode) ? MODE_WARNING :
3467 if (barMode != -1) {
3468 boolean animate = true;
3469 if (mStatusBarView != null) {
3470 mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
3472 if (mNavigationBarView != null) {
3473 mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
3479 private void dispatchDemoCommandToView(String command, Bundle args, int id) {
3480 if (mStatusBarView == null) return;
3481 View v = mStatusBarView.findViewById(id);
3482 if (v instanceof DemoMode) {
3483 ((DemoMode)v).dispatchDemoCommand(command, args);
3488 * @return The {@link StatusBarState} the status bar is in.
3490 public int getBarState() {
3494 public void showKeyguard() {
3495 setBarState(StatusBarState.KEYGUARD);
3496 updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3497 if (!mScreenOnFromKeyguard) {
3499 // If the screen is off already, we need to disable touch events because these might
3500 // collapse the panel after we expanded it, and thus we would end up with a blank
3502 mNotificationPanel.setTouchDisabled(true);
3504 instantExpandNotificationsPanel();
3505 mLeaveOpenOnKeyguardHide = false;
3506 if (mDraggedDownRow != null) {
3507 mDraggedDownRow.setUserLocked(false);
3508 mDraggedDownRow.notifyHeightChanged();
3509 mDraggedDownRow = null;
3513 public boolean isCollapsing() {
3514 return mNotificationPanel.isCollapsing();
3517 public void addPostCollapseAction(Runnable r) {
3518 mPostCollapseRunnables.add(r);
3521 public boolean isInLaunchTransition() {
3522 return mNotificationPanel.isLaunchTransitionRunning()
3523 || mNotificationPanel.isLaunchTransitionFinished();
3527 * Fades the content of the keyguard away after the launch transition is done.
3529 * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
3531 * @param endRunnable the runnable to be run when the transition is done
3533 public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
3534 final Runnable endRunnable) {
3535 Runnable hideRunnable = new Runnable() {
3538 mLaunchTransitionFadingAway = true;
3539 if (beforeFading != null) {
3542 mNotificationPanel.setAlpha(1);
3543 mNotificationPanel.animate()
3545 .setStartDelay(FADE_KEYGUARD_START_DELAY)
3546 .setDuration(FADE_KEYGUARD_DURATION)
3548 .withEndAction(new Runnable() {
3551 mNotificationPanel.setAlpha(1);
3552 if (endRunnable != null) {
3555 mLaunchTransitionFadingAway = false;
3560 if (mNotificationPanel.isLaunchTransitionRunning()) {
3561 mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
3568 * @return true if we would like to stay in the shade, false if it should go away entirely
3570 public boolean hideKeyguard() {
3571 boolean staying = mLeaveOpenOnKeyguardHide;
3572 setBarState(StatusBarState.SHADE);
3573 if (mLeaveOpenOnKeyguardHide) {
3574 mLeaveOpenOnKeyguardHide = false;
3575 mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
3576 if (mDraggedDownRow != null) {
3577 mDraggedDownRow.setUserLocked(false);
3578 mDraggedDownRow = null;
3581 instantCollapseNotificationPanel();
3583 updateKeyguardState(staying, false /* fromShadeLocked */);
3587 public long calculateGoingToFullShadeDelay() {
3588 return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
3592 * Notifies the status bar the Keyguard is fading away with the specified timings.
3594 * @param delay the animation delay in miliseconds
3595 * @param fadeoutDuration the duration of the exit animation, in milliseconds
3597 public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
3598 mKeyguardFadingAway = true;
3599 mKeyguardFadingAwayDelay = delay;
3600 mKeyguardFadingAwayDuration = fadeoutDuration;
3601 mWaitingForKeyguardExit = false;
3602 disable(mDisabledUnmodified, true /* animate */);
3605 public boolean isKeyguardFadingAway() {
3606 return mKeyguardFadingAway;
3610 * Notifies that the Keyguard fading away animation is done.
3612 public void finishKeyguardFadingAway() {
3613 mKeyguardFadingAway = false;
3616 private void updatePublicMode() {
3617 setLockscreenPublicMode(
3618 (mStatusBarKeyguardViewManager.isShowing() ||
3619 mStatusBarKeyguardViewManager.isOccluded())
3620 && mStatusBarKeyguardViewManager.isSecure());
3623 private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
3624 if (mState == StatusBarState.KEYGUARD) {
3625 mKeyguardIndicationController.setVisible(true);
3626 mNotificationPanel.resetViews();
3627 mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
3629 mKeyguardIndicationController.setVisible(false);
3630 mKeyguardUserSwitcher.setKeyguard(false,
3631 goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
3633 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3634 mScrimController.setKeyguardShowing(true);
3636 mScrimController.setKeyguardShowing(false);
3638 mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
3639 updateDozingState();
3641 updateStackScrollerState(goingToFullShade);
3642 updateNotifications();
3644 updateCarrierLabelVisibility(false);
3645 updateMediaMetaData(false);
3646 mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
3647 mStatusBarKeyguardViewManager.isSecure());
3650 private void updateDozingState() {
3651 if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
3654 mNotificationPanel.setDozing(mDozing);
3656 mKeyguardBottomArea.setVisibility(View.INVISIBLE);
3657 mStackScroller.setDark(true, false /*animate*/);
3659 mKeyguardBottomArea.setVisibility(View.VISIBLE);
3660 mStackScroller.setDark(false, false /*animate*/);
3662 mScrimController.setDozing(mDozing);
3665 public void updateStackScrollerState(boolean goingToFullShade) {
3666 if (mStackScroller == null) return;
3667 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
3668 mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
3669 mStackScroller.setDimmed(onKeyguard, false /* animate */);
3670 mStackScroller.setExpandingEnabled(!onKeyguard);
3671 ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
3672 mStackScroller.setActivatedChild(null);
3673 if (activatedChild != null) {
3674 activatedChild.makeInactive(false /* animate */);
3678 public void userActivity() {
3679 if (mState == StatusBarState.KEYGUARD) {
3680 mKeyguardViewMediatorCallback.userActivity();
3684 public boolean interceptMediaKey(KeyEvent event) {
3685 return mState == StatusBarState.KEYGUARD
3686 && mStatusBarKeyguardViewManager.interceptMediaKey(event);
3689 public boolean onMenuPressed() {
3690 return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
3693 public boolean onBackPressed() {
3694 if (mStatusBarKeyguardViewManager.onBackPressed()) {
3697 if (mNotificationPanel.isQsExpanded()) {
3698 if (mNotificationPanel.isQsDetailShowing()) {
3699 mNotificationPanel.closeQsDetail();
3701 mNotificationPanel.animateCloseQs();
3705 if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
3706 animateCollapsePanels();
3712 public boolean onSpacePressed() {
3713 if (mScreenOn != null && mScreenOn
3714 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
3715 animateCollapsePanels(0 /* flags */, true /* force */);
3721 private void showBouncer() {
3722 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3723 mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
3724 mStatusBarKeyguardViewManager.dismiss();
3728 private void instantExpandNotificationsPanel() {
3730 // Make our window larger and the panel expanded.
3731 makeExpandedVisible(true);
3732 mNotificationPanel.instantExpand();
3735 private void instantCollapseNotificationPanel() {
3736 mNotificationPanel.setExpandedFraction(0);
3740 public void onActivated(ActivatableNotificationView view) {
3741 mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
3742 ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
3743 if (previousView != null) {
3744 previousView.makeInactive(true /* animate */);
3746 mStackScroller.setActivatedChild(view);
3750 * @param state The {@link StatusBarState} to set.
3752 public void setBarState(int state) {
3754 mStatusBarWindowManager.setStatusBarState(state);
3758 public void onActivationReset(ActivatableNotificationView view) {
3759 if (view == mStackScroller.getActivatedChild()) {
3760 mKeyguardIndicationController.hideTransientIndication();
3761 mStackScroller.setActivatedChild(null);
3765 public void onTrackingStarted() {
3766 runPostCollapseRunnables();
3769 public void onUnlockHintStarted() {
3770 mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
3773 public void onHintFinished() {
3774 // Delay the reset a bit so the user can read the text.
3775 mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
3778 public void onCameraHintStarted() {
3779 mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
3782 public void onPhoneHintStarted() {
3783 mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
3786 public void onTrackingStopped(boolean expand) {
3787 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3788 if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
3795 protected int getMaxKeyguardNotifications() {
3796 return mKeyguardMaxNotificationCount;
3799 public NavigationBarView getNavigationBarView() {
3800 return mNavigationBarView;
3803 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
3806 public boolean onDraggedDown(View startingChild) {
3807 if (hasActiveNotifications()) {
3809 // We have notifications, go to locked shade.
3810 goToLockedShade(startingChild);
3814 // No notifications - abort gesture.
3820 public void onDragDownReset() {
3821 mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
3825 public void onThresholdReached() {
3826 mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
3830 public void onTouchSlopExceeded() {
3831 mStackScroller.removeLongPressCallback();
3835 public void setEmptyDragAmount(float amount) {
3836 mNotificationPanel.setEmptyDragAmount(amount);
3840 * If secure with redaction: Show bouncer, go to unlocked shade.
3842 * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
3844 * @param expandView The view to expand after going to the shade.
3846 public void goToLockedShade(View expandView) {
3847 ExpandableNotificationRow row = null;
3848 if (expandView instanceof ExpandableNotificationRow) {
3849 row = (ExpandableNotificationRow) expandView;
3850 row.setUserExpanded(true);
3852 boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
3853 || !mShowLockscreenNotifications;
3854 if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
3855 mLeaveOpenOnKeyguardHide = true;
3857 mDraggedDownRow = row;
3859 mNotificationPanel.animateToFullShade(0 /* delay */);
3860 setBarState(StatusBarState.SHADE_LOCKED);
3861 updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3863 row.setUserLocked(false);
3869 * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
3871 public void goToKeyguard() {
3872 if (mState == StatusBarState.SHADE_LOCKED) {
3873 mStackScroller.onGoToKeyguard();
3874 setBarState(StatusBarState.KEYGUARD);
3875 updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
3880 * @return a ViewGroup that spans the entire panel which contains the quick settings
3882 public ViewGroup getQuickSettingsOverlayParent() {
3883 return mNotificationPanel;
3886 public long getKeyguardFadingAwayDelay() {
3887 return mKeyguardFadingAwayDelay;
3890 public long getKeyguardFadingAwayDuration() {
3891 return mKeyguardFadingAwayDuration;
3894 public LinearLayout getSystemIcons() {
3895 return mSystemIcons;
3898 public LinearLayout getSystemIconArea() {
3899 return mSystemIconArea;
3903 public void setBouncerShowing(boolean bouncerShowing) {
3904 super.setBouncerShowing(bouncerShowing);
3905 disable(mDisabledUnmodified, true /* animate */);
3908 public void onScreenTurnedOff() {
3909 mScreenOnFromKeyguard = false;
3910 mScreenOnComingFromTouch = false;
3911 mStackScroller.setAnimationsEnabled(false);
3914 public void onScreenTurnedOn() {
3915 mScreenOnFromKeyguard = true;
3916 mStackScroller.setAnimationsEnabled(true);
3917 mNotificationPanel.onScreenTurnedOn();
3918 mNotificationPanel.setTouchDisabled(false);
3922 * This handles long-press of both back and recents. They are
3923 * handled together to capture them both being long-pressed
3924 * at the same time to exit screen pinning (lock task).
3926 * When accessibility mode is on, only a long-press from recents
3927 * is required to exit.
3929 * In all other circumstances we try to pass through long-press events
3930 * for Back, so that apps can still use it. Which can be from two things.
3931 * 1) Not currently in screen pinning (lock task).
3932 * 2) Back is long-pressed without recents.
3934 private void handleLongPressBackRecents(View v) {
3936 boolean sendBackLongPress = false;
3937 IActivityManager activityManager = ActivityManagerNative.getDefault();
3938 boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
3939 if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
3940 long time = System.currentTimeMillis();
3941 // If we recently long-pressed the other button then they were
3942 // long-pressed 'together'
3943 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
3944 activityManager.stopLockTaskModeOnCurrent();
3945 } else if ((v.getId() == R.id.back)
3946 && !mNavigationBarView.getRecentsButton().isPressed()) {
3947 // If we aren't pressing recents right now then they presses
3948 // won't be together, so send the standard long-press action.
3949 sendBackLongPress = true;
3951 mLastLockToAppLongPress = time;
3953 // If this is back still need to handle sending the long-press event.
3954 if (v.getId() == R.id.back) {
3955 sendBackLongPress = true;
3956 } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
3957 // When in accessibility mode a long press that is recents (not back)
3958 // should stop lock task.
3959 activityManager.stopLockTaskModeOnCurrent();
3962 if (sendBackLongPress) {
3963 KeyButtonView keyButtonView = (KeyButtonView) v;
3964 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
3965 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
3967 } catch (RemoteException e) {
3968 Log.d(TAG, "Unable to reach activity manager", e);
3975 protected void showRecents(boolean triggeredFromAltTab) {
3976 // Set the recents visibility flag
3977 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
3978 notifyUiVisibilityChanged(mSystemUiVisibility);
3979 super.showRecents(triggeredFromAltTab);
3983 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
3984 // Unset the recents visibility flag
3985 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
3986 notifyUiVisibilityChanged(mSystemUiVisibility);
3987 super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
3991 protected void toggleRecents() {
3992 // Toggle the recents visibility flag
3993 mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
3994 notifyUiVisibilityChanged(mSystemUiVisibility);
3995 super.toggleRecents();
3999 public void onVisibilityChanged(boolean visible) {
4000 // Update the recents visibility flag
4002 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
4004 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
4006 notifyUiVisibilityChanged(mSystemUiVisibility);
4009 public boolean hasActiveNotifications() {
4010 return !mNotificationData.getActiveNotifications().isEmpty();
4013 public void wakeUpIfDozing(long time, boolean fromTouch) {
4014 if (mDozing && mScrimController.isPulsing()) {
4015 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
4018 mScreenOnComingFromTouch = true;
4023 private final class ShadeUpdates {
4024 private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
4025 private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
4027 public void check() {
4028 mNewVisibleNotifications.clear();
4029 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
4030 for (int i = 0; i < activeNotifications.size(); i++) {
4031 final Entry entry = activeNotifications.get(i);
4032 final boolean visible = entry.row != null
4033 && entry.row.getVisibility() == View.VISIBLE;
4035 mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
4038 final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
4039 mVisibleNotifications.clear();
4040 mVisibleNotifications.addAll(mNewVisibleNotifications);
4042 // We have new notifications
4043 if (updates && mDozeServiceHost != null) {
4044 mDozeServiceHost.fireNewNotifications();
4049 private final class DozeServiceHost implements DozeHost {
4050 // Amount of time to allow to update the time shown on the screen before releasing
4051 // the wakelock. This timeout is design to compensate for the fact that we don't
4052 // currently have a way to know when time display contents have actually been
4053 // refreshed once we've finished rendering a new frame.
4054 private static final long PROCESSING_TIME = 500;
4056 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
4057 private final H mHandler = new H();
4060 public String toString() {
4061 return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
4064 public void firePowerSaveChanged(boolean active) {
4065 for (Callback callback : mCallbacks) {
4066 callback.onPowerSaveChanged(active);
4070 public void fireBuzzBeepBlinked() {
4071 for (Callback callback : mCallbacks) {
4072 callback.onBuzzBeepBlinked();
4076 public void fireNotificationLight(boolean on) {
4077 for (Callback callback : mCallbacks) {
4078 callback.onNotificationLight(on);
4082 public void fireNewNotifications() {
4083 for (Callback callback : mCallbacks) {
4084 callback.onNewNotifications();
4089 public void addCallback(@NonNull Callback callback) {
4090 mCallbacks.add(callback);
4094 public void removeCallback(@NonNull Callback callback) {
4095 mCallbacks.remove(callback);
4099 public void startDozing(@NonNull Runnable ready) {
4100 mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget();
4104 public void pulseWhileDozing(@NonNull PulseCallback callback) {
4105 mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, callback).sendToTarget();
4109 public void stopDozing() {
4110 mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget();
4114 public boolean isPowerSaveActive() {
4115 return mBatteryController != null && mBatteryController.isPowerSave();
4118 private void handleStartDozing(@NonNull Runnable ready) {
4121 DozeLog.traceDozing(mContext, mDozing);
4122 updateDozingState();
4127 private void handlePulseWhileDozing(@NonNull PulseCallback callback) {
4128 mScrimController.pulse(callback);
4131 private void handleStopDozing() {
4134 DozeLog.traceDozing(mContext, mDozing);
4135 updateDozingState();
4139 private final class H extends Handler {
4140 private static final int MSG_START_DOZING = 1;
4141 private static final int MSG_PULSE_WHILE_DOZING = 2;
4142 private static final int MSG_STOP_DOZING = 3;
4145 public void handleMessage(Message msg) {
4147 case MSG_START_DOZING:
4148 handleStartDozing((Runnable) msg.obj);
4150 case MSG_PULSE_WHILE_DOZING:
4151 handlePulseWhileDozing((PulseCallback) msg.obj);
4153 case MSG_STOP_DOZING: