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