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