From 72fc8db1e2b14da16bfb9170bc6172d3f93d0e97 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 6 Jun 2017 18:07:47 -0700 Subject: [PATCH] Fixed a flicker of group notifications When replacing a supressed group with it's summary, the icon would flicker. We're now suppressing icon animations of replaced notifications. Test: add notification and children 1 by 1 and observe no flickering Change-Id: I79440e473296e766ab80832ce74f450aee1af8ea Fixes: 38459207 --- .../systemui/statusbar/NotificationData.java | 12 ++-- .../systemui/statusbar/StatusBarIconView.java | 27 ++++++--- .../phone/NotificationIconAreaController.java | 47 ++++++++++++++- .../phone/NotificationIconContainer.java | 57 ++++++++++++++++--- .../systemui/statusbar/phone/StatusBar.java | 2 +- 5 files changed, 122 insertions(+), 23 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 1844946e75b6b..dd15d6adcfaa1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -141,12 +141,12 @@ public class NotificationData { // Construct the icon. icon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); + sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); // Construct the expanded icon. expandedIcon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); + sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); final StatusBarIcon ic = new StatusBarIcon( sbn.getUser(), @@ -182,9 +182,11 @@ public class NotificationData { * @param n the notification to read the icon from. * @throws InflationException */ - public void updateIcons(Context context, Notification n) throws InflationException { + public void updateIcons(Context context, StatusBarNotification sbn) + throws InflationException { if (icon != null) { // Update the icon + Notification n = sbn.getNotification(); final StatusBarIcon ic = new StatusBarIcon( notification.getUser(), notification.getPackageName(), @@ -192,8 +194,8 @@ public class NotificationData { n.iconLevel, n.number, StatusBarIconView.contentDescForNotification(context, n)); - icon.setNotification(n); - expandedIcon.setNotification(n); + icon.setNotification(sbn); + expandedIcon.setNotification(sbn); if (!icon.set(ic) || !expandedIcon.set(ic)) { throw new InflationException("Couldn't update icon: " + ic); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index c7fbbf956c68c..e489ba7c83b64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Parcelable; import android.os.UserHandle; +import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; @@ -99,7 +100,7 @@ public class StatusBarIconView extends AnimatedImageView { private int mNumberX; private int mNumberY; private String mNumberText; - private Notification mNotification; + private StatusBarNotification mNotification; private final boolean mBlocked; private int mDensity; private float mIconScale = 1.0f; @@ -127,11 +128,11 @@ public class StatusBarIconView extends AnimatedImageView { }; private final NotificationIconDozeHelper mDozer; - public StatusBarIconView(Context context, String slot, Notification notification) { - this(context, slot, notification, false); + public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) { + this(context, slot, sbn, false); } - public StatusBarIconView(Context context, String slot, Notification notification, + public StatusBarIconView(Context context, String slot, StatusBarNotification sbn, boolean blocked) { super(context); mDozer = new NotificationIconDozeHelper(context); @@ -141,7 +142,7 @@ public class StatusBarIconView extends AnimatedImageView { mNumberPain.setTextAlign(Paint.Align.CENTER); mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color)); mNumberPain.setAntiAlias(true); - setNotification(notification); + setNotification(sbn); maybeUpdateIconScaleDimens(); setScaleType(ScaleType.CENTER); mDensity = context.getResources().getDisplayMetrics().densityDpi; @@ -203,9 +204,11 @@ public class StatusBarIconView extends AnimatedImageView { } } - public void setNotification(Notification notification) { + public void setNotification(StatusBarNotification notification) { mNotification = notification; - setContentDescription(notification); + if (notification != null) { + setContentDescription(notification.getNotification()); + } } public StatusBarIconView(Context context, AttributeSet attrs) { @@ -311,6 +314,10 @@ public class StatusBarIconView extends AnimatedImageView { return true; } + public Icon getSourceIcon() { + return mIcon.icon; + } + private Drawable getIcon(StatusBarIcon icon) { return getIcon(getContext(), icon); } @@ -350,7 +357,7 @@ public class StatusBarIconView extends AnimatedImageView { public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); if (mNotification != null) { - event.setParcelableData(mNotification); + event.setParcelableData(mNotification.getNotification()); } } @@ -452,6 +459,10 @@ public class StatusBarIconView extends AnimatedImageView { + " notification=" + mNotification + ")"; } + public StatusBarNotification getNotification() { + return mNotification; + } + public String getSlot() { return mSlot; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 52838b052bd4e..5bfdd1f81cc2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -4,11 +4,15 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; +import android.graphics.drawable.Icon; import android.support.annotation.NonNull; +import android.support.v4.util.ArrayMap; +import android.support.v4.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; +import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.NotificationColorUtil; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -180,14 +184,54 @@ public class NotificationIconAreaController implements DarkReceiver { } } + // In case we are changing the suppression of a group, the replacement shouldn't flicker + // and it should just be replaced instead. We therefore look for notifications that were + // just replaced by the child or vice-versa to suppress this. + ArrayMap> replacingIcons = new ArrayMap<>(); ArrayList toRemove = new ArrayList<>(); for (int i = 0; i < hostLayout.getChildCount(); i++) { View child = hostLayout.getChildAt(i); + if (!(child instanceof StatusBarIconView)) { + continue; + } if (!toShow.contains(child)) { - toRemove.add(child); + boolean iconWasReplaced = false; + StatusBarIconView removedIcon = (StatusBarIconView) child; + String removedGroupKey = removedIcon.getNotification().getGroupKey(); + for (int j = 0; j < toShow.size(); j++) { + StatusBarIconView candidate = toShow.get(j); + if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon())) + && candidate.getNotification().getGroupKey().equals(removedGroupKey)) { + if (!iconWasReplaced) { + iconWasReplaced = true; + } else { + iconWasReplaced = false; + break; + } + } + } + if (iconWasReplaced) { + ArrayList statusBarIcons = replacingIcons.get(removedGroupKey); + if (statusBarIcons == null) { + statusBarIcons = new ArrayList<>(); + replacingIcons.put(removedGroupKey, statusBarIcons); + } + statusBarIcons.add(removedIcon.getStatusBarIcon()); + } + toRemove.add(removedIcon); } } + // removing all duplicates + ArrayList duplicates = new ArrayList<>(); + for (String key : replacingIcons.keySet()) { + ArrayList statusBarIcons = replacingIcons.get(key); + if (statusBarIcons.size() != 1) { + duplicates.add(key); + } + } + replacingIcons.removeAll(duplicates); + hostLayout.setReplacingIcons(replacingIcons); final int toRemoveCount = toRemove.size(); for (int i = 0; i < toRemoveCount; i++) { @@ -217,6 +261,7 @@ public class NotificationIconAreaController implements DarkReceiver { hostLayout.addView(expected, i); } hostLayout.setChangingViewPositions(false); + hostLayout.setReplacingIcons(null); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 9000eb4655d1f..06b80410bcdab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -21,9 +21,13 @@ import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.drawable.Icon; +import android.support.v4.util.ArrayMap; +import android.support.v4.util.ArraySet; import android.util.AttributeSet; import android.view.View; +import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.AlphaOptimizedFrameLayout; @@ -33,6 +37,7 @@ import com.android.systemui.statusbar.stack.AnimationProperties; import com.android.systemui.statusbar.stack.StackStateAnimator; import com.android.systemui.statusbar.stack.ViewState; +import java.util.ArrayList; import java.util.HashMap; /** @@ -117,6 +122,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private float mVisualOverflowAdaption; private boolean mDisallowNextAnimation; private boolean mAnimationsEnabled = true; + private ArrayMap> mReplacingIcons; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -183,11 +189,17 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { @Override public void onViewAdded(View child) { super.onViewAdded(child); + boolean isReplacingIcon = isReplacingIcon(child); if (!mChangingViewPositions) { - mIconStates.put(child, new IconState()); + IconState v = new IconState(); + if (isReplacingIcon) { + v.justAdded = false; + v.justReplaced = true; + } + mIconStates.put(child, v); } int childIndex = indexOfChild(child); - if (childIndex < getChildCount() - 1 + if (childIndex < getChildCount() - 1 && !isReplacingIcon && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) { if (mAddAnimationStartIndex < 0) { mAddAnimationStartIndex = childIndex; @@ -200,13 +212,34 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } + private boolean isReplacingIcon(View child) { + if (mReplacingIcons == null) { + return false; + } + if (!(child instanceof StatusBarIconView)) { + return false; + } + StatusBarIconView iconView = (StatusBarIconView) child; + Icon sourceIcon = iconView.getSourceIcon(); + String groupKey = iconView.getNotification().getGroupKey(); + ArrayList statusBarIcons = mReplacingIcons.get(groupKey); + if (statusBarIcons != null) { + StatusBarIcon replacedIcon = statusBarIcons.get(0); + if (sourceIcon.sameAs(replacedIcon.icon)) { + return true; + } + } + return false; + } + @Override public void onViewRemoved(View child) { super.onViewRemoved(child); if (child instanceof StatusBarIconView) { + boolean isReplacingIcon = isReplacingIcon(child); final StatusBarIconView icon = (StatusBarIconView) child; if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN - && child.getVisibility() == VISIBLE) { + && child.getVisibility() == VISIBLE && isReplacingIcon) { int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX()); if (mAddAnimationStartIndex < 0) { mAddAnimationStartIndex = animationStartIndex; @@ -216,9 +249,11 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } if (!mChangingViewPositions) { mIconStates.remove(child); - addTransientView(icon, 0); - icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */, - () -> removeTransientView(icon)); + if (!isReplacingIcon) { + addTransientView(icon, 0); + icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */, + () -> removeTransientView(icon)); + } } } } @@ -470,11 +505,16 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAnimationsEnabled = enabled; } + public void setReplacingIcons(ArrayMap> replacingIcons) { + mReplacingIcons = replacingIcons; + } + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; public int visibleState; public boolean justAdded = true; + private boolean justReplaced; public boolean needsCannedAnimation; public boolean useFullTransitionAmount; public boolean useLinearTransitionAmount; @@ -493,9 +533,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { && !mDisallowNextAnimation && !noAnimations; if (animationsAllowed) { - if (justAdded) { + if (justAdded || justReplaced) { super.applyToView(icon); - if (iconAppearAmount != 0.0f) { + if (justAdded && iconAppearAmount != 0.0f) { icon.setAlpha(0.0f); icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */); @@ -554,6 +594,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } justAdded = false; + justReplaced = false; needsCannedAnimation = false; justUndarkened = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 07ab6876e42bd..9b7a1a3c9d889 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -6762,7 +6762,7 @@ public class StatusBar extends SystemUI implements DemoMode, entry.notification = notification; mGroupManager.onEntryUpdated(entry, oldNotification); - entry.updateIcons(mContext, n); + entry.updateIcons(mContext, notification); inflateViews(entry, mStackScroller); boolean shouldPeek = shouldPeek(entry, notification);