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