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