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