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