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