PhoneStatusBar.java revision 0b99ea94f02b1002ef65aa64363d396a8876b583
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.systemui.statusbar.phone; 18 19 20import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 21import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; 22import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 23import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 24import static android.app.StatusBarManager.windowStateToString; 25import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; 29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; 31 32import android.animation.Animator; 33import android.animation.AnimatorListenerAdapter; 34import android.animation.TimeInterpolator; 35import android.annotation.NonNull; 36import android.app.ActivityManager; 37import android.app.ActivityManagerNative; 38import android.app.IActivityManager; 39import android.app.Notification; 40import android.app.PendingIntent; 41import android.app.StatusBarManager; 42import android.content.BroadcastReceiver; 43import android.content.Context; 44import android.content.Intent; 45import android.content.IntentFilter; 46import android.content.res.Configuration; 47import android.content.res.Resources; 48import android.database.ContentObserver; 49import android.graphics.Bitmap; 50import android.graphics.Canvas; 51import android.graphics.ColorFilter; 52import android.graphics.PixelFormat; 53import android.graphics.Point; 54import android.graphics.PorterDuff; 55import android.graphics.PorterDuffXfermode; 56import android.graphics.Rect; 57import android.graphics.Xfermode; 58import android.graphics.drawable.ColorDrawable; 59import android.graphics.drawable.Drawable; 60import android.inputmethodservice.InputMethodService; 61import android.media.AudioAttributes; 62import android.media.MediaMetadata; 63import android.media.session.MediaController; 64import android.media.session.MediaSession; 65import android.media.session.MediaSessionManager; 66import android.media.session.PlaybackState; 67import android.os.AsyncTask; 68import android.os.Bundle; 69import android.os.Handler; 70import android.os.IBinder; 71import android.os.Message; 72import android.os.PowerManager; 73import android.os.RemoteException; 74import android.os.SystemClock; 75import android.os.UserHandle; 76import android.provider.Settings; 77import android.service.notification.NotificationListenerService; 78import android.service.notification.NotificationListenerService.RankingMap; 79import android.service.notification.StatusBarNotification; 80import android.util.ArraySet; 81import android.util.DisplayMetrics; 82import android.util.EventLog; 83import android.util.Log; 84import android.view.Display; 85import android.view.Gravity; 86import android.view.HardwareCanvas; 87import android.view.KeyEvent; 88import android.view.LayoutInflater; 89import android.view.MotionEvent; 90import android.view.VelocityTracker; 91import android.view.View; 92import android.view.ViewGroup; 93import android.view.ViewGroup.LayoutParams; 94import android.view.ViewPropertyAnimator; 95import android.view.ViewStub; 96import android.view.ViewTreeObserver; 97import android.view.WindowManager; 98import android.view.accessibility.AccessibilityEvent; 99import android.view.accessibility.AccessibilityManager; 100import android.view.animation.AccelerateDecelerateInterpolator; 101import android.view.animation.AccelerateInterpolator; 102import android.view.animation.Animation; 103import android.view.animation.AnimationUtils; 104import android.view.animation.DecelerateInterpolator; 105import android.view.animation.Interpolator; 106import android.view.animation.LinearInterpolator; 107import android.view.animation.PathInterpolator; 108import android.widget.FrameLayout; 109import android.widget.ImageView; 110import android.widget.LinearLayout; 111import android.widget.TextView; 112 113import com.android.internal.statusbar.StatusBarIcon; 114import com.android.keyguard.KeyguardHostView.OnDismissAction; 115import com.android.keyguard.ViewMediatorCallback; 116import com.android.systemui.BatteryMeterView; 117import com.android.systemui.DemoMode; 118import com.android.systemui.EventLogTags; 119import com.android.systemui.FontSizeUtils; 120import com.android.systemui.R; 121import com.android.systemui.doze.DozeHost; 122import com.android.systemui.doze.DozeLog; 123import com.android.systemui.keyguard.KeyguardViewMediator; 124import com.android.systemui.qs.QSPanel; 125import com.android.systemui.statusbar.ActivatableNotificationView; 126import com.android.systemui.statusbar.BackDropView; 127import com.android.systemui.statusbar.BaseStatusBar; 128import com.android.systemui.statusbar.CommandQueue; 129import com.android.systemui.statusbar.DismissView; 130import com.android.systemui.statusbar.DragDownHelper; 131import com.android.systemui.statusbar.EmptyShadeView; 132import com.android.systemui.statusbar.ExpandableNotificationRow; 133import com.android.systemui.statusbar.GestureRecorder; 134import com.android.systemui.statusbar.KeyguardIndicationController; 135import com.android.systemui.statusbar.NotificationData; 136import com.android.systemui.statusbar.NotificationData.Entry; 137import com.android.systemui.statusbar.NotificationOverflowContainer; 138import com.android.systemui.statusbar.ScrimView; 139import com.android.systemui.statusbar.SignalClusterView; 140import com.android.systemui.statusbar.SpeedBumpView; 141import com.android.systemui.statusbar.StatusBarIconView; 142import com.android.systemui.statusbar.StatusBarState; 143import com.android.systemui.statusbar.policy.AccessibilityController; 144import com.android.systemui.statusbar.policy.BatteryController; 145import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 146import com.android.systemui.statusbar.policy.BluetoothControllerImpl; 147import com.android.systemui.statusbar.policy.BrightnessMirrorController; 148import com.android.systemui.statusbar.policy.CastControllerImpl; 149import com.android.systemui.statusbar.policy.FlashlightController; 150import com.android.systemui.statusbar.policy.HeadsUpNotificationView; 151import com.android.systemui.statusbar.policy.HotspotControllerImpl; 152import com.android.systemui.statusbar.policy.KeyButtonView; 153import com.android.systemui.statusbar.policy.KeyguardMonitor; 154import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 155import com.android.systemui.statusbar.policy.LocationControllerImpl; 156import com.android.systemui.statusbar.policy.NetworkControllerImpl; 157import com.android.systemui.statusbar.policy.NextAlarmController; 158import com.android.systemui.statusbar.policy.PreviewInflater; 159import com.android.systemui.statusbar.policy.RotationLockControllerImpl; 160import com.android.systemui.statusbar.policy.SecurityControllerImpl; 161import com.android.systemui.statusbar.policy.UserInfoController; 162import com.android.systemui.statusbar.policy.UserSwitcherController; 163import com.android.systemui.statusbar.policy.ZenModeController; 164import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 165import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; 166import com.android.systemui.statusbar.stack.StackScrollAlgorithm; 167import com.android.systemui.statusbar.stack.StackScrollState.ViewState; 168import com.android.systemui.volume.VolumeComponent; 169 170import java.io.FileDescriptor; 171import java.io.PrintWriter; 172import java.util.ArrayList; 173import java.util.Collection; 174import java.util.Collections; 175import java.util.List; 176 177public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 178 DragDownHelper.DragDownCallback, ActivityStarter { 179 static final String TAG = "PhoneStatusBar"; 180 public static final boolean DEBUG = BaseStatusBar.DEBUG; 181 public static final boolean SPEW = false; 182 public static final boolean DUMPTRUCK = true; // extra dumpsys info 183 public static final boolean DEBUG_GESTURES = false; 184 public static final boolean DEBUG_MEDIA = false; 185 public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; 186 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 mStatusBarView.collapseAllPanels(true); 2376 } 2377 2378 void makeExpandedInvisible() { 2379 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible 2380 + " mExpandedVisible=" + mExpandedVisible); 2381 2382 if (!mExpandedVisible || mStatusBarWindow == null) { 2383 return; 2384 } 2385 2386 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) 2387 mStatusBarView.collapseAllPanels(/*animate=*/ false); 2388 2389 // reset things to their proper state 2390 if (mScrollViewAnim != null) mScrollViewAnim.cancel(); 2391 if (mClearButtonAnim != null) mClearButtonAnim.cancel(); 2392 2393 mStackScroller.setVisibility(View.VISIBLE); 2394 mNotificationPanel.setVisibility(View.GONE); 2395 2396 mNotificationPanel.closeQs(); 2397 2398 mExpandedVisible = false; 2399 if (mNavigationBarView != null) 2400 mNavigationBarView.setSlippery(false); 2401 visibilityChanged(false); 2402 2403 // Shrink the window to the size of the status bar only 2404 mStatusBarWindowManager.setStatusBarExpanded(false); 2405 mStatusBarView.setFocusable(true); 2406 2407 // Close any "App info" popups that might have snuck on-screen 2408 dismissPopups(); 2409 2410 runPostCollapseRunnables(); 2411 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 2412 showBouncer(); 2413 disable(mDisabledUnmodified, true /* animate */); 2414 } 2415 2416 public boolean interceptTouchEvent(MotionEvent event) { 2417 if (DEBUG_GESTURES) { 2418 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { 2419 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, 2420 event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled); 2421 } 2422 2423 } 2424 2425 if (SPEW) { 2426 Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" 2427 + mDisabled + " mTracking=" + mTracking); 2428 } else if (CHATTY) { 2429 if (event.getAction() != MotionEvent.ACTION_MOVE) { 2430 Log.d(TAG, String.format( 2431 "panel: %s at (%f, %f) mDisabled=0x%08x", 2432 MotionEvent.actionToString(event.getAction()), 2433 event.getRawX(), event.getRawY(), mDisabled)); 2434 } 2435 } 2436 2437 if (DEBUG_GESTURES) { 2438 mGestureRec.add(event); 2439 } 2440 2441 if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { 2442 final boolean upOrCancel = 2443 event.getAction() == MotionEvent.ACTION_UP || 2444 event.getAction() == MotionEvent.ACTION_CANCEL; 2445 if (upOrCancel && !mExpandedVisible) { 2446 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 2447 } else { 2448 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 2449 } 2450 } 2451 return false; 2452 } 2453 2454 public GestureRecorder getGestureRecorder() { 2455 return mGestureRec; 2456 } 2457 2458 private void setNavigationIconHints(int hints) { 2459 if (hints == mNavigationIconHints) return; 2460 2461 mNavigationIconHints = hints; 2462 2463 if (mNavigationBarView != null) { 2464 mNavigationBarView.setNavigationIconHints(hints); 2465 } 2466 checkBarModes(); 2467 } 2468 2469 @Override // CommandQueue 2470 public void setWindowState(int window, int state) { 2471 boolean showing = state == WINDOW_STATE_SHOWING; 2472 if (mStatusBarWindow != null 2473 && window == StatusBarManager.WINDOW_STATUS_BAR 2474 && mStatusBarWindowState != state) { 2475 mStatusBarWindowState = state; 2476 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); 2477 if (!showing) { 2478 mStatusBarView.collapseAllPanels(false); 2479 } 2480 } 2481 if (mNavigationBarView != null 2482 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 2483 && mNavigationBarWindowState != state) { 2484 mNavigationBarWindowState = state; 2485 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 2486 } 2487 } 2488 2489 @Override // CommandQueue 2490 public void buzzBeepBlinked() { 2491 if (mDozeServiceHost != null) { 2492 mDozeServiceHost.fireBuzzBeepBlinked(); 2493 } 2494 } 2495 2496 @Override 2497 public void notificationLightOff() { 2498 if (mDozeServiceHost != null) { 2499 mDozeServiceHost.fireNotificationLight(false); 2500 } 2501 } 2502 2503 @Override 2504 public void notificationLightPulse(int argb, int onMillis, int offMillis) { 2505 if (mDozeServiceHost != null) { 2506 mDozeServiceHost.fireNotificationLight(true); 2507 } 2508 } 2509 2510 @Override // CommandQueue 2511 public void setSystemUiVisibility(int vis, int mask) { 2512 final int oldVal = mSystemUiVisibility; 2513 final int newVal = (oldVal&~mask) | (vis&mask); 2514 final int diff = newVal ^ oldVal; 2515 if (DEBUG) Log.d(TAG, String.format( 2516 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s", 2517 Integer.toHexString(vis), Integer.toHexString(mask), 2518 Integer.toHexString(oldVal), Integer.toHexString(newVal), 2519 Integer.toHexString(diff))); 2520 if (diff != 0) { 2521 // we never set the recents bit via this method, so save the prior state to prevent 2522 // clobbering the bit below 2523 final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; 2524 2525 mSystemUiVisibility = newVal; 2526 2527 // update low profile 2528 if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 2529 final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0; 2530 if (lightsOut) { 2531 animateCollapsePanels(); 2532 if (mTicking) { 2533 haltTicker(); 2534 } 2535 } 2536 2537 setAreThereNotifications(); 2538 } 2539 2540 // update status bar mode 2541 final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), 2542 View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); 2543 2544 // update navigation bar mode 2545 final int nbMode = mNavigationBarView == null ? -1 : computeBarMode( 2546 oldVal, newVal, mNavigationBarView.getBarTransitions(), 2547 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT); 2548 final boolean sbModeChanged = sbMode != -1; 2549 final boolean nbModeChanged = nbMode != -1; 2550 boolean checkBarModes = false; 2551 if (sbModeChanged && sbMode != mStatusBarMode) { 2552 mStatusBarMode = sbMode; 2553 checkBarModes = true; 2554 } 2555 if (nbModeChanged && nbMode != mNavigationBarMode) { 2556 mNavigationBarMode = nbMode; 2557 checkBarModes = true; 2558 } 2559 if (checkBarModes) { 2560 checkBarModes(); 2561 } 2562 if (sbModeChanged || nbModeChanged) { 2563 // update transient bar autohide 2564 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) { 2565 scheduleAutohide(); 2566 } else { 2567 cancelAutohide(); 2568 } 2569 } 2570 2571 // ready to unhide 2572 if ((vis & View.STATUS_BAR_UNHIDE) != 0) { 2573 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; 2574 } 2575 if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { 2576 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; 2577 } 2578 2579 // restore the recents bit 2580 if (wasRecentsVisible) { 2581 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 2582 } 2583 2584 // send updated sysui visibility to window manager 2585 notifyUiVisibilityChanged(mSystemUiVisibility); 2586 } 2587 } 2588 2589 private int computeBarMode(int oldVis, int newVis, BarTransitions transitions, 2590 int transientFlag, int translucentFlag) { 2591 final int oldMode = barMode(oldVis, transientFlag, translucentFlag); 2592 final int newMode = barMode(newVis, transientFlag, translucentFlag); 2593 if (oldMode == newMode) { 2594 return -1; // no mode change 2595 } 2596 return newMode; 2597 } 2598 2599 private int barMode(int vis, int transientFlag, int translucentFlag) { 2600 return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT 2601 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT 2602 : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT 2603 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT 2604 : MODE_OPAQUE; 2605 } 2606 2607 private void checkBarModes() { 2608 if (mDemoMode) return; 2609 checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); 2610 if (mNavigationBarView != null) { 2611 checkBarMode(mNavigationBarMode, 2612 mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); 2613 } 2614 } 2615 2616 private void checkBarMode(int mode, int windowState, BarTransitions transitions) { 2617 final boolean powerSave = mBatteryController.isPowerSave(); 2618 final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN 2619 && !powerSave; 2620 if (powerSave && getBarState() == StatusBarState.SHADE) { 2621 mode = MODE_WARNING; 2622 } 2623 transitions.transitionTo(mode, anim); 2624 } 2625 2626 private void finishBarAnimations() { 2627 mStatusBarView.getBarTransitions().finishAnimations(); 2628 if (mNavigationBarView != null) { 2629 mNavigationBarView.getBarTransitions().finishAnimations(); 2630 } 2631 } 2632 2633 private final Runnable mCheckBarModes = new Runnable() { 2634 @Override 2635 public void run() { 2636 checkBarModes(); 2637 } 2638 }; 2639 2640 @Override 2641 public void setInteracting(int barWindow, boolean interacting) { 2642 mInteractingWindows = interacting 2643 ? (mInteractingWindows | barWindow) 2644 : (mInteractingWindows & ~barWindow); 2645 if (mInteractingWindows != 0) { 2646 suspendAutohide(); 2647 } else { 2648 resumeSuspendedAutohide(); 2649 } 2650 checkBarModes(); 2651 } 2652 2653 private void resumeSuspendedAutohide() { 2654 if (mAutohideSuspended) { 2655 scheduleAutohide(); 2656 mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher 2657 } 2658 } 2659 2660 private void suspendAutohide() { 2661 mHandler.removeCallbacks(mAutohide); 2662 mHandler.removeCallbacks(mCheckBarModes); 2663 mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0; 2664 } 2665 2666 private void cancelAutohide() { 2667 mAutohideSuspended = false; 2668 mHandler.removeCallbacks(mAutohide); 2669 } 2670 2671 private void scheduleAutohide() { 2672 cancelAutohide(); 2673 mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS); 2674 } 2675 2676 private void checkUserAutohide(View v, MotionEvent event) { 2677 if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed 2678 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar 2679 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars 2680 ) { 2681 userAutohide(); 2682 } 2683 } 2684 2685 private void userAutohide() { 2686 cancelAutohide(); 2687 mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear 2688 } 2689 2690 private boolean areLightsOn() { 2691 return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); 2692 } 2693 2694 public void setLightsOn(boolean on) { 2695 Log.v(TAG, "setLightsOn(" + on + ")"); 2696 if (on) { 2697 setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); 2698 } else { 2699 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); 2700 } 2701 } 2702 2703 private void notifyUiVisibilityChanged(int vis) { 2704 try { 2705 mWindowManagerService.statusBarVisibilityChanged(vis); 2706 } catch (RemoteException ex) { 2707 } 2708 } 2709 2710 public void topAppWindowChanged(boolean showMenu) { 2711 if (DEBUG) { 2712 Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 2713 } 2714 if (mNavigationBarView != null) { 2715 mNavigationBarView.setMenuVisibility(showMenu); 2716 } 2717 2718 // See above re: lights-out policy for legacy apps. 2719 if (showMenu) setLightsOn(true); 2720 } 2721 2722 @Override 2723 public void setImeWindowStatus(IBinder token, int vis, int backDisposition, 2724 boolean showImeSwitcher) { 2725 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 2726 int flags = mNavigationIconHints; 2727 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { 2728 flags |= NAVIGATION_HINT_BACK_ALT; 2729 } else { 2730 flags &= ~NAVIGATION_HINT_BACK_ALT; 2731 } 2732 if (showImeSwitcher) { 2733 flags |= NAVIGATION_HINT_IME_SHOWN; 2734 } else { 2735 flags &= ~NAVIGATION_HINT_IME_SHOWN; 2736 } 2737 2738 setNavigationIconHints(flags); 2739 } 2740 2741 @Override 2742 protected void tick(StatusBarNotification n, boolean firstTime) { 2743 if (!mTickerEnabled) return; 2744 2745 // no ticking in lights-out mode 2746 if (!areLightsOn()) return; 2747 2748 // no ticking in Setup 2749 if (!isDeviceProvisioned()) return; 2750 2751 // not for you 2752 if (!isNotificationForCurrentProfiles(n)) return; 2753 2754 // Show the ticker if one is requested. Also don't do this 2755 // until status bar window is attached to the window manager, 2756 // because... well, what's the point otherwise? And trying to 2757 // run a ticker without being attached will crash! 2758 if (n.getNotification().tickerText != null && mStatusBarWindow != null 2759 && mStatusBarWindow.getWindowToken() != null) { 2760 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS 2761 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { 2762 mTicker.addEntry(n); 2763 } 2764 } 2765 } 2766 2767 private class MyTicker extends Ticker { 2768 MyTicker(Context context, View sb) { 2769 super(context, sb); 2770 if (!mTickerEnabled) { 2771 Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable()); 2772 } 2773 } 2774 2775 @Override 2776 public void tickerStarting() { 2777 if (!mTickerEnabled) return; 2778 mTicking = true; 2779 mStatusBarContents.setVisibility(View.GONE); 2780 mTickerView.setVisibility(View.VISIBLE); 2781 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); 2782 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); 2783 } 2784 2785 @Override 2786 public void tickerDone() { 2787 if (!mTickerEnabled) return; 2788 mStatusBarContents.setVisibility(View.VISIBLE); 2789 mTickerView.setVisibility(View.GONE); 2790 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); 2791 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, 2792 mTickingDoneListener)); 2793 } 2794 2795 public void tickerHalting() { 2796 if (!mTickerEnabled) return; 2797 if (mStatusBarContents.getVisibility() != View.VISIBLE) { 2798 mStatusBarContents.setVisibility(View.VISIBLE); 2799 mStatusBarContents 2800 .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); 2801 } 2802 mTickerView.setVisibility(View.GONE); 2803 // we do not animate the ticker away at this point, just get rid of it (b/6992707) 2804 } 2805 } 2806 2807 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; 2808 public void onAnimationEnd(Animation animation) { 2809 mTicking = false; 2810 } 2811 public void onAnimationRepeat(Animation animation) { 2812 } 2813 public void onAnimationStart(Animation animation) { 2814 } 2815 }; 2816 2817 private Animation loadAnim(int id, Animation.AnimationListener listener) { 2818 Animation anim = AnimationUtils.loadAnimation(mContext, id); 2819 if (listener != null) { 2820 anim.setAnimationListener(listener); 2821 } 2822 return anim; 2823 } 2824 2825 public static String viewInfo(View v) { 2826 return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() 2827 + ") " + v.getWidth() + "x" + v.getHeight() + "]"; 2828 } 2829 2830 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2831 synchronized (mQueueLock) { 2832 pw.println("Current Status Bar state:"); 2833 pw.println(" mExpandedVisible=" + mExpandedVisible 2834 + ", mTrackingPosition=" + mTrackingPosition); 2835 pw.println(" mTickerEnabled=" + mTickerEnabled); 2836 if (mTickerEnabled) { 2837 pw.println(" mTicking=" + mTicking); 2838 pw.println(" mTickerView: " + viewInfo(mTickerView)); 2839 } 2840 pw.println(" mTracking=" + mTracking); 2841 pw.println(" mDisplayMetrics=" + mDisplayMetrics); 2842 pw.println(" mStackScroller: " + viewInfo(mStackScroller)); 2843 pw.println(" mStackScroller: " + viewInfo(mStackScroller) 2844 + " scroll " + mStackScroller.getScrollX() 2845 + "," + mStackScroller.getScrollY()); 2846 } 2847 2848 pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); 2849 pw.print(" mStatusBarWindowState="); 2850 pw.println(windowStateToString(mStatusBarWindowState)); 2851 pw.print(" mStatusBarMode="); 2852 pw.println(BarTransitions.modeToString(mStatusBarMode)); 2853 pw.print(" mDozing="); pw.println(mDozing); 2854 pw.print(" mZenMode="); 2855 pw.println(Settings.Global.zenModeToString(mZenMode)); 2856 pw.print(" mUseHeadsUp="); 2857 pw.println(mUseHeadsUp); 2858 pw.print(" interrupting package: "); 2859 pw.println(hunStateToString(mHeadsUpNotificationView.getEntry())); 2860 dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); 2861 if (mNavigationBarView != null) { 2862 pw.print(" mNavigationBarWindowState="); 2863 pw.println(windowStateToString(mNavigationBarWindowState)); 2864 pw.print(" mNavigationBarMode="); 2865 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 2866 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 2867 } 2868 2869 pw.print(" mNavigationBarView="); 2870 if (mNavigationBarView == null) { 2871 pw.println("null"); 2872 } else { 2873 mNavigationBarView.dump(fd, pw, args); 2874 } 2875 2876 pw.print(" mMediaSessionManager="); 2877 pw.println(mMediaSessionManager); 2878 pw.print(" mMediaNotificationKey="); 2879 pw.println(mMediaNotificationKey); 2880 pw.print(" mMediaController="); 2881 pw.print(mMediaController); 2882 if (mMediaController != null) { 2883 pw.print(" state=" + mMediaController.getPlaybackState()); 2884 } 2885 pw.println(); 2886 pw.print(" mMediaMetadata="); 2887 pw.print(mMediaMetadata); 2888 if (mMediaMetadata != null) { 2889 pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE)); 2890 } 2891 pw.println(); 2892 2893 pw.println(" Panels: "); 2894 if (mNotificationPanel != null) { 2895 pw.println(" mNotificationPanel=" + 2896 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); 2897 pw.print (" "); 2898 mNotificationPanel.dump(fd, pw, args); 2899 } 2900 2901 DozeLog.dump(pw); 2902 2903 if (DUMPTRUCK) { 2904 synchronized (mNotificationData) { 2905 mNotificationData.dump(pw, " "); 2906 } 2907 2908 int N = mStatusIcons.getChildCount(); 2909 pw.println(" system icons: " + N); 2910 for (int i=0; i<N; i++) { 2911 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i); 2912 pw.println(" [" + i + "] icon=" + ic); 2913 } 2914 2915 if (false) { 2916 pw.println("see the logcat for a dump of the views we have created."); 2917 // must happen on ui thread 2918 mHandler.post(new Runnable() { 2919 public void run() { 2920 mStatusBarView.getLocationOnScreen(mAbsPos); 2921 Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 2922 + ") " + mStatusBarView.getWidth() + "x" 2923 + getStatusBarHeight()); 2924 mStatusBarView.debug(); 2925 } 2926 }); 2927 } 2928 } 2929 2930 if (DEBUG_GESTURES) { 2931 pw.print(" status bar gestures: "); 2932 mGestureRec.dump(fd, pw, args); 2933 } 2934 2935 if (mNetworkController != null) { 2936 mNetworkController.dump(fd, pw, args); 2937 } 2938 if (mBluetoothController != null) { 2939 mBluetoothController.dump(fd, pw, args); 2940 } 2941 if (mCastController != null) { 2942 mCastController.dump(fd, pw, args); 2943 } 2944 if (mUserSwitcherController != null) { 2945 mUserSwitcherController.dump(fd, pw, args); 2946 } 2947 if (mBatteryController != null) { 2948 mBatteryController.dump(fd, pw, args); 2949 } 2950 if (mNextAlarmController != null) { 2951 mNextAlarmController.dump(fd, pw, args); 2952 } 2953 if (mSecurityController != null) { 2954 mSecurityController.dump(fd, pw, args); 2955 } 2956 } 2957 2958 private String hunStateToString(Entry entry) { 2959 if (entry == null) return "null"; 2960 if (entry.notification == null) return "corrupt"; 2961 return entry.notification.getPackageName(); 2962 } 2963 2964 private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { 2965 pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); 2966 pw.println(BarTransitions.modeToString(transitions.getMode())); 2967 } 2968 2969 @Override 2970 public void createAndAddWindows() { 2971 addStatusBarWindow(); 2972 } 2973 2974 private void addStatusBarWindow() { 2975 makeStatusBarView(); 2976 mStatusBarWindowManager = new StatusBarWindowManager(mContext); 2977 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); 2978 } 2979 2980 static final float saturate(float a) { 2981 return a < 0f ? 0f : (a > 1f ? 1f : a); 2982 } 2983 2984 @Override 2985 public void updateExpandedViewPos(int thingy) { 2986 if (SPEW) Log.v(TAG, "updateExpandedViewPos"); 2987 2988 // on larger devices, the notification panel is propped open a bit 2989 mNotificationPanel.setMinimumHeight( 2990 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y)); 2991 2992 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams(); 2993 lp.gravity = mNotificationPanelGravity; 2994 mNotificationPanel.setLayoutParams(lp); 2995 2996 updateCarrierLabelVisibility(false); 2997 } 2998 2999 // called by makeStatusbar and also by PhoneStatusBarView 3000 void updateDisplaySize() { 3001 mDisplay.getMetrics(mDisplayMetrics); 3002 mDisplay.getSize(mCurrentDisplaySize); 3003 if (DEBUG_GESTURES) { 3004 mGestureRec.tag("display", 3005 String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); 3006 } 3007 } 3008 3009 public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, 3010 final boolean dismissShade) { 3011 if (onlyProvisioned && !isDeviceProvisioned()) return; 3012 3013 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( 3014 mContext, intent, mCurrentUserId); 3015 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 3016 dismissKeyguardThenExecute(new OnDismissAction() { 3017 @Override 3018 public boolean onDismiss() { 3019 AsyncTask.execute(new Runnable() { 3020 public void run() { 3021 try { 3022 if (keyguardShowing && !afterKeyguardGone) { 3023 ActivityManagerNative.getDefault() 3024 .keyguardWaitingForActivityDrawn(); 3025 } 3026 intent.setFlags( 3027 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 3028 mContext.startActivityAsUser( 3029 intent, new UserHandle(UserHandle.USER_CURRENT)); 3030 overrideActivityPendingAppTransition( 3031 keyguardShowing && !afterKeyguardGone); 3032 } catch (RemoteException e) { 3033 } 3034 } 3035 }); 3036 if (dismissShade) { 3037 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 3038 } 3039 return true; 3040 } 3041 }, afterKeyguardGone); 3042 } 3043 3044 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 3045 public void onReceive(Context context, Intent intent) { 3046 if (DEBUG) Log.v(TAG, "onReceive: " + intent); 3047 String action = intent.getAction(); 3048 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 3049 int flags = CommandQueue.FLAG_EXCLUDE_NONE; 3050 String reason = intent.getStringExtra("reason"); 3051 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { 3052 flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; 3053 } 3054 animateCollapsePanels(flags); 3055 } 3056 else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 3057 mScreenOn = false; 3058 notifyNavigationBarScreenOn(false); 3059 notifyHeadsUpScreenOn(false); 3060 finishBarAnimations(); 3061 stopNotificationLogging(); 3062 resetUserExpandedStates(); 3063 } 3064 else if (Intent.ACTION_SCREEN_ON.equals(action)) { 3065 mScreenOn = true; 3066 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018) 3067 repositionNavigationBar(); 3068 notifyNavigationBarScreenOn(true); 3069 startNotificationLoggingIfScreenOnAndVisible(); 3070 } 3071 else if (ACTION_DEMO.equals(action)) { 3072 Bundle bundle = intent.getExtras(); 3073 if (bundle != null) { 3074 String command = bundle.getString("command", "").trim().toLowerCase(); 3075 if (command.length() > 0) { 3076 try { 3077 dispatchDemoCommand(command, bundle); 3078 } catch (Throwable t) { 3079 Log.w(TAG, "Error running demo command, intent=" + intent, t); 3080 } 3081 } 3082 } 3083 } else if ("fake_artwork".equals(action)) { 3084 if (DEBUG_MEDIA_FAKE_ARTWORK) { 3085 updateMediaMetaData(true); 3086 } 3087 } 3088 } 3089 }; 3090 3091 private void resetUserExpandedStates() { 3092 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 3093 final int notificationCount = activeNotifications.size(); 3094 for (int i = 0; i < notificationCount; i++) { 3095 NotificationData.Entry entry = activeNotifications.get(i); 3096 if (entry.row != null) { 3097 entry.row.resetUserExpansion(); 3098 } 3099 } 3100 } 3101 3102 @Override 3103 protected void dismissKeyguardThenExecute(final OnDismissAction action, 3104 boolean afterKeyguardGone) { 3105 if (mStatusBarKeyguardViewManager.isShowing()) { 3106 if (UnlockMethodCache.getInstance(mContext).isMethodInsecure() 3107 && mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) { 3108 action.onDismiss(); 3109 mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() { 3110 @Override 3111 public void run() { 3112 mStatusBarKeyguardViewManager.dismiss(); 3113 } 3114 }); 3115 } else { 3116 mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone); 3117 } 3118 } else { 3119 action.onDismiss(); 3120 } 3121 } 3122 3123 // SystemUIService notifies SystemBars of configuration changes, which then calls down here 3124 @Override 3125 protected void onConfigurationChanged(Configuration newConfig) { 3126 super.onConfigurationChanged(newConfig); // calls refreshLayout 3127 3128 if (DEBUG) { 3129 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); 3130 } 3131 updateDisplaySize(); // populates mDisplayMetrics 3132 3133 updateResources(); 3134 updateClockSize(); 3135 repositionNavigationBar(); 3136 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 3137 updateShowSearchHoldoff(); 3138 updateRowStates(); 3139 } 3140 3141 @Override 3142 public void userSwitched(int newUserId) { 3143 if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); 3144 animateCollapsePanels(); 3145 updateNotifications(); 3146 resetUserSetupObserver(); 3147 setControllerUsers(); 3148 } 3149 3150 private void setControllerUsers() { 3151 if (mZenModeController != null) { 3152 mZenModeController.setUserId(mCurrentUserId); 3153 } 3154 } 3155 3156 private void resetUserSetupObserver() { 3157 mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver); 3158 mUserSetupObserver.onChange(false); 3159 mContext.getContentResolver().registerContentObserver( 3160 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true, 3161 mUserSetupObserver, 3162 mCurrentUserId); 3163 } 3164 3165 private void setHeadsUpVisibility(boolean vis) { 3166 if (!ENABLE_HEADS_UP) return; 3167 if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); 3168 EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS, 3169 vis ? mHeadsUpNotificationView.getKey() : "", 3170 vis ? 1 : 0); 3171 mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); 3172 } 3173 3174 public void onHeadsUpDismissed() { 3175 mHeadsUpNotificationView.dismiss(); 3176 } 3177 3178 /** 3179 * Reload some of our resources when the configuration changes. 3180 * 3181 * We don't reload everything when the configuration changes -- we probably 3182 * should, but getting that smooth is tough. Someday we'll fix that. In the 3183 * meantime, just update the things that we know change. 3184 */ 3185 void updateResources() { 3186 // Update the quick setting tiles 3187 if (mQSPanel != null) { 3188 mQSPanel.updateResources(); 3189 } 3190 3191 loadDimens(); 3192 mLinearOutSlowIn = AnimationUtils.loadInterpolator( 3193 mContext, android.R.interpolator.linear_out_slow_in); 3194 3195 if (mNotificationPanel != null) { 3196 mNotificationPanel.updateResources(); 3197 } 3198 if (mHeadsUpNotificationView != null) { 3199 mHeadsUpNotificationView.updateResources(); 3200 } 3201 if (mBrightnessMirrorController != null) { 3202 mBrightnessMirrorController.updateResources(); 3203 } 3204 } 3205 3206 private void updateClockSize() { 3207 if (mStatusBarView == null) return; 3208 TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock); 3209 if (clock != null) { 3210 FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size); 3211 } 3212 } 3213 protected void loadDimens() { 3214 final Resources res = mContext.getResources(); 3215 3216 mNaturalBarHeight = res.getDimensionPixelSize( 3217 com.android.internal.R.dimen.status_bar_height); 3218 3219 int newIconSize = res.getDimensionPixelSize( 3220 com.android.internal.R.dimen.status_bar_icon_size); 3221 int newIconHPadding = res.getDimensionPixelSize( 3222 R.dimen.status_bar_icon_padding); 3223 3224 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) { 3225// Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding); 3226 mIconHPadding = newIconHPadding; 3227 mIconSize = newIconSize; 3228 //reloadAllNotificationIcons(); // reload the tray 3229 } 3230 3231 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 3232 3233 mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity); 3234 if (mNotificationPanelGravity <= 0) { 3235 mNotificationPanelGravity = Gravity.START | Gravity.TOP; 3236 } 3237 3238 mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); 3239 mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height); 3240 3241 mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); 3242 if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { 3243 mNotificationPanelMinHeightFrac = 0f; 3244 } 3245 3246 mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay); 3247 mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); 3248 mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); 3249 3250 mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count); 3251 3252 if (DEBUG) Log.v(TAG, "updateResources"); 3253 } 3254 3255 // Visibility reporting 3256 3257 @Override 3258 protected void visibilityChanged(boolean visible) { 3259 mVisible = visible; 3260 if (visible) { 3261 startNotificationLoggingIfScreenOnAndVisible(); 3262 } else { 3263 stopNotificationLogging(); 3264 } 3265 super.visibilityChanged(visible); 3266 } 3267 3268 private void stopNotificationLogging() { 3269 // Report all notifications as invisible and turn down the 3270 // reporter. 3271 if (!mCurrentlyVisibleNotifications.isEmpty()) { 3272 logNotificationVisibilityChanges( 3273 Collections.<String>emptyList(), mCurrentlyVisibleNotifications); 3274 mCurrentlyVisibleNotifications.clear(); 3275 } 3276 mHandler.removeCallbacks(mVisibilityReporter); 3277 mStackScroller.setChildLocationsChangedListener(null); 3278 } 3279 3280 private void startNotificationLoggingIfScreenOnAndVisible() { 3281 if (mVisible && mScreenOn) { 3282 mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); 3283 // Some transitions like mScreenOn=false -> mScreenOn=true don't 3284 // cause the scroller to emit child location events. Hence generate 3285 // one ourselves to guarantee that we're reporting visible 3286 // notifications. 3287 // (Note that in cases where the scroller does emit events, this 3288 // additional event doesn't break anything.) 3289 mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); 3290 } 3291 } 3292 3293 private void logNotificationVisibilityChanges( 3294 Collection<String> newlyVisible, Collection<String> noLongerVisible) { 3295 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 3296 return; 3297 } 3298 String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); 3299 String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); 3300 try { 3301 mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); 3302 } catch (RemoteException e) { 3303 // Ignore. 3304 } 3305 } 3306 3307 // 3308 // tracing 3309 // 3310 3311 void postStartTracing() { 3312 mHandler.postDelayed(mStartTracing, 3000); 3313 } 3314 3315 void vibrate() { 3316 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( 3317 Context.VIBRATOR_SERVICE); 3318 vib.vibrate(250, VIBRATION_ATTRIBUTES); 3319 } 3320 3321 Runnable mStartTracing = new Runnable() { 3322 public void run() { 3323 vibrate(); 3324 SystemClock.sleep(250); 3325 Log.d(TAG, "startTracing"); 3326 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); 3327 mHandler.postDelayed(mStopTracing, 10000); 3328 } 3329 }; 3330 3331 Runnable mStopTracing = new Runnable() { 3332 public void run() { 3333 android.os.Debug.stopMethodTracing(); 3334 Log.d(TAG, "stopTracing"); 3335 vibrate(); 3336 } 3337 }; 3338 3339 @Override 3340 protected void haltTicker() { 3341 if (mTickerEnabled) { 3342 mTicker.halt(); 3343 } 3344 } 3345 3346 @Override 3347 protected boolean shouldDisableNavbarGestures() { 3348 return !isDeviceProvisioned() 3349 || mExpandedVisible 3350 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; 3351 } 3352 3353 public void postStartSettingsActivity(final Intent intent, int delay) { 3354 mHandler.postDelayed(new Runnable() { 3355 @Override 3356 public void run() { 3357 handleStartSettingsActivity(intent, true /*onlyProvisioned*/); 3358 } 3359 }, delay); 3360 } 3361 3362 private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) { 3363 startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); 3364 } 3365 3366 private static class FastColorDrawable extends Drawable { 3367 private final int mColor; 3368 3369 public FastColorDrawable(int color) { 3370 mColor = 0xff000000 | color; 3371 } 3372 3373 @Override 3374 public void draw(Canvas canvas) { 3375 canvas.drawColor(mColor, PorterDuff.Mode.SRC); 3376 } 3377 3378 @Override 3379 public void setAlpha(int alpha) { 3380 } 3381 3382 @Override 3383 public void setColorFilter(ColorFilter cf) { 3384 } 3385 3386 @Override 3387 public int getOpacity() { 3388 return PixelFormat.OPAQUE; 3389 } 3390 3391 @Override 3392 public void setBounds(int left, int top, int right, int bottom) { 3393 } 3394 3395 @Override 3396 public void setBounds(Rect bounds) { 3397 } 3398 } 3399 3400 @Override 3401 public void destroy() { 3402 super.destroy(); 3403 if (mStatusBarWindow != null) { 3404 mWindowManager.removeViewImmediate(mStatusBarWindow); 3405 mStatusBarWindow = null; 3406 } 3407 if (mNavigationBarView != null) { 3408 mWindowManager.removeViewImmediate(mNavigationBarView); 3409 mNavigationBarView = null; 3410 } 3411 mContext.unregisterReceiver(mBroadcastReceiver); 3412 } 3413 3414 private boolean mDemoModeAllowed; 3415 private boolean mDemoMode; 3416 private DemoStatusIcons mDemoStatusIcons; 3417 3418 @Override 3419 public void dispatchDemoCommand(String command, Bundle args) { 3420 if (!mDemoModeAllowed) { 3421 mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), 3422 "sysui_demo_allowed", 0) != 0; 3423 } 3424 if (!mDemoModeAllowed) return; 3425 if (command.equals(COMMAND_ENTER)) { 3426 mDemoMode = true; 3427 } else if (command.equals(COMMAND_EXIT)) { 3428 mDemoMode = false; 3429 checkBarModes(); 3430 } else if (!mDemoMode) { 3431 // automatically enter demo mode on first demo command 3432 dispatchDemoCommand(COMMAND_ENTER, new Bundle()); 3433 } 3434 boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); 3435 if (modeChange || command.equals(COMMAND_CLOCK)) { 3436 dispatchDemoCommandToView(command, args, R.id.clock); 3437 } 3438 if (modeChange || command.equals(COMMAND_BATTERY)) { 3439 dispatchDemoCommandToView(command, args, R.id.battery); 3440 } 3441 if (modeChange || command.equals(COMMAND_STATUS)) { 3442 if (mDemoStatusIcons == null) { 3443 mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize); 3444 } 3445 mDemoStatusIcons.dispatchDemoCommand(command, args); 3446 } 3447 if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { 3448 mNetworkController.dispatchDemoCommand(command, args); 3449 } 3450 if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { 3451 View notifications = mStatusBarView == null ? null 3452 : mStatusBarView.findViewById(R.id.notification_icon_area); 3453 if (notifications != null) { 3454 String visible = args.getString("visible"); 3455 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; 3456 notifications.setVisibility(vis); 3457 } 3458 } 3459 if (command.equals(COMMAND_BARS)) { 3460 String mode = args.getString("mode"); 3461 int barMode = "opaque".equals(mode) ? MODE_OPAQUE : 3462 "translucent".equals(mode) ? MODE_TRANSLUCENT : 3463 "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT : 3464 "transparent".equals(mode) ? MODE_TRANSPARENT : 3465 "warning".equals(mode) ? MODE_WARNING : 3466 -1; 3467 if (barMode != -1) { 3468 boolean animate = true; 3469 if (mStatusBarView != null) { 3470 mStatusBarView.getBarTransitions().transitionTo(barMode, animate); 3471 } 3472 if (mNavigationBarView != null) { 3473 mNavigationBarView.getBarTransitions().transitionTo(barMode, animate); 3474 } 3475 } 3476 } 3477 } 3478 3479 private void dispatchDemoCommandToView(String command, Bundle args, int id) { 3480 if (mStatusBarView == null) return; 3481 View v = mStatusBarView.findViewById(id); 3482 if (v instanceof DemoMode) { 3483 ((DemoMode)v).dispatchDemoCommand(command, args); 3484 } 3485 } 3486 3487 /** 3488 * @return The {@link StatusBarState} the status bar is in. 3489 */ 3490 public int getBarState() { 3491 return mState; 3492 } 3493 3494 public void showKeyguard() { 3495 setBarState(StatusBarState.KEYGUARD); 3496 updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); 3497 if (!mScreenOnFromKeyguard) { 3498 3499 // If the screen is off already, we need to disable touch events because these might 3500 // collapse the panel after we expanded it, and thus we would end up with a blank 3501 // Keyguard. 3502 mNotificationPanel.setTouchDisabled(true); 3503 } 3504 instantExpandNotificationsPanel(); 3505 mLeaveOpenOnKeyguardHide = false; 3506 if (mDraggedDownRow != null) { 3507 mDraggedDownRow.setUserLocked(false); 3508 mDraggedDownRow.notifyHeightChanged(); 3509 mDraggedDownRow = null; 3510 } 3511 } 3512 3513 public boolean isCollapsing() { 3514 return mNotificationPanel.isCollapsing(); 3515 } 3516 3517 public void addPostCollapseAction(Runnable r) { 3518 mPostCollapseRunnables.add(r); 3519 } 3520 3521 public boolean isInLaunchTransition() { 3522 return mNotificationPanel.isLaunchTransitionRunning() 3523 || mNotificationPanel.isLaunchTransitionFinished(); 3524 } 3525 3526 /** 3527 * Fades the content of the keyguard away after the launch transition is done. 3528 * 3529 * @param beforeFading the runnable to be run when the circle is fully expanded and the fading 3530 * starts 3531 * @param endRunnable the runnable to be run when the transition is done 3532 */ 3533 public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, 3534 final Runnable endRunnable) { 3535 Runnable hideRunnable = new Runnable() { 3536 @Override 3537 public void run() { 3538 mLaunchTransitionFadingAway = true; 3539 if (beforeFading != null) { 3540 beforeFading.run(); 3541 } 3542 mNotificationPanel.setAlpha(1); 3543 mNotificationPanel.animate() 3544 .alpha(0) 3545 .setStartDelay(FADE_KEYGUARD_START_DELAY) 3546 .setDuration(FADE_KEYGUARD_DURATION) 3547 .withLayer() 3548 .withEndAction(new Runnable() { 3549 @Override 3550 public void run() { 3551 mNotificationPanel.setAlpha(1); 3552 if (endRunnable != null) { 3553 endRunnable.run(); 3554 } 3555 mLaunchTransitionFadingAway = false; 3556 } 3557 }); 3558 } 3559 }; 3560 if (mNotificationPanel.isLaunchTransitionRunning()) { 3561 mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); 3562 } else { 3563 hideRunnable.run(); 3564 } 3565 } 3566 3567 /** 3568 * @return true if we would like to stay in the shade, false if it should go away entirely 3569 */ 3570 public boolean hideKeyguard() { 3571 boolean staying = mLeaveOpenOnKeyguardHide; 3572 setBarState(StatusBarState.SHADE); 3573 if (mLeaveOpenOnKeyguardHide) { 3574 mLeaveOpenOnKeyguardHide = false; 3575 mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay()); 3576 if (mDraggedDownRow != null) { 3577 mDraggedDownRow.setUserLocked(false); 3578 mDraggedDownRow = null; 3579 } 3580 } else { 3581 instantCollapseNotificationPanel(); 3582 } 3583 updateKeyguardState(staying, false /* fromShadeLocked */); 3584 return staying; 3585 } 3586 3587 public long calculateGoingToFullShadeDelay() { 3588 return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration; 3589 } 3590 3591 /** 3592 * Notifies the status bar the Keyguard is fading away with the specified timings. 3593 * 3594 * @param delay the animation delay in miliseconds 3595 * @param fadeoutDuration the duration of the exit animation, in milliseconds 3596 */ 3597 public void setKeyguardFadingAway(long delay, long fadeoutDuration) { 3598 mKeyguardFadingAway = true; 3599 mKeyguardFadingAwayDelay = delay; 3600 mKeyguardFadingAwayDuration = fadeoutDuration; 3601 mWaitingForKeyguardExit = false; 3602 disable(mDisabledUnmodified, true /* animate */); 3603 } 3604 3605 public boolean isKeyguardFadingAway() { 3606 return mKeyguardFadingAway; 3607 } 3608 3609 /** 3610 * Notifies that the Keyguard fading away animation is done. 3611 */ 3612 public void finishKeyguardFadingAway() { 3613 mKeyguardFadingAway = false; 3614 } 3615 3616 private void updatePublicMode() { 3617 setLockscreenPublicMode( 3618 (mStatusBarKeyguardViewManager.isShowing() || 3619 mStatusBarKeyguardViewManager.isOccluded()) 3620 && mStatusBarKeyguardViewManager.isSecure()); 3621 } 3622 3623 private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) { 3624 if (mState == StatusBarState.KEYGUARD) { 3625 mKeyguardIndicationController.setVisible(true); 3626 mNotificationPanel.resetViews(); 3627 mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked); 3628 } else { 3629 mKeyguardIndicationController.setVisible(false); 3630 mKeyguardUserSwitcher.setKeyguard(false, 3631 goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked); 3632 } 3633 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3634 mScrimController.setKeyguardShowing(true); 3635 } else { 3636 mScrimController.setKeyguardShowing(false); 3637 } 3638 mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade); 3639 updateDozingState(); 3640 updatePublicMode(); 3641 updateStackScrollerState(goingToFullShade); 3642 updateNotifications(); 3643 checkBarModes(); 3644 updateCarrierLabelVisibility(false); 3645 updateMediaMetaData(false); 3646 mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), 3647 mStatusBarKeyguardViewManager.isSecure()); 3648 } 3649 3650 private void updateDozingState() { 3651 if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) { 3652 return; 3653 } 3654 mNotificationPanel.setDozing(mDozing); 3655 if (mDozing) { 3656 mKeyguardBottomArea.setVisibility(View.INVISIBLE); 3657 mStackScroller.setDark(true, false /*animate*/); 3658 } else { 3659 mKeyguardBottomArea.setVisibility(View.VISIBLE); 3660 mStackScroller.setDark(false, false /*animate*/); 3661 } 3662 mScrimController.setDozing(mDozing); 3663 } 3664 3665 public void updateStackScrollerState(boolean goingToFullShade) { 3666 if (mStackScroller == null) return; 3667 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 3668 mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade); 3669 mStackScroller.setDimmed(onKeyguard, false /* animate */); 3670 mStackScroller.setExpandingEnabled(!onKeyguard); 3671 ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); 3672 mStackScroller.setActivatedChild(null); 3673 if (activatedChild != null) { 3674 activatedChild.makeInactive(false /* animate */); 3675 } 3676 } 3677 3678 public void userActivity() { 3679 if (mState == StatusBarState.KEYGUARD) { 3680 mKeyguardViewMediatorCallback.userActivity(); 3681 } 3682 } 3683 3684 public boolean interceptMediaKey(KeyEvent event) { 3685 return mState == StatusBarState.KEYGUARD 3686 && mStatusBarKeyguardViewManager.interceptMediaKey(event); 3687 } 3688 3689 public boolean onMenuPressed() { 3690 return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed(); 3691 } 3692 3693 public boolean onBackPressed() { 3694 if (mStatusBarKeyguardViewManager.onBackPressed()) { 3695 return true; 3696 } 3697 if (mNotificationPanel.isQsExpanded()) { 3698 if (mNotificationPanel.isQsDetailShowing()) { 3699 mNotificationPanel.closeQsDetail(); 3700 } else { 3701 mNotificationPanel.animateCloseQs(); 3702 } 3703 return true; 3704 } 3705 if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { 3706 animateCollapsePanels(); 3707 return true; 3708 } 3709 return false; 3710 } 3711 3712 public boolean onSpacePressed() { 3713 if (mScreenOn != null && mScreenOn 3714 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { 3715 animateCollapsePanels(0 /* flags */, true /* force */); 3716 return true; 3717 } 3718 return false; 3719 } 3720 3721 private void showBouncer() { 3722 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3723 mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); 3724 mStatusBarKeyguardViewManager.dismiss(); 3725 } 3726 } 3727 3728 private void instantExpandNotificationsPanel() { 3729 3730 // Make our window larger and the panel expanded. 3731 makeExpandedVisible(true); 3732 mNotificationPanel.instantExpand(); 3733 } 3734 3735 private void instantCollapseNotificationPanel() { 3736 mNotificationPanel.setExpandedFraction(0); 3737 } 3738 3739 @Override 3740 public void onActivated(ActivatableNotificationView view) { 3741 mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); 3742 ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); 3743 if (previousView != null) { 3744 previousView.makeInactive(true /* animate */); 3745 } 3746 mStackScroller.setActivatedChild(view); 3747 } 3748 3749 /** 3750 * @param state The {@link StatusBarState} to set. 3751 */ 3752 public void setBarState(int state) { 3753 mState = state; 3754 mStatusBarWindowManager.setStatusBarState(state); 3755 } 3756 3757 @Override 3758 public void onActivationReset(ActivatableNotificationView view) { 3759 if (view == mStackScroller.getActivatedChild()) { 3760 mKeyguardIndicationController.hideTransientIndication(); 3761 mStackScroller.setActivatedChild(null); 3762 } 3763 } 3764 3765 public void onTrackingStarted() { 3766 runPostCollapseRunnables(); 3767 } 3768 3769 public void onUnlockHintStarted() { 3770 mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); 3771 } 3772 3773 public void onHintFinished() { 3774 // Delay the reset a bit so the user can read the text. 3775 mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); 3776 } 3777 3778 public void onCameraHintStarted() { 3779 mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); 3780 } 3781 3782 public void onPhoneHintStarted() { 3783 mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); 3784 } 3785 3786 public void onTrackingStopped(boolean expand) { 3787 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3788 if (!expand && !mUnlockMethodCache.isMethodInsecure()) { 3789 showBouncer(); 3790 } 3791 } 3792 } 3793 3794 @Override 3795 protected int getMaxKeyguardNotifications() { 3796 return mKeyguardMaxNotificationCount; 3797 } 3798 3799 public NavigationBarView getNavigationBarView() { 3800 return mNavigationBarView; 3801 } 3802 3803 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ 3804 3805 @Override 3806 public boolean onDraggedDown(View startingChild) { 3807 if (hasActiveNotifications()) { 3808 3809 // We have notifications, go to locked shade. 3810 goToLockedShade(startingChild); 3811 return true; 3812 } else { 3813 3814 // No notifications - abort gesture. 3815 return false; 3816 } 3817 } 3818 3819 @Override 3820 public void onDragDownReset() { 3821 mStackScroller.setDimmed(true /* dimmed */, true /* animated */); 3822 } 3823 3824 @Override 3825 public void onThresholdReached() { 3826 mStackScroller.setDimmed(false /* dimmed */, true /* animate */); 3827 } 3828 3829 @Override 3830 public void onTouchSlopExceeded() { 3831 mStackScroller.removeLongPressCallback(); 3832 } 3833 3834 @Override 3835 public void setEmptyDragAmount(float amount) { 3836 mNotificationPanel.setEmptyDragAmount(amount); 3837 } 3838 3839 /** 3840 * If secure with redaction: Show bouncer, go to unlocked shade. 3841 * 3842 * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> 3843 * 3844 * @param expandView The view to expand after going to the shade. 3845 */ 3846 public void goToLockedShade(View expandView) { 3847 ExpandableNotificationRow row = null; 3848 if (expandView instanceof ExpandableNotificationRow) { 3849 row = (ExpandableNotificationRow) expandView; 3850 row.setUserExpanded(true); 3851 } 3852 boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId) 3853 || !mShowLockscreenNotifications; 3854 if (isLockscreenPublicMode() && fullShadeNeedsBouncer) { 3855 mLeaveOpenOnKeyguardHide = true; 3856 showBouncer(); 3857 mDraggedDownRow = row; 3858 } else { 3859 mNotificationPanel.animateToFullShade(0 /* delay */); 3860 setBarState(StatusBarState.SHADE_LOCKED); 3861 updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); 3862 if (row != null) { 3863 row.setUserLocked(false); 3864 } 3865 } 3866 } 3867 3868 /** 3869 * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. 3870 */ 3871 public void goToKeyguard() { 3872 if (mState == StatusBarState.SHADE_LOCKED) { 3873 mStackScroller.onGoToKeyguard(); 3874 setBarState(StatusBarState.KEYGUARD); 3875 updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/); 3876 } 3877 } 3878 3879 /** 3880 * @return a ViewGroup that spans the entire panel which contains the quick settings 3881 */ 3882 public ViewGroup getQuickSettingsOverlayParent() { 3883 return mNotificationPanel; 3884 } 3885 3886 public long getKeyguardFadingAwayDelay() { 3887 return mKeyguardFadingAwayDelay; 3888 } 3889 3890 public long getKeyguardFadingAwayDuration() { 3891 return mKeyguardFadingAwayDuration; 3892 } 3893 3894 public LinearLayout getSystemIcons() { 3895 return mSystemIcons; 3896 } 3897 3898 public LinearLayout getSystemIconArea() { 3899 return mSystemIconArea; 3900 } 3901 3902 @Override 3903 public void setBouncerShowing(boolean bouncerShowing) { 3904 super.setBouncerShowing(bouncerShowing); 3905 disable(mDisabledUnmodified, true /* animate */); 3906 } 3907 3908 public void onScreenTurnedOff() { 3909 mScreenOnFromKeyguard = false; 3910 mScreenOnComingFromTouch = false; 3911 mStackScroller.setAnimationsEnabled(false); 3912 } 3913 3914 public void onScreenTurnedOn() { 3915 mScreenOnFromKeyguard = true; 3916 mStackScroller.setAnimationsEnabled(true); 3917 mNotificationPanel.onScreenTurnedOn(); 3918 mNotificationPanel.setTouchDisabled(false); 3919 } 3920 3921 /** 3922 * This handles long-press of both back and recents. They are 3923 * handled together to capture them both being long-pressed 3924 * at the same time to exit screen pinning (lock task). 3925 * 3926 * When accessibility mode is on, only a long-press from recents 3927 * is required to exit. 3928 * 3929 * In all other circumstances we try to pass through long-press events 3930 * for Back, so that apps can still use it. Which can be from two things. 3931 * 1) Not currently in screen pinning (lock task). 3932 * 2) Back is long-pressed without recents. 3933 */ 3934 private void handleLongPressBackRecents(View v) { 3935 try { 3936 boolean sendBackLongPress = false; 3937 IActivityManager activityManager = ActivityManagerNative.getDefault(); 3938 boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled(); 3939 if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) { 3940 long time = System.currentTimeMillis(); 3941 // If we recently long-pressed the other button then they were 3942 // long-pressed 'together' 3943 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { 3944 activityManager.stopLockTaskModeOnCurrent(); 3945 } else if ((v.getId() == R.id.back) 3946 && !mNavigationBarView.getRecentsButton().isPressed()) { 3947 // If we aren't pressing recents right now then they presses 3948 // won't be together, so send the standard long-press action. 3949 sendBackLongPress = true; 3950 } 3951 mLastLockToAppLongPress = time; 3952 } else { 3953 // If this is back still need to handle sending the long-press event. 3954 if (v.getId() == R.id.back) { 3955 sendBackLongPress = true; 3956 } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) { 3957 // When in accessibility mode a long press that is recents (not back) 3958 // should stop lock task. 3959 activityManager.stopLockTaskModeOnCurrent(); 3960 } 3961 } 3962 if (sendBackLongPress) { 3963 KeyButtonView keyButtonView = (KeyButtonView) v; 3964 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 3965 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 3966 } 3967 } catch (RemoteException e) { 3968 Log.d(TAG, "Unable to reach activity manager", e); 3969 } 3970 } 3971 3972 // Recents 3973 3974 @Override 3975 protected void showRecents(boolean triggeredFromAltTab) { 3976 // Set the recents visibility flag 3977 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 3978 notifyUiVisibilityChanged(mSystemUiVisibility); 3979 super.showRecents(triggeredFromAltTab); 3980 } 3981 3982 @Override 3983 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 3984 // Unset the recents visibility flag 3985 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; 3986 notifyUiVisibilityChanged(mSystemUiVisibility); 3987 super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 3988 } 3989 3990 @Override 3991 protected void toggleRecents() { 3992 // Toggle the recents visibility flag 3993 mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE; 3994 notifyUiVisibilityChanged(mSystemUiVisibility); 3995 super.toggleRecents(); 3996 } 3997 3998 @Override 3999 public void onVisibilityChanged(boolean visible) { 4000 // Update the recents visibility flag 4001 if (visible) { 4002 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 4003 } else { 4004 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; 4005 } 4006 notifyUiVisibilityChanged(mSystemUiVisibility); 4007 } 4008 4009 public boolean hasActiveNotifications() { 4010 return !mNotificationData.getActiveNotifications().isEmpty(); 4011 } 4012 4013 public void wakeUpIfDozing(long time, boolean fromTouch) { 4014 if (mDozing && mScrimController.isPulsing()) { 4015 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 4016 pm.wakeUp(time); 4017 if (fromTouch) { 4018 mScreenOnComingFromTouch = true; 4019 } 4020 } 4021 } 4022 4023 private final class ShadeUpdates { 4024 private final ArraySet<String> mVisibleNotifications = new ArraySet<String>(); 4025 private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>(); 4026 4027 public void check() { 4028 mNewVisibleNotifications.clear(); 4029 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 4030 for (int i = 0; i < activeNotifications.size(); i++) { 4031 final Entry entry = activeNotifications.get(i); 4032 final boolean visible = entry.row != null 4033 && entry.row.getVisibility() == View.VISIBLE; 4034 if (visible) { 4035 mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime()); 4036 } 4037 } 4038 final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications); 4039 mVisibleNotifications.clear(); 4040 mVisibleNotifications.addAll(mNewVisibleNotifications); 4041 4042 // We have new notifications 4043 if (updates && mDozeServiceHost != null) { 4044 mDozeServiceHost.fireNewNotifications(); 4045 } 4046 } 4047 } 4048 4049 private final class DozeServiceHost implements DozeHost { 4050 // Amount of time to allow to update the time shown on the screen before releasing 4051 // the wakelock. This timeout is design to compensate for the fact that we don't 4052 // currently have a way to know when time display contents have actually been 4053 // refreshed once we've finished rendering a new frame. 4054 private static final long PROCESSING_TIME = 500; 4055 4056 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 4057 private final H mHandler = new H(); 4058 4059 @Override 4060 public String toString() { 4061 return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; 4062 } 4063 4064 public void firePowerSaveChanged(boolean active) { 4065 for (Callback callback : mCallbacks) { 4066 callback.onPowerSaveChanged(active); 4067 } 4068 } 4069 4070 public void fireBuzzBeepBlinked() { 4071 for (Callback callback : mCallbacks) { 4072 callback.onBuzzBeepBlinked(); 4073 } 4074 } 4075 4076 public void fireNotificationLight(boolean on) { 4077 for (Callback callback : mCallbacks) { 4078 callback.onNotificationLight(on); 4079 } 4080 } 4081 4082 public void fireNewNotifications() { 4083 for (Callback callback : mCallbacks) { 4084 callback.onNewNotifications(); 4085 } 4086 } 4087 4088 @Override 4089 public void addCallback(@NonNull Callback callback) { 4090 mCallbacks.add(callback); 4091 } 4092 4093 @Override 4094 public void removeCallback(@NonNull Callback callback) { 4095 mCallbacks.remove(callback); 4096 } 4097 4098 @Override 4099 public void startDozing(@NonNull Runnable ready) { 4100 mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget(); 4101 } 4102 4103 @Override 4104 public void pulseWhileDozing(@NonNull PulseCallback callback) { 4105 mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, callback).sendToTarget(); 4106 } 4107 4108 @Override 4109 public void stopDozing() { 4110 mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget(); 4111 } 4112 4113 @Override 4114 public boolean isPowerSaveActive() { 4115 return mBatteryController != null && mBatteryController.isPowerSave(); 4116 } 4117 4118 private void handleStartDozing(@NonNull Runnable ready) { 4119 if (!mDozing) { 4120 mDozing = true; 4121 DozeLog.traceDozing(mContext, mDozing); 4122 updateDozingState(); 4123 } 4124 ready.run(); 4125 } 4126 4127 private void handlePulseWhileDozing(@NonNull PulseCallback callback) { 4128 mScrimController.pulse(callback); 4129 } 4130 4131 private void handleStopDozing() { 4132 if (mDozing) { 4133 mDozing = false; 4134 DozeLog.traceDozing(mContext, mDozing); 4135 updateDozingState(); 4136 } 4137 } 4138 4139 private final class H extends Handler { 4140 private static final int MSG_START_DOZING = 1; 4141 private static final int MSG_PULSE_WHILE_DOZING = 2; 4142 private static final int MSG_STOP_DOZING = 3; 4143 4144 @Override 4145 public void handleMessage(Message msg) { 4146 switch (msg.what) { 4147 case MSG_START_DOZING: 4148 handleStartDozing((Runnable) msg.obj); 4149 break; 4150 case MSG_PULSE_WHILE_DOZING: 4151 handlePulseWhileDozing((PulseCallback) msg.obj); 4152 break; 4153 case MSG_STOP_DOZING: 4154 handleStopDozing(); 4155 break; 4156 } 4157 } 4158 } 4159 } 4160} 4161