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