1 package com.android.systemui.statusbar.phone; 2 3 import android.app.NotificationManager; 4 import android.content.Context; 5 import android.content.res.Resources; 6 import android.graphics.Color; 7 import android.graphics.Rect; 8 import android.os.Bundle; 9 import android.os.Trace; 10 import android.view.LayoutInflater; 11 import android.view.View; 12 import android.widget.FrameLayout; 13 14 import androidx.annotation.NonNull; 15 import androidx.annotation.VisibleForTesting; 16 import androidx.collection.ArrayMap; 17 18 import com.android.internal.statusbar.StatusBarIcon; 19 import com.android.internal.util.ContrastColorUtil; 20 import com.android.settingslib.Utils; 21 import com.android.systemui.R; 22 import com.android.systemui.animation.Interpolators; 23 import com.android.systemui.dagger.SysUISingleton; 24 import com.android.systemui.demomode.DemoMode; 25 import com.android.systemui.demomode.DemoModeController; 26 import com.android.systemui.plugins.DarkIconDispatcher; 27 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; 28 import com.android.systemui.plugins.statusbar.StatusBarStateController; 29 import com.android.systemui.statusbar.CrossFadeHelper; 30 import com.android.systemui.statusbar.NotificationListener; 31 import com.android.systemui.statusbar.NotificationMediaManager; 32 import com.android.systemui.statusbar.NotificationShelfController; 33 import com.android.systemui.statusbar.StatusBarIconView; 34 import com.android.systemui.statusbar.StatusBarState; 35 import com.android.systemui.statusbar.notification.AnimatableProperty; 36 import com.android.systemui.statusbar.notification.NotificationUtils; 37 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 38 import com.android.systemui.statusbar.notification.PropertyAnimator; 39 import com.android.systemui.statusbar.notification.collection.ListEntry; 40 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 41 import com.android.systemui.statusbar.notification.stack.AnimationProperties; 42 import com.android.systemui.statusbar.window.StatusBarWindowController; 43 import com.android.wm.shell.bubbles.Bubbles; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.Objects; 48 import java.util.Optional; 49 import java.util.function.Function; 50 51 import javax.inject.Inject; 52 53 /** 54 * A controller for the space in the status bar to the left of the system icons. This area is 55 * normally reserved for notifications. 56 */ 57 @SysUISingleton 58 public class NotificationIconAreaController implements 59 DarkReceiver, 60 StatusBarStateController.StateListener, 61 NotificationWakeUpCoordinator.WakeUpListener, 62 DemoMode { 63 64 public static final String HIGH_PRIORITY = "high_priority"; 65 private static final long AOD_ICONS_APPEAR_DURATION = 200; 66 67 private final ContrastColorUtil mContrastColorUtil; 68 private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons; 69 private final StatusBarStateController mStatusBarStateController; 70 private final NotificationMediaManager mMediaManager; 71 private final NotificationWakeUpCoordinator mWakeUpCoordinator; 72 private final KeyguardBypassController mBypassController; 73 private final DozeParameters mDozeParameters; 74 private final Optional<Bubbles> mBubblesOptional; 75 private final StatusBarWindowController mStatusBarWindowController; 76 private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 77 78 private int mIconSize; 79 private int mIconHPadding; 80 private int mIconTint = Color.WHITE; 81 private int mCenteredIconTint = Color.WHITE; 82 83 private List<ListEntry> mNotificationEntries = List.of(); 84 protected View mNotificationIconArea; 85 private NotificationIconContainer mNotificationIcons; 86 private NotificationIconContainer mShelfIcons; 87 protected View mCenteredIconArea; 88 private NotificationIconContainer mCenteredIcon; 89 private NotificationIconContainer mAodIcons; 90 private StatusBarIconView mCenteredIconView; 91 private final Rect mTintArea = new Rect(); 92 private Context mContext; 93 94 private final DemoModeController mDemoModeController; 95 96 private int mAodIconAppearTranslation; 97 98 private boolean mAnimationsEnabled; 99 private int mAodIconTint; 100 private boolean mAodIconsVisible; 101 private boolean mShowLowPriority = true; 102 103 @VisibleForTesting 104 final NotificationListener.NotificationSettingsListener mSettingsListener = 105 new NotificationListener.NotificationSettingsListener() { 106 @Override 107 public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { 108 mShowLowPriority = !hideSilentStatusIcons; 109 updateStatusBarIcons(); 110 } 111 }; 112 113 @Inject NotificationIconAreaController( Context context, StatusBarStateController statusBarStateController, NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardBypassController keyguardBypassController, NotificationMediaManager notificationMediaManager, NotificationListener notificationListener, DozeParameters dozeParameters, Optional<Bubbles> bubblesOptional, DemoModeController demoModeController, DarkIconDispatcher darkIconDispatcher, StatusBarWindowController statusBarWindowController, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController)114 public NotificationIconAreaController( 115 Context context, 116 StatusBarStateController statusBarStateController, 117 NotificationWakeUpCoordinator wakeUpCoordinator, 118 KeyguardBypassController keyguardBypassController, 119 NotificationMediaManager notificationMediaManager, 120 NotificationListener notificationListener, 121 DozeParameters dozeParameters, 122 Optional<Bubbles> bubblesOptional, 123 DemoModeController demoModeController, 124 DarkIconDispatcher darkIconDispatcher, 125 StatusBarWindowController statusBarWindowController, 126 UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { 127 mContrastColorUtil = ContrastColorUtil.getInstance(context); 128 mContext = context; 129 mStatusBarStateController = statusBarStateController; 130 mStatusBarStateController.addCallback(this); 131 mMediaManager = notificationMediaManager; 132 mDozeParameters = dozeParameters; 133 mWakeUpCoordinator = wakeUpCoordinator; 134 wakeUpCoordinator.addListener(this); 135 mBypassController = keyguardBypassController; 136 mBubblesOptional = bubblesOptional; 137 mDemoModeController = demoModeController; 138 mDemoModeController.addCallback(this); 139 mStatusBarWindowController = statusBarWindowController; 140 mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; 141 notificationListener.addNotificationSettingsListener(mSettingsListener); 142 143 initializeNotificationAreaViews(context); 144 reloadAodColor(); 145 darkIconDispatcher.addDarkReceiver(this); 146 } 147 inflateIconArea(LayoutInflater inflater)148 protected View inflateIconArea(LayoutInflater inflater) { 149 return inflater.inflate(R.layout.notification_icon_area, null); 150 } 151 152 /** 153 * Initializes the views that will represent the notification area. 154 */ initializeNotificationAreaViews(Context context)155 protected void initializeNotificationAreaViews(Context context) { 156 reloadDimens(context); 157 158 LayoutInflater layoutInflater = LayoutInflater.from(context); 159 mNotificationIconArea = inflateIconArea(layoutInflater); 160 mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons); 161 162 mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null); 163 mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon); 164 } 165 166 /** 167 * Called by the Keyguard*ViewController whose view contains the aod icons. 168 */ setupAodIcons(@onNull NotificationIconContainer aodIcons)169 public void setupAodIcons(@NonNull NotificationIconContainer aodIcons) { 170 boolean changed = mAodIcons != null && aodIcons != mAodIcons; 171 if (changed) { 172 mAodIcons.setAnimationsEnabled(false); 173 mAodIcons.removeAllViews(); 174 } 175 mAodIcons = aodIcons; 176 mAodIcons.setOnLockScreen(true); 177 updateAodIconsVisibility(false /* animate */, changed); 178 updateAnimations(); 179 if (changed) { 180 updateAodNotificationIcons(); 181 } 182 updateIconLayoutParams(mContext); 183 } 184 185 /** 186 * Update position of the view, with optional animation 187 */ updatePosition(int x, AnimationProperties props, boolean animate)188 public void updatePosition(int x, AnimationProperties props, boolean animate) { 189 if (mAodIcons != null) { 190 PropertyAnimator.setProperty(mAodIcons, AnimatableProperty.TRANSLATION_X, x, props, 191 animate); 192 } 193 } 194 setupShelf(NotificationShelfController notificationShelfController)195 public void setupShelf(NotificationShelfController notificationShelfController) { 196 mShelfIcons = notificationShelfController.getShelfIcons(); 197 notificationShelfController.setCollapsedIcons(mNotificationIcons); 198 } 199 onDensityOrFontScaleChanged(Context context)200 public void onDensityOrFontScaleChanged(Context context) { 201 updateIconLayoutParams(context); 202 } 203 updateIconLayoutParams(Context context)204 private void updateIconLayoutParams(Context context) { 205 reloadDimens(context); 206 final FrameLayout.LayoutParams params = generateIconLayoutParams(); 207 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) { 208 View child = mNotificationIcons.getChildAt(i); 209 child.setLayoutParams(params); 210 } 211 for (int i = 0; i < mCenteredIcon.getChildCount(); i++) { 212 View child = mCenteredIcon.getChildAt(i); 213 child.setLayoutParams(params); 214 } 215 if (mShelfIcons != null) { 216 for (int i = 0; i < mShelfIcons.getChildCount(); i++) { 217 View child = mShelfIcons.getChildAt(i); 218 child.setLayoutParams(params); 219 } 220 } 221 if (mAodIcons != null) { 222 for (int i = 0; i < mAodIcons.getChildCount(); i++) { 223 View child = mAodIcons.getChildAt(i); 224 child.setLayoutParams(params); 225 } 226 } 227 } 228 229 @NonNull generateIconLayoutParams()230 private FrameLayout.LayoutParams generateIconLayoutParams() { 231 return new FrameLayout.LayoutParams( 232 mIconSize + 2 * mIconHPadding, mStatusBarWindowController.getStatusBarHeight()); 233 } 234 reloadDimens(Context context)235 private void reloadDimens(Context context) { 236 Resources res = context.getResources(); 237 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); 238 mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding); 239 mAodIconAppearTranslation = res.getDimensionPixelSize( 240 R.dimen.shelf_appear_translation); 241 } 242 243 /** 244 * Returns the view that represents the notification area. 245 */ getNotificationInnerAreaView()246 public View getNotificationInnerAreaView() { 247 return mNotificationIconArea; 248 } 249 250 /** 251 * Returns the view that represents the centered notification area. 252 */ getCenteredNotificationAreaView()253 public View getCenteredNotificationAreaView() { 254 return mCenteredIconArea; 255 } 256 257 /** 258 * See {@link com.android.systemui.statusbar.policy.DarkIconDispatcher#setIconsDarkArea}. 259 * Sets the color that should be used to tint any icons in the notification area. 260 * 261 * @param tintArea the area in which to tint the icons, specified in screen coordinates 262 * @param darkIntensity 263 */ onDarkChanged(Rect tintArea, float darkIntensity, int iconTint)264 public void onDarkChanged(Rect tintArea, float darkIntensity, int iconTint) { 265 if (tintArea == null) { 266 mTintArea.setEmpty(); 267 } else { 268 mTintArea.set(tintArea); 269 } 270 271 if (DarkIconDispatcher.isInArea(tintArea, mNotificationIconArea)) { 272 mIconTint = iconTint; 273 } 274 275 if (DarkIconDispatcher.isInArea(tintArea, mCenteredIconArea)) { 276 mCenteredIconTint = iconTint; 277 } 278 279 applyNotificationIconsTint(); 280 } 281 shouldShowNotificationIcon(NotificationEntry entry, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon)282 protected boolean shouldShowNotificationIcon(NotificationEntry entry, 283 boolean showAmbient, boolean showLowPriority, boolean hideDismissed, 284 boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, 285 boolean hidePulsing, boolean onlyShowCenteredIcon) { 286 287 final boolean isCenteredNotificationIcon = mCenteredIconView != null 288 && entry.getIcons().getCenteredIcon() != null 289 && Objects.equals(entry.getIcons().getCenteredIcon(), mCenteredIconView); 290 if (onlyShowCenteredIcon) { 291 return isCenteredNotificationIcon; 292 } 293 if (hideCenteredIcon && isCenteredNotificationIcon && !entry.isRowHeadsUp()) { 294 return false; 295 } 296 if (entry.getRanking().isAmbient() && !showAmbient) { 297 return false; 298 } 299 if (hideCurrentMedia && entry.getKey().equals(mMediaManager.getMediaNotificationKey())) { 300 return false; 301 } 302 if (!showLowPriority && entry.getImportance() < NotificationManager.IMPORTANCE_DEFAULT) { 303 return false; 304 } 305 if (!entry.isTopLevelChild()) { 306 return false; 307 } 308 if (entry.getRow().getVisibility() == View.GONE) { 309 return false; 310 } 311 if (entry.isRowDismissed() && hideDismissed) { 312 return false; 313 } 314 if (hideRepliedMessages && entry.isLastMessageFromReply()) { 315 return false; 316 } 317 // showAmbient == show in shade but not shelf 318 if (!showAmbient && entry.shouldSuppressStatusBar()) { 319 return false; 320 } 321 if (hidePulsing && entry.showingPulsing() 322 && (!mWakeUpCoordinator.getNotificationsFullyHidden() 323 || !entry.isPulseSuppressed())) { 324 return false; 325 } 326 if (mBubblesOptional.isPresent() 327 && mBubblesOptional.get().isBubbleExpanded(entry.getKey())) { 328 return false; 329 } 330 return true; 331 } 332 /** 333 * Updates the notifications with the given list of notifications to display. 334 */ updateNotificationIcons(List<ListEntry> entries)335 public void updateNotificationIcons(List<ListEntry> entries) { 336 mNotificationEntries = entries; 337 updateNotificationIcons(); 338 } 339 updateNotificationIcons()340 private void updateNotificationIcons() { 341 Trace.beginSection("NotificationIconAreaController.updateNotificationIcons"); 342 updateStatusBarIcons(); 343 updateShelfIcons(); 344 updateCenterIcon(); 345 updateAodNotificationIcons(); 346 347 applyNotificationIconsTint(); 348 Trace.endSection(); 349 } 350 updateShelfIcons()351 private void updateShelfIcons() { 352 updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons, 353 true /* showAmbient */, 354 true /* showLowPriority */, 355 false /* hideDismissed */, 356 false /* hideRepliedMessages */, 357 false /* hideCurrentMedia */, 358 false /* hide centered icon */, 359 false /* hidePulsing */, 360 false /* onlyShowCenteredIcon */); 361 } 362 updateStatusBarIcons()363 public void updateStatusBarIcons() { 364 updateIconsForLayout(entry -> entry.getIcons().getStatusBarIcon(), mNotificationIcons, 365 false /* showAmbient */, 366 mShowLowPriority, 367 true /* hideDismissed */, 368 true /* hideRepliedMessages */, 369 false /* hideCurrentMedia */, 370 true /* hide centered icon */, 371 false /* hidePulsing */, 372 false /* onlyShowCenteredIcon */); 373 } 374 updateCenterIcon()375 private void updateCenterIcon() { 376 updateIconsForLayout(entry -> entry.getIcons().getCenteredIcon(), mCenteredIcon, 377 false /* showAmbient */, 378 true /* showLowPriority */, 379 false /* hideDismissed */, 380 false /* hideRepliedMessages */, 381 false /* hideCurrentMedia */, 382 false /* hide centered icon */, 383 false /* hidePulsing */, 384 true/* onlyShowCenteredIcon */); 385 } 386 updateAodNotificationIcons()387 public void updateAodNotificationIcons() { 388 if (mAodIcons == null) { 389 return; 390 } 391 updateIconsForLayout(entry -> entry.getIcons().getAodIcon(), mAodIcons, 392 false /* showAmbient */, 393 true /* showLowPriority */, 394 true /* hideDismissed */, 395 true /* hideRepliedMessages */, 396 true /* hideCurrentMedia */, 397 true /* hide centered icon */, 398 mBypassController.getBypassEnabled() /* hidePulsing */, 399 false /* onlyShowCenteredIcon */); 400 } 401 402 @VisibleForTesting shouldShouldLowPriorityIcons()403 boolean shouldShouldLowPriorityIcons() { 404 return mShowLowPriority; 405 } 406 407 /** 408 * Updates the notification icons for a host layout. This will ensure that the notification 409 * host layout will have the same icons like the ones in here. 410 * @param function A function to look up an icon view based on an entry 411 * @param hostLayout which layout should be updated 412 * @param showAmbient should ambient notification icons be shown 413 * @param showLowPriority should icons from silent notifications be shown 414 * @param hideDismissed should dismissed icons be hidden 415 * @param hideRepliedMessages should messages that have been replied to be hidden 416 * @param hidePulsing should pulsing notifications be hidden 417 */ updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function, NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon)418 private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function, 419 NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, 420 boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, 421 boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) { 422 ArrayList<StatusBarIconView> toShow = new ArrayList<>(mNotificationEntries.size()); 423 // Filter out ambient notifications and notification children. 424 for (int i = 0; i < mNotificationEntries.size(); i++) { 425 NotificationEntry entry = mNotificationEntries.get(i).getRepresentativeEntry(); 426 if (entry != null && entry.getRow() != null) { 427 if (shouldShowNotificationIcon(entry, showAmbient, showLowPriority, hideDismissed, 428 hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing, 429 onlyShowCenteredIcon)) { 430 StatusBarIconView iconView = function.apply(entry); 431 if (iconView != null) { 432 toShow.add(iconView); 433 } 434 } 435 } 436 } 437 438 // In case we are changing the suppression of a group, the replacement shouldn't flicker 439 // and it should just be replaced instead. We therefore look for notifications that were 440 // just replaced by the child or vice-versa to suppress this. 441 442 ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>(); 443 ArrayList<View> toRemove = new ArrayList<>(); 444 for (int i = 0; i < hostLayout.getChildCount(); i++) { 445 View child = hostLayout.getChildAt(i); 446 if (!(child instanceof StatusBarIconView)) { 447 continue; 448 } 449 if (!toShow.contains(child)) { 450 boolean iconWasReplaced = false; 451 StatusBarIconView removedIcon = (StatusBarIconView) child; 452 String removedGroupKey = removedIcon.getNotification().getGroupKey(); 453 for (int j = 0; j < toShow.size(); j++) { 454 StatusBarIconView candidate = toShow.get(j); 455 if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon())) 456 && candidate.getNotification().getGroupKey().equals(removedGroupKey)) { 457 if (!iconWasReplaced) { 458 iconWasReplaced = true; 459 } else { 460 iconWasReplaced = false; 461 break; 462 } 463 } 464 } 465 if (iconWasReplaced) { 466 ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey); 467 if (statusBarIcons == null) { 468 statusBarIcons = new ArrayList<>(); 469 replacingIcons.put(removedGroupKey, statusBarIcons); 470 } 471 statusBarIcons.add(removedIcon.getStatusBarIcon()); 472 } 473 toRemove.add(removedIcon); 474 } 475 } 476 // removing all duplicates 477 ArrayList<String> duplicates = new ArrayList<>(); 478 for (String key : replacingIcons.keySet()) { 479 ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key); 480 if (statusBarIcons.size() != 1) { 481 duplicates.add(key); 482 } 483 } 484 replacingIcons.removeAll(duplicates); 485 hostLayout.setReplacingIcons(replacingIcons); 486 487 final int toRemoveCount = toRemove.size(); 488 for (int i = 0; i < toRemoveCount; i++) { 489 hostLayout.removeView(toRemove.get(i)); 490 } 491 492 final FrameLayout.LayoutParams params = generateIconLayoutParams(); 493 for (int i = 0; i < toShow.size(); i++) { 494 StatusBarIconView v = toShow.get(i); 495 // The view might still be transiently added if it was just removed and added again 496 hostLayout.removeTransientView(v); 497 if (v.getParent() == null) { 498 if (hideDismissed) { 499 v.setOnDismissListener(mUpdateStatusBarIcons); 500 } 501 hostLayout.addView(v, i, params); 502 } 503 } 504 505 hostLayout.setChangingViewPositions(true); 506 // Re-sort notification icons 507 final int childCount = hostLayout.getChildCount(); 508 for (int i = 0; i < childCount; i++) { 509 View actual = hostLayout.getChildAt(i); 510 StatusBarIconView expected = toShow.get(i); 511 if (actual == expected) { 512 continue; 513 } 514 hostLayout.removeView(expected); 515 hostLayout.addView(expected, i); 516 } 517 hostLayout.setChangingViewPositions(false); 518 hostLayout.setReplacingIcons(null); 519 } 520 521 /** 522 * Applies {@link #mIconTint} to the notification icons. 523 * Applies {@link #mCenteredIconTint} to the center notification icon. 524 */ applyNotificationIconsTint()525 private void applyNotificationIconsTint() { 526 for (int i = 0; i < mNotificationIcons.getChildCount(); i++) { 527 final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i); 528 if (iv.getWidth() != 0) { 529 updateTintForIcon(iv, mIconTint); 530 } else { 531 iv.executeOnLayout(() -> updateTintForIcon(iv, mIconTint)); 532 } 533 } 534 535 for (int i = 0; i < mCenteredIcon.getChildCount(); i++) { 536 final StatusBarIconView iv = (StatusBarIconView) mCenteredIcon.getChildAt(i); 537 if (iv.getWidth() != 0) { 538 updateTintForIcon(iv, mCenteredIconTint); 539 } else { 540 iv.executeOnLayout(() -> updateTintForIcon(iv, mCenteredIconTint)); 541 } 542 } 543 544 updateAodIconColors(); 545 } 546 updateTintForIcon(StatusBarIconView v, int tint)547 private void updateTintForIcon(StatusBarIconView v, int tint) { 548 boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L)); 549 int color = StatusBarIconView.NO_COLOR; 550 boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mContrastColorUtil); 551 if (colorize) { 552 color = DarkIconDispatcher.getTint(mTintArea, v, tint); 553 } 554 v.setStaticDrawableColor(color); 555 v.setDecorColor(tint); 556 } 557 558 /** 559 * Shows the icon view given in the center. 560 */ showIconCentered(NotificationEntry entry)561 public void showIconCentered(NotificationEntry entry) { 562 StatusBarIconView icon = entry == null ? null : entry.getIcons().getCenteredIcon(); 563 if (!Objects.equals(mCenteredIconView, icon)) { 564 mCenteredIconView = icon; 565 updateNotificationIcons(); 566 } 567 } 568 showIconIsolated(StatusBarIconView icon, boolean animated)569 public void showIconIsolated(StatusBarIconView icon, boolean animated) { 570 mNotificationIcons.showIconIsolated(icon, animated); 571 } 572 setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate)573 public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) { 574 mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate); 575 } 576 577 @Override onDozingChanged(boolean isDozing)578 public void onDozingChanged(boolean isDozing) { 579 if (mAodIcons == null) { 580 return; 581 } 582 boolean animate = mDozeParameters.getAlwaysOn() 583 && !mDozeParameters.getDisplayNeedsBlanking(); 584 mAodIcons.setDozing(isDozing, animate, 0); 585 } 586 setAnimationsEnabled(boolean enabled)587 public void setAnimationsEnabled(boolean enabled) { 588 mAnimationsEnabled = enabled; 589 updateAnimations(); 590 } 591 592 @Override onStateChanged(int newState)593 public void onStateChanged(int newState) { 594 updateAodIconsVisibility(false /* animate */, false /* force */); 595 updateAnimations(); 596 } 597 updateAnimations()598 private void updateAnimations() { 599 boolean inShade = mStatusBarStateController.getState() == StatusBarState.SHADE; 600 if (mAodIcons != null) { 601 mAodIcons.setAnimationsEnabled(mAnimationsEnabled && !inShade); 602 } 603 mCenteredIcon.setAnimationsEnabled(mAnimationsEnabled && inShade); 604 mNotificationIcons.setAnimationsEnabled(mAnimationsEnabled && inShade); 605 } 606 onThemeChanged()607 public void onThemeChanged() { 608 reloadAodColor(); 609 updateAodIconColors(); 610 } 611 getHeight()612 public int getHeight() { 613 return mAodIcons == null ? 0 : mAodIcons.getHeight(); 614 } 615 appearAodIcons()616 public void appearAodIcons() { 617 if (mAodIcons == null) { 618 return; 619 } 620 if (mDozeParameters.shouldControlScreenOff()) { 621 mAodIcons.setTranslationY(-mAodIconAppearTranslation); 622 mAodIcons.setAlpha(0); 623 animateInAodIconTranslation(); 624 mAodIcons.animate() 625 .alpha(1) 626 .setInterpolator(Interpolators.LINEAR) 627 .setDuration(AOD_ICONS_APPEAR_DURATION) 628 .start(); 629 } else { 630 mAodIcons.setAlpha(1.0f); 631 mAodIcons.setTranslationY(0); 632 } 633 } 634 animateInAodIconTranslation()635 private void animateInAodIconTranslation() { 636 mAodIcons.animate() 637 .setInterpolator(Interpolators.DECELERATE_QUINT) 638 .translationY(0) 639 .setDuration(AOD_ICONS_APPEAR_DURATION) 640 .start(); 641 } 642 reloadAodColor()643 private void reloadAodColor() { 644 mAodIconTint = Utils.getColorAttrDefaultColor(mContext, 645 R.attr.wallpaperTextColor); 646 } 647 updateAodIconColors()648 private void updateAodIconColors() { 649 if (mAodIcons != null) { 650 for (int i = 0; i < mAodIcons.getChildCount(); i++) { 651 final StatusBarIconView iv = (StatusBarIconView) mAodIcons.getChildAt(i); 652 if (iv.getWidth() != 0) { 653 updateTintForIcon(iv, mAodIconTint); 654 } else { 655 iv.executeOnLayout(() -> updateTintForIcon(iv, mAodIconTint)); 656 } 657 } 658 } 659 } 660 661 @Override onFullyHiddenChanged(boolean fullyHidden)662 public void onFullyHiddenChanged(boolean fullyHidden) { 663 boolean animate = true; 664 if (!mBypassController.getBypassEnabled()) { 665 animate = mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking(); 666 // We only want the appear animations to happen when the notifications get fully hidden, 667 // since otherwise the unhide animation overlaps 668 animate &= fullyHidden; 669 } 670 updateAodIconsVisibility(animate, false /* force */); 671 updateAodNotificationIcons(); 672 } 673 674 @Override onPulseExpansionChanged(boolean expandingChanged)675 public void onPulseExpansionChanged(boolean expandingChanged) { 676 if (expandingChanged) { 677 updateAodIconsVisibility(true /* animate */, false /* force */); 678 } 679 } 680 updateAodIconsVisibility(boolean animate, boolean forceUpdate)681 private void updateAodIconsVisibility(boolean animate, boolean forceUpdate) { 682 if (mAodIcons == null) { 683 return; 684 } 685 boolean visible = mBypassController.getBypassEnabled() 686 || mWakeUpCoordinator.getNotificationsFullyHidden(); 687 688 // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is 689 // playing, in which case we want them to be visible since we're animating in the AOD UI and 690 // will be switching to KEYGUARD shortly. 691 if (mStatusBarStateController.getState() != StatusBarState.KEYGUARD 692 && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) { 693 visible = false; 694 } 695 if (visible && mWakeUpCoordinator.isPulseExpanding() 696 && !mBypassController.getBypassEnabled()) { 697 visible = false; 698 } 699 if (mAodIconsVisible != visible || forceUpdate) { 700 mAodIconsVisible = visible; 701 mAodIcons.animate().cancel(); 702 if (animate) { 703 boolean wasFullyInvisible = mAodIcons.getVisibility() != View.VISIBLE; 704 if (mAodIconsVisible) { 705 if (wasFullyInvisible) { 706 // No fading here, let's just appear the icons instead! 707 mAodIcons.setVisibility(View.VISIBLE); 708 mAodIcons.setAlpha(1.0f); 709 appearAodIcons(); 710 } else { 711 // Let's make sure the icon are translated to 0, since we cancelled it above 712 animateInAodIconTranslation(); 713 // We were fading out, let's fade in instead 714 CrossFadeHelper.fadeIn(mAodIcons); 715 } 716 } else { 717 // Let's make sure the icon are translated to 0, since we cancelled it above 718 animateInAodIconTranslation(); 719 CrossFadeHelper.fadeOut(mAodIcons); 720 } 721 } else { 722 mAodIcons.setAlpha(1.0f); 723 mAodIcons.setTranslationY(0); 724 mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 725 } 726 } 727 } 728 729 @Override demoCommands()730 public List<String> demoCommands() { 731 ArrayList<String> commands = new ArrayList<>(); 732 commands.add(DemoMode.COMMAND_NOTIFICATIONS); 733 return commands; 734 } 735 736 @Override dispatchDemoCommand(String command, Bundle args)737 public void dispatchDemoCommand(String command, Bundle args) { 738 if (mNotificationIconArea != null) { 739 String visible = args.getString("visible"); 740 int vis = "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; 741 mNotificationIconArea.setVisibility(vis); 742 } 743 } 744 745 @Override onDemoModeFinished()746 public void onDemoModeFinished() { 747 if (mNotificationIconArea != null) { 748 mNotificationIconArea.setVisibility(View.VISIBLE); 749 } 750 } 751 } 752