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