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