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