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