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