diff options
27 files changed, 1071 insertions, 301 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index ec1fa738f819..ae2e1d1324ff 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -83,8 +83,7 @@ object Flags { val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true) @JvmField - val NOTIFICATION_GROUP_CORNER = - unreleasedFlag(116, "notification_group_corner", teamfood = true) + val USE_ROUNDNESS_SOURCETYPES = unreleasedFlag(116, "use_roundness_sourcetype", teamfood = true) // TODO(b/259217907) @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index d7eddf53dea5..56c34a0b3665 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -39,6 +39,7 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -66,6 +67,8 @@ public class NotificationShelf extends ActivatableNotificationView implements // the next icon has translated out of the way, to avoid overlapping. private static final Interpolator ICON_ALPHA_INTERPOLATOR = new PathInterpolator(0.6f, 0f, 0.6f, 0f); + private static final SourceType BASE_VALUE = SourceType.from("BaseValue"); + private static final SourceType SHELF_SCROLL = SourceType.from("ShelfScroll"); private NotificationIconContainer mShelfIcons; private int[] mTmp = new int[2]; @@ -112,19 +115,24 @@ public class NotificationShelf extends ActivatableNotificationView implements setClipChildren(false); setClipToPadding(false); mShelfIcons.setIsStaticLayout(false); - requestBottomRoundness(1.0f, /* animate = */ false, SourceType.DefaultValue); - requestTopRoundness(1f, false, SourceType.DefaultValue); + requestRoundness(/* top = */ 1f, /* bottom = */ 1f, BASE_VALUE, /* animate = */ false); - // Setting this to first in section to get the clipping to the top roundness correct. This - // value determines the way we are clipping to the top roundness of the overall shade - setFirstInSection(true); + if (!mUseRoundnessSourceTypes) { + // Setting this to first in section to get the clipping to the top roundness correct. + // This value determines the way we are clipping to the top roundness of the overall + // shade + setFirstInSection(true); + } updateResources(); } public void bind(AmbientState ambientState, - NotificationStackScrollLayoutController hostLayoutController) { + NotificationStackScrollLayoutController hostLayoutController) { mAmbientState = ambientState; mHostLayoutController = hostLayoutController; + hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> { + child.requestRoundnessReset(SHELF_SCROLL); + }); } private void updateResources() { @@ -185,9 +193,11 @@ public class NotificationShelf extends ActivatableNotificationView implements + " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')'; } - /** Update the state of the shelf. */ + /** + * Update the state of the shelf. + */ public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { ExpandableView lastView = ambientState.getLastVisibleBackgroundChild(); ShelfState viewState = (ShelfState) getViewState(); if (mShowNotificationShelf && lastView != null) { @@ -246,7 +256,7 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * @param fractionToShade Fraction of lockscreen to shade transition - * @param shortestWidth Shortest width to use for lockscreen shelf + * @param shortestWidth Shortest width to use for lockscreen shelf */ @VisibleForTesting public void updateActualWidth(float fractionToShade, float shortestWidth) { @@ -281,9 +291,9 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * @param localX Click x from left of screen - * @param slop Margin of error within which we count x for valid click - * @param left Left of shelf, from left of screen - * @param right Right of shelf, from left of screen + * @param slop Margin of error within which we count x for valid click + * @param left Left of shelf, from left of screen + * @param right Right of shelf, from left of screen * @return Whether click x was in view */ @VisibleForTesting @@ -293,8 +303,8 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * @param localY Click y from top of shelf - * @param slop Margin of error within which we count y for valid click - * @param top Top of shelf + * @param slop Margin of error within which we count y for valid click + * @param top Top of shelf * @param bottom Height of shelf * @return Whether click y was in view */ @@ -306,7 +316,7 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * @param localX Click x * @param localY Click y - * @param slop Margin of error for valid click + * @param slop Margin of error for valid click * @return Whether this click was on the visible (non-clipped) part of the shelf */ @Override @@ -478,13 +488,15 @@ public class NotificationShelf extends ActivatableNotificationView implements } } - private void updateCornerRoundnessOnScroll(ActivatableNotificationView anv, float viewStart, + private void updateCornerRoundnessOnScroll( + ActivatableNotificationView anv, + float viewStart, float shelfStart) { final boolean isUnlockedHeadsUp = !mAmbientState.isOnKeyguard() && !mAmbientState.isShadeExpanded() && anv instanceof ExpandableNotificationRow - && ((ExpandableNotificationRow) anv).isHeadsUp(); + && anv.isHeadsUp(); final boolean isHunGoingToShade = mAmbientState.isShadeExpanded() && anv == mAmbientState.getTrackedHeadsUpRow(); @@ -506,41 +518,40 @@ public class NotificationShelf extends ActivatableNotificationView implements * mAmbientState.getExpansionFraction(); final float cornerAnimationTop = shelfStart - cornerAnimationDistance; - if (viewEnd >= cornerAnimationTop) { - // Round bottom corners within animation bounds - final float changeFraction = MathUtils.saturate( - (viewEnd - cornerAnimationTop) / cornerAnimationDistance); - anv.requestBottomRoundness( - /* value = */ anv.isLastInSection() ? 1f : changeFraction, - /* animate = */ false, - SourceType.OnScroll); - - } else if (viewEnd < cornerAnimationTop) { - // Fast scroll skips frames and leaves corners with unfinished rounding. - // Reset top and bottom corners outside of animation bounds. - anv.requestBottomRoundness( - /* value = */ anv.isLastInSection() ? 1f : 0f, - /* animate = */ false, - SourceType.OnScroll); + final SourceType sourceType; + if (mUseRoundnessSourceTypes) { + sourceType = SHELF_SCROLL; + } else { + sourceType = LegacySourceType.OnScroll; } - if (viewStart >= cornerAnimationTop) { + final float topValue; + if (!mUseRoundnessSourceTypes && anv.isFirstInSection()) { + topValue = 1f; + } else if (viewStart >= cornerAnimationTop) { // Round top corners within animation bounds - final float changeFraction = MathUtils.saturate( + topValue = MathUtils.saturate( (viewStart - cornerAnimationTop) / cornerAnimationDistance); - anv.requestTopRoundness( - /* value = */ anv.isFirstInSection() ? 1f : changeFraction, - /* animate = */ false, - SourceType.OnScroll); + } else { + // Fast scroll skips frames and leaves corners with unfinished rounding. + // Reset top and bottom corners outside of animation bounds. + topValue = 0f; + } + anv.requestTopRoundness(topValue, sourceType, /* animate = */ false); - } else if (viewStart < cornerAnimationTop) { + final float bottomValue; + if (!mUseRoundnessSourceTypes && anv.isLastInSection()) { + bottomValue = 1f; + } else if (viewEnd >= cornerAnimationTop) { + // Round bottom corners within animation bounds + bottomValue = MathUtils.saturate( + (viewEnd - cornerAnimationTop) / cornerAnimationDistance); + } else { // Fast scroll skips frames and leaves corners with unfinished rounding. // Reset top and bottom corners outside of animation bounds. - anv.requestTopRoundness( - /* value = */ anv.isFirstInSection() ? 1f : 0f, - /* animate = */ false, - SourceType.OnScroll); + bottomValue = 0f; } + anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false); } /** @@ -626,10 +637,11 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * Update the clipping of this view. + * * @return the amount that our own top should be clipped */ private int updateNotificationClipHeight(ExpandableView view, - float notificationClipEnd, int childIndex) { + float notificationClipEnd, int childIndex) { float viewEnd = view.getTranslationY() + view.getActualHeight(); boolean isPinned = (view.isPinned() || view.isHeadsUpAnimatingAway()) && !mAmbientState.isDozingAndNotPulsing(view); @@ -657,7 +669,7 @@ public class NotificationShelf extends ActivatableNotificationView implements @Override public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd, - int outlineTranslation) { + int outlineTranslation) { if (!mHasItemsInStableShelf) { shadowIntensity = 0.0f; } @@ -665,18 +677,24 @@ public class NotificationShelf extends ActivatableNotificationView implements } /** - * @param i Index of the view in the host layout. - * @param view The current ExpandableView. - * @param scrollingFast Whether we are scrolling fast. + * @param i Index of the view in the host layout. + * @param view The current ExpandableView. + * @param scrollingFast Whether we are scrolling fast. * @param expandingAnimated Whether we are expanding a notification. - * @param isLastChild Whether this is the last view. - * @param shelfClipStart The point at which notifications start getting clipped by the shelf. + * @param isLastChild Whether this is the last view. + * @param shelfClipStart The point at which notifications start getting clipped by the shelf. * @return The amount how much this notification is in the shelf. - * 0f is not in shelf. 1f is completely in shelf. + * 0f is not in shelf. 1f is completely in shelf. */ @VisibleForTesting - public float getAmountInShelf(int i, ExpandableView view, boolean scrollingFast, - boolean expandingAnimated, boolean isLastChild, float shelfClipStart) { + public float getAmountInShelf( + int i, + ExpandableView view, + boolean scrollingFast, + boolean expandingAnimated, + boolean isLastChild, + float shelfClipStart + ) { // Let's calculate how much the view is in the shelf float viewStart = view.getTranslationY(); @@ -755,8 +773,13 @@ public class NotificationShelf extends ActivatableNotificationView implements return start; } - private void updateIconPositioning(ExpandableView view, float iconTransitionAmount, - boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) { + private void updateIconPositioning( + ExpandableView view, + float iconTransitionAmount, + boolean scrollingFast, + boolean expandingAnimated, + boolean isLastChild + ) { StatusBarIconView icon = view.getShelfIcon(); NotificationIconContainer.IconState iconState = getIconState(icon); if (iconState == null) { @@ -817,7 +840,7 @@ public class NotificationShelf extends ActivatableNotificationView implements || row.showingPulsing() || row.getTranslationZ() > mAmbientState.getBaseZHeight(); - iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount; + iconState.iconAppearAmount = iconState.hidden ? 0f : transitionAmount; // Fade in icons at shelf start // This is important for conversation icons, which are badged and need x reset @@ -847,7 +870,7 @@ public class NotificationShelf extends ActivatableNotificationView implements } private float getFullyClosedTranslation() { - return - (getIntrinsicHeight() - mStatusBarHeight) / 2; + return -(getIntrinsicHeight() - mStatusBarHeight) / 2; } @Override @@ -904,7 +927,7 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * @return whether the shelf has any icons in it when a potential animation has finished, i.e - * if the current state would be applied right now + * if the current state would be applied right now */ public boolean hasItemsInStableShelf() { return mHasItemsInStableShelf; @@ -962,7 +985,7 @@ public class NotificationShelf extends ActivatableNotificationView implements @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { + int oldTop, int oldRight, int oldBottom) { updateRelativeOffset(); } @@ -981,12 +1004,11 @@ public class NotificationShelf extends ActivatableNotificationView implements /** * This method resets the OnScroll roundness of a view to 0f - * + * <p> * Note: This should be the only class that handles roundness {@code SourceType.OnScroll} */ - public static void resetOnScrollRoundness(ExpandableView expandableView) { - expandableView.requestTopRoundness(0f, false, SourceType.OnScroll); - expandableView.requestBottomRoundness(0f, false, SourceType.OnScroll); + public static void resetLegacyOnScrollRoundness(ExpandableView expandableView) { + expandableView.requestRoundnessReset(LegacySourceType.OnScroll); } public class ShelfState extends ExpandableViewState { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java index 3b1fa1779c17..bb84c758d87d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import android.view.View; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; @@ -42,14 +44,17 @@ public class NotificationShelfController { private AmbientState mAmbientState; @Inject - public NotificationShelfController(NotificationShelf notificationShelf, + public NotificationShelfController( + NotificationShelf notificationShelf, ActivatableNotificationViewController activatableNotificationViewController, KeyguardBypassController keyguardBypassController, - SysuiStatusBarStateController statusBarStateController) { + SysuiStatusBarStateController statusBarStateController, + FeatureFlags featureFlags) { mView = notificationShelf; mActivatableNotificationViewController = activatableNotificationViewController; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; + mView.useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)); mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { @@ -88,7 +93,7 @@ public class NotificationShelfController { public @View.Visibility int getVisibility() { return mView.getVisibility(); - }; + } public void setCollapsedIcons(NotificationIconContainer notificationIcons) { mView.setCollapsedIcons(notificationIcons); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index ed7f648081c8..0eb00008e289 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -74,8 +74,8 @@ interface Roundable { @JvmDefault fun requestTopRoundness( @FloatRange(from = 0.0, to = 1.0) value: Float, - animate: Boolean, sourceType: SourceType, + animate: Boolean, ): Boolean { val roundnessMap = roundableState.topRoundnessMap val lastValue = roundnessMap.values.maxOrNull() ?: 0f @@ -105,6 +105,30 @@ interface Roundable { } /** + * Request the top roundness [value] for a specific [sourceType]. Animate the roundness if the + * view is shown. + * + * The top roundness of a [Roundable] can be defined by different [sourceType]. In case more + * origins require different roundness, for the same property, the maximum value will always be + * chosen. + * + * @param value a value between 0f and 1f. + * @param sourceType the source from which the request for roundness comes. + * @return Whether the roundness was changed. + */ + @JvmDefault + fun requestTopRoundness( + @FloatRange(from = 0.0, to = 1.0) value: Float, + sourceType: SourceType, + ): Boolean { + return requestTopRoundness( + value = value, + sourceType = sourceType, + animate = roundableState.targetView.isShown + ) + } + + /** * Request the bottom roundness [value] for a specific [sourceType]. * * The bottom roundness of a [Roundable] can be defined by different [sourceType]. In case more @@ -119,8 +143,8 @@ interface Roundable { @JvmDefault fun requestBottomRoundness( @FloatRange(from = 0.0, to = 1.0) value: Float, - animate: Boolean, sourceType: SourceType, + animate: Boolean, ): Boolean { val roundnessMap = roundableState.bottomRoundnessMap val lastValue = roundnessMap.values.maxOrNull() ?: 0f @@ -149,9 +173,101 @@ interface Roundable { return false } + /** + * Request the bottom roundness [value] for a specific [sourceType]. Animate the roundness if + * the view is shown. + * + * The bottom roundness of a [Roundable] can be defined by different [sourceType]. In case more + * origins require different roundness, for the same property, the maximum value will always be + * chosen. + * + * @param value value between 0f and 1f. + * @param sourceType the source from which the request for roundness comes. + * @return Whether the roundness was changed. + */ + @JvmDefault + fun requestBottomRoundness( + @FloatRange(from = 0.0, to = 1.0) value: Float, + sourceType: SourceType, + ): Boolean { + return requestBottomRoundness( + value = value, + sourceType = sourceType, + animate = roundableState.targetView.isShown + ) + } + + /** + * Request the roundness [value] for a specific [sourceType]. + * + * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case + * more origins require different roundness, for the same property, the maximum value will + * always be chosen. + * + * @param top top value between 0f and 1f. + * @param bottom bottom value between 0f and 1f. + * @param sourceType the source from which the request for roundness comes. + * @param animate true if it should animate to that value. + * @return Whether the roundness was changed. + */ + @JvmDefault + fun requestRoundness( + @FloatRange(from = 0.0, to = 1.0) top: Float, + @FloatRange(from = 0.0, to = 1.0) bottom: Float, + sourceType: SourceType, + animate: Boolean, + ): Boolean { + val hasTopChanged = + requestTopRoundness(value = top, sourceType = sourceType, animate = animate) + val hasBottomChanged = + requestBottomRoundness(value = bottom, sourceType = sourceType, animate = animate) + return hasTopChanged || hasBottomChanged + } + + /** + * Request the roundness [value] for a specific [sourceType]. Animate the roundness if the view + * is shown. + * + * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case + * more origins require different roundness, for the same property, the maximum value will + * always be chosen. + * + * @param top top value between 0f and 1f. + * @param bottom bottom value between 0f and 1f. + * @param sourceType the source from which the request for roundness comes. + * @return Whether the roundness was changed. + */ + @JvmDefault + fun requestRoundness( + @FloatRange(from = 0.0, to = 1.0) top: Float, + @FloatRange(from = 0.0, to = 1.0) bottom: Float, + sourceType: SourceType, + ): Boolean { + return requestRoundness( + top = top, + bottom = bottom, + sourceType = sourceType, + animate = roundableState.targetView.isShown, + ) + } + + /** + * Request the roundness 0f for a [SourceType]. Animate the roundness if the view is shown. + * + * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case + * more origins require different roundness, for the same property, the maximum value will + * always be chosen. + * + * @param sourceType the source from which the request for roundness comes. + */ + @JvmDefault + fun requestRoundnessReset(sourceType: SourceType) { + requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType) + } + /** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */ @JvmDefault - fun applyRoundness() { + fun applyRoundnessAndInvalidate() { roundableState.targetView.invalidate() } @@ -227,7 +343,7 @@ class RoundableState( /** Set the current top roundness */ internal fun setTopRoundness( value: Float, - animated: Boolean = targetView.isShown, + animated: Boolean, ) { PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated) } @@ -235,11 +351,19 @@ class RoundableState( /** Set the current bottom roundness */ internal fun setBottomRoundness( value: Float, - animated: Boolean = targetView.isShown, + animated: Boolean, ) { PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated) } + fun debugString() = buildString { + append("TargetView: ${targetView.hashCode()} ") + append("Top: $topRoundness ") + append(topRoundnessMap.map { "${it.key} ${it.value}" }) + append(" Bottom: $bottomRoundness ") + append(bottomRoundnessMap.map { "${it.key} ${it.value}" }) + } + companion object { private val DURATION: AnimationProperties = AnimationProperties() @@ -252,7 +376,7 @@ class RoundableState( override fun setValue(view: View, value: Float) { roundable.roundableState.topRoundness = value - roundable.applyRoundness() + roundable.applyRoundnessAndInvalidate() } }, R.id.top_roundess_animator_tag, @@ -267,7 +391,7 @@ class RoundableState( override fun setValue(view: View, value: Float) { roundable.roundableState.bottomRoundness = value - roundable.applyRoundness() + roundable.applyRoundnessAndInvalidate() } }, R.id.bottom_roundess_animator_tag, @@ -277,7 +401,31 @@ class RoundableState( } } -enum class SourceType { +/** + * Interface used to define the owner of a roundness. Usually the [SourceType] is defined as a + * private property of a class. + */ +interface SourceType { + companion object { + /** + * This is the most convenient way to define a new [SourceType]. + * + * For example: + * + * ```kotlin + * private val SECTION = SourceType.from("Section") + * ``` + */ + @JvmStatic + fun from(name: String) = + object : SourceType { + override fun toString() = name + } + } +} + +@Deprecated("Use SourceType.from() instead", ReplaceWith("SourceType.from()")) +enum class LegacySourceType : SourceType { DefaultValue, OnDismissAnimation, OnScroll, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index d29298a2f637..fbe88dff07f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -39,9 +39,13 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import java.util.HashSet; +import java.util.Set; + /** * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf} * to implement dimming/activating on Keyguard for the double-tap gesture @@ -91,6 +95,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView = new PathInterpolator(0.6f, 0, 0.5f, 1); private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR = new PathInterpolator(0, 0, 0.5f, 1); + private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>(); private int mTintedRippleColor; private int mNormalRippleColor; private Gefingerpoken mTouchHandler; @@ -134,6 +139,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private boolean mDismissed; private boolean mRefocusOnDismiss; private AccessibilityManager mAccessibilityManager; + protected boolean mUseRoundnessSourceTypes; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -613,9 +619,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected void resetAllContentAlphas() {} @Override - public void applyRoundness() { - super.applyRoundness(); + public void applyRoundnessAndInvalidate() { applyBackgroundRoundness(getTopCornerRadius(), getBottomCornerRadius()); + super.applyRoundnessAndInvalidate(); } @Override @@ -775,6 +781,33 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mAccessibilityManager = accessibilityManager; } + /** + * Enable the support for rounded corner based on the SourceType + * @param enabled true if is supported + */ + public void useRoundnessSourceTypes(boolean enabled) { + mUseRoundnessSourceTypes = enabled; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mUseRoundnessSourceTypes && !mOnDetachResetRoundness.isEmpty()) { + for (SourceType sourceType : mOnDetachResetRoundness) { + requestRoundnessReset(sourceType); + } + mOnDetachResetRoundness.clear(); + } + } + + /** + * SourceType which should be reset when this View is detached + * @param sourceType will be reset on View detached + */ + public void addOnDetachResetRoundness(SourceType sourceType) { + mOnDetachResetRoundness.add(sourceType); + } + public interface OnActivatedListener { void onActivated(ActivatableNotificationView view); void onActivationReset(ActivatableNotificationView view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index d7d5ac961249..c7c1634ea105 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -91,6 +91,7 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; +import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -143,6 +144,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private static final int MENU_VIEW_INDEX = 0; public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f; private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30); + private static final SourceType BASE_VALUE = SourceType.from("BaseValue"); + private static final SourceType FROM_PARENT = SourceType.from("FromParent(ENR)"); + private static final SourceType PINNED = SourceType.from("Pinned"); // We don't correctly track dark mode until the content views are inflated, so always update // the background on first content update just in case it happens to be during a theme change. @@ -150,6 +154,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mNotificationTranslationFinished = false; private boolean mIsSnoozed; private boolean mIsFaded; + private boolean mAnimatePinnedRoundness = false; /** * Listener for when {@link ExpandableNotificationRow} is laid out. @@ -376,7 +381,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private float mTopRoundnessDuringLaunchAnimation; private float mBottomRoundnessDuringLaunchAnimation; - private boolean mIsNotificationGroupCornerEnabled; + private float mSmallRoundness; /** * Returns whether the given {@code statusBarNotification} is a system notification. @@ -844,7 +849,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } onAttachedChildrenCountChanged(); row.setIsChildInGroup(false, null); - row.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue); + if (!mUseRoundnessSourceTypes) { + row.requestBottomRoundness(0.0f, LegacySourceType.DefaultValue, /* animate = */ false); + } } /** @@ -860,7 +867,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (child.keepInParentForDismissAnimation()) { mChildrenContainer.removeNotification(child); child.setIsChildInGroup(false, null); - child.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue); + if (!mUseRoundnessSourceTypes) { + LegacySourceType sourceType = LegacySourceType.DefaultValue; + child.requestBottomRoundness(0f, sourceType, /* animate = */ false); + } child.setKeepInParentForDismissAnimation(false); logKeepInParentChildDetached(child); childCountChanged = true; @@ -915,6 +925,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationParent.updateBackgroundForGroupState(); } updateBackgroundClipping(); + if (mUseRoundnessSourceTypes) { + updateBaseRoundness(); + } } @Override @@ -1033,6 +1046,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (isAboveShelf() != wasAboveShelf) { mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); } + if (mUseRoundnessSourceTypes) { + if (pinned) { + // Should be animated if someone explicitly set it to 0 and the row is shown. + boolean animated = mAnimatePinnedRoundness && isShown(); + requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated); + } else { + requestRoundnessReset(PINNED); + mAnimatePinnedRoundness = true; + } + } } @Override @@ -1607,6 +1630,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView super(context, attrs); mImageResolver = new NotificationInlineImageResolver(context, new NotificationInlineImageCache()); + float radius = getResources().getDimension(R.dimen.notification_corner_radius_small); + mSmallRoundness = radius / getMaxRadius(); initDimens(); } @@ -1839,7 +1864,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mChildrenContainer.setIsLowPriority(mIsLowPriority); mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.onNotificationUpdated(); - mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled); + mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes); mTranslateableViews.add(mChildrenContainer); }); @@ -2271,7 +2296,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public float getTopRoundness() { - if (mExpandAnimationRunning) { + if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) { return mTopRoundnessDuringLaunchAnimation; } @@ -2280,7 +2305,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public float getBottomRoundness() { - if (mExpandAnimationRunning) { + if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) { return mBottomRoundnessDuringLaunchAnimation; } @@ -3436,17 +3461,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - public void applyRoundness() { - super.applyRoundness(); + public void applyRoundnessAndInvalidate() { applyChildrenRoundness(); + super.applyRoundnessAndInvalidate(); } private void applyChildrenRoundness() { if (mIsSummaryWithChildren) { - mChildrenContainer.requestBottomRoundness( - getBottomRoundness(), - /* animate = */ false, - SourceType.DefaultValue); + if (mUseRoundnessSourceTypes) { + mChildrenContainer.requestRoundness( + /* top = */ getTopRoundness(), + /* bottom = */ getBottomRoundness(), + FROM_PARENT); + } else { + mChildrenContainer.requestBottomRoundness( + getBottomRoundness(), + LegacySourceType.DefaultValue, + /* animate = */ false); + } } } @@ -3605,6 +3637,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else { pw.println("no viewState!!!"); } + pw.println("Roundness: " + getRoundableState().debugString()); if (mIsSummaryWithChildren) { pw.println(); @@ -3649,14 +3682,38 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mTargetPoint; } + /** Update the minimum roundness based on current state */ + private void updateBaseRoundness() { + if (isChildInGroup()) { + requestRoundnessReset(BASE_VALUE); + } else { + requestRoundness(mSmallRoundness, mSmallRoundness, BASE_VALUE); + } + } + /** - * Enable the support for rounded corner in notification group + * Enable the support for rounded corner based on the SourceType * @param enabled true if is supported */ - public void enableNotificationGroupCorner(boolean enabled) { - mIsNotificationGroupCornerEnabled = enabled; + @Override + public void useRoundnessSourceTypes(boolean enabled) { + super.useRoundnessSourceTypes(enabled); if (mChildrenContainer != null) { - mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled); + mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes); + } + } + + @Override + public String toString() { + String roundableStateDebug = "RoundableState = " + getRoundableState().debugString(); + return "ExpandableNotificationRow:" + hashCode() + " { " + roundableStateDebug + " }"; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mUseRoundnessSourceTypes) { + updateBaseRoundness(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 8a400d5fef99..d1138608805b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -255,8 +255,8 @@ public class ExpandableNotificationRowController implements NotifViewController mStatusBarStateController.removeCallback(mStatusBarStateListener); } }); - mView.enableNotificationGroupCorner( - mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_CORNER)); + mView.useRoundnessSourceTypes( + mFeatureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)); } private final StatusBarStateController.StateListener mStatusBarStateListener = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 232462714f7d..0213b969551e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -219,9 +219,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { } @Override - public void applyRoundness() { + public void applyRoundnessAndInvalidate() { invalidateOutline(); - super.applyRoundness(); + super.applyRoundnessAndInvalidate(); } protected void setBackgroundTop(int backgroundTop) { @@ -233,7 +233,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { public void onDensityOrFontScaleChanged() { initDimens(); - applyRoundness(); + applyRoundnessAndInvalidate(); } @Override @@ -241,7 +241,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { int previousHeight = getActualHeight(); super.setActualHeight(actualHeight, notifyListeners); if (previousHeight != actualHeight) { - applyRoundness(); + applyRoundnessAndInvalidate(); } } @@ -250,7 +250,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { int previousAmount = getClipTopAmount(); super.setClipTopAmount(clipTopAmount); if (previousAmount != clipTopAmount) { - applyRoundness(); + applyRoundnessAndInvalidate(); } } @@ -259,14 +259,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { int previousAmount = getClipBottomAmount(); super.setClipBottomAmount(clipBottomAmount); if (previousAmount != clipBottomAmount) { - applyRoundness(); + applyRoundnessAndInvalidate(); } } protected void setOutlineAlpha(float alpha) { if (alpha != mOutlineAlpha) { mOutlineAlpha = alpha; - applyRoundness(); + applyRoundnessAndInvalidate(); } } @@ -280,7 +280,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { setOutlineRect(rect.left, rect.top, rect.right, rect.bottom); } else { mCustomOutline = false; - applyRoundness(); + applyRoundnessAndInvalidate(); } } @@ -340,7 +340,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { // Outlines need to be at least 1 dp mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom); mOutlineRect.right = (int) Math.max(left, mOutlineRect.right); - applyRoundness(); + applyRoundnessAndInvalidate(); } public Path getCustomClipPath(View child) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index f13e48d55ae4..87a9098579a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -343,4 +343,14 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } } } + + /** + * Interface that handle the Roundness changes + */ + public interface RoundnessChangedListener { + /** + * This method will be called when this class call applyRoundnessAndInvalidate() + */ + void applyRoundnessAndInvalidate(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index d43ca823089f..6350e49f64df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationGroupingUtil; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.FeedbackIcon; +import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.Roundable; @@ -83,6 +84,7 @@ public class NotificationChildrenContainer extends ViewGroup return mAnimationFilter; } }.setDuration(200); + private static final SourceType FROM_PARENT = SourceType.from("FromParent(NCC)"); private final List<View> mDividers = new ArrayList<>(); private final List<ExpandableNotificationRow> mAttachedChildren = new ArrayList<>(); @@ -131,7 +133,7 @@ public class NotificationChildrenContainer extends ViewGroup private int mUntruncatedChildCount; private boolean mContainingNotificationIsFaded = false; private RoundableState mRoundableState; - private boolean mIsNotificationGroupCornerEnabled; + private boolean mUseRoundnessSourceTypes; public NotificationChildrenContainer(Context context) { this(context, null); @@ -313,9 +315,12 @@ public class NotificationChildrenContainer extends ViewGroup row.setContentTransformationAmount(0, false /* isLastChild */); row.setNotificationFaded(mContainingNotificationIsFaded); - // This is a workaround, the NotificationShelf should be the owner of `OnScroll` roundness. - // Here we should reset the `OnScroll` roundness only on top-level rows. - NotificationShelf.resetOnScrollRoundness(row); + if (!mUseRoundnessSourceTypes) { + // This is a workaround, the NotificationShelf should be the owner of `OnScroll` + // roundness. + // Here we should reset the `OnScroll` roundness only on top-level rows. + NotificationShelf.resetLegacyOnScrollRoundness(row); + } // It doesn't make sense to keep old animations around, lets cancel them! ExpandableViewState viewState = row.getViewState(); @@ -323,6 +328,10 @@ public class NotificationChildrenContainer extends ViewGroup viewState.cancelAnimations(row); row.cancelAppearDrawing(); } + + if (mUseRoundnessSourceTypes) { + applyRoundnessAndInvalidate(); + } } private void ensureRemovedFromTransientContainer(View v) { @@ -356,6 +365,11 @@ public class NotificationChildrenContainer extends ViewGroup if (!row.isRemoved()) { mGroupingUtil.restoreChildNotification(row); } + + if (mUseRoundnessSourceTypes) { + row.requestRoundnessReset(FROM_PARENT); + applyRoundnessAndInvalidate(); + } } /** @@ -841,7 +855,7 @@ public class NotificationChildrenContainer extends ViewGroup isCanvasChanged = true; canvas.save(); - if (mIsNotificationGroupCornerEnabled && translation != 0f) { + if (mUseRoundnessSourceTypes && translation != 0f) { clipPath.offset(translation, 0f); canvas.clipPath(clipPath); clipPath.offset(-translation, 0f); @@ -1392,24 +1406,28 @@ public class NotificationChildrenContainer extends ViewGroup } @Override - public void applyRoundness() { - Roundable.super.applyRoundness(); + public void applyRoundnessAndInvalidate() { boolean last = true; for (int i = mAttachedChildren.size() - 1; i >= 0; i--) { ExpandableNotificationRow child = mAttachedChildren.get(i); if (child.getVisibility() == View.GONE) { continue; } - child.requestTopRoundness( - /* value = */ 0f, - /* animate = */ isShown(), - SourceType.DefaultValue); - child.requestBottomRoundness( - /* value = */ last ? getBottomRoundness() : 0f, - /* animate = */ isShown(), - SourceType.DefaultValue); + if (mUseRoundnessSourceTypes) { + child.requestRoundness( + /* top = */ 0f, + /* bottom = */ last ? getBottomRoundness() : 0f, + FROM_PARENT); + } else { + child.requestRoundness( + /* top = */ 0f, + /* bottom = */ last ? getBottomRoundness() : 0f, + LegacySourceType.DefaultValue, + /* animate = */ isShown()); + } last = false; } + Roundable.super.applyRoundnessAndInvalidate(); } public void setHeaderVisibleAmount(float headerVisibleAmount) { @@ -1467,10 +1485,17 @@ public class NotificationChildrenContainer extends ViewGroup } /** - * Enable the support for rounded corner in notification group + * Enable the support for rounded corner based on the SourceType + * * @param enabled true if is supported */ - public void enableNotificationGroupCorner(boolean enabled) { - mIsNotificationGroupCornerEnabled = enabled; + public void useRoundnessSourceTypes(boolean enabled) { + mUseRoundnessSourceTypes = enabled; + } + + @Override + public String toString() { + String roundableStateDebug = "RoundableState = " + getRoundableState().debugString(); + return "NotificationChildrenContainer:" + hashCode() + " { " + roundableStateDebug + " }"; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 6810055ad3bf..fde8c4d453ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -25,6 +25,9 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.SourceType; @@ -45,6 +48,7 @@ import javax.inject.Inject; public class NotificationRoundnessManager implements Dumpable { private static final String TAG = "NotificationRoundnessManager"; + private static final SourceType DISMISS_ANIMATION = SourceType.from("DismissAnimation"); private final ExpandableView[] mFirstInSectionViews; private final ExpandableView[] mLastInSectionViews; @@ -63,12 +67,14 @@ public class NotificationRoundnessManager implements Dumpable { private ExpandableView mSwipedView = null; private Roundable mViewBeforeSwipedView = null; private Roundable mViewAfterSwipedView = null; + private boolean mUseRoundnessSourceTypes; @Inject NotificationRoundnessManager( NotificationSectionsFeatureManager sectionsFeatureManager, NotificationRoundnessLogger notifLogger, - DumpManager dumpManager) { + DumpManager dumpManager, + FeatureFlags featureFlags) { int numberOfSections = sectionsFeatureManager.getNumberOfBuckets(); mFirstInSectionViews = new ExpandableView[numberOfSections]; mLastInSectionViews = new ExpandableView[numberOfSections]; @@ -76,6 +82,7 @@ public class NotificationRoundnessManager implements Dumpable { mTmpLastInSectionViews = new ExpandableView[numberOfSections]; mNotifLogger = notifLogger; mDumpManager = dumpManager; + mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); mDumpManager.registerDumpable(TAG, this); } @@ -94,6 +101,7 @@ public class NotificationRoundnessManager implements Dumpable { } public void updateView(ExpandableView view, boolean animate) { + if (mUseRoundnessSourceTypes) return; boolean changed = updateViewWithoutCallback(view, animate); if (changed) { mRoundingChangedCallback.run(); @@ -110,6 +118,7 @@ public class NotificationRoundnessManager implements Dumpable { boolean updateViewWithoutCallback( ExpandableView view, boolean animate) { + if (mUseRoundnessSourceTypes) return false; if (view == null || view == mViewBeforeSwipedView || view == mViewAfterSwipedView) { @@ -118,13 +127,13 @@ public class NotificationRoundnessManager implements Dumpable { final boolean isTopChanged = view.requestTopRoundness( getRoundnessDefaultValue(view, true /* top */), - animate, - SourceType.DefaultValue); + LegacySourceType.DefaultValue, + animate); final boolean isBottomChanged = view.requestBottomRoundness( getRoundnessDefaultValue(view, /* top = */ false), - animate, - SourceType.DefaultValue); + LegacySourceType.DefaultValue, + animate); final boolean isFirstInSection = isFirstInSection(view); final boolean isLastInSection = isLastInSection(view); @@ -139,6 +148,7 @@ public class NotificationRoundnessManager implements Dumpable { } private boolean isFirstInSection(ExpandableView view) { + if (mUseRoundnessSourceTypes) return false; for (int i = 0; i < mFirstInSectionViews.length; i++) { if (view == mFirstInSectionViews[i]) { return true; @@ -148,6 +158,7 @@ public class NotificationRoundnessManager implements Dumpable { } private boolean isLastInSection(ExpandableView view) { + if (mUseRoundnessSourceTypes) return false; for (int i = mLastInSectionViews.length - 1; i >= 0; i--) { if (view == mLastInSectionViews[i]) { return true; @@ -160,9 +171,6 @@ public class NotificationRoundnessManager implements Dumpable { Roundable viewBefore, ExpandableView viewSwiped, Roundable viewAfter) { - final boolean animate = true; - final SourceType source = SourceType.OnDismissAnimation; - // This method requires you to change the roundness of the current View targets and reset // the roundness of the old View targets (if any) to 0f. // To avoid conflicts, it generates a set of old Views and removes the current Views @@ -172,31 +180,34 @@ public class NotificationRoundnessManager implements Dumpable { if (mSwipedView != null) oldViews.add(mSwipedView); if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView); + final SourceType source; + if (mUseRoundnessSourceTypes) { + source = DISMISS_ANIMATION; + } else { + source = LegacySourceType.OnDismissAnimation; + } + mViewBeforeSwipedView = viewBefore; if (viewBefore != null) { oldViews.remove(viewBefore); - viewBefore.requestTopRoundness(0f, animate, source); - viewBefore.requestBottomRoundness(1f, animate, source); + viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, source); } mSwipedView = viewSwiped; if (viewSwiped != null) { oldViews.remove(viewSwiped); - viewSwiped.requestTopRoundness(1f, animate, source); - viewSwiped.requestBottomRoundness(1f, animate, source); + viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, source); } mViewAfterSwipedView = viewAfter; if (viewAfter != null) { oldViews.remove(viewAfter); - viewAfter.requestTopRoundness(1f, animate, source); - viewAfter.requestBottomRoundness(0f, animate, source); + viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, source); } // After setting the current Views, reset the views that are still present in the set. for (Roundable oldView : oldViews) { - oldView.requestTopRoundness(0f, animate, source); - oldView.requestBottomRoundness(0f, animate, source); + oldView.requestRoundnessReset(source); } } @@ -204,7 +215,23 @@ public class NotificationRoundnessManager implements Dumpable { mIsClearAllInProgress = isClearingAll; } + /** + * Check if "Clear all" notifications is in progress. + */ + public boolean isClearAllInProgress() { + return mIsClearAllInProgress; + } + + /** + * Check if we can request the `Pulsing` roundness for notification. + */ + public boolean shouldRoundNotificationPulsing() { + return mRoundForPulsingViews; + } + private float getRoundnessDefaultValue(Roundable view, boolean top) { + if (mUseRoundnessSourceTypes) return 0f; + if (view == null) { return 0f; } @@ -250,6 +277,7 @@ public class NotificationRoundnessManager implements Dumpable { } public void setExpanded(float expandedHeight, float appearFraction) { + if (mUseRoundnessSourceTypes) return; mExpanded = expandedHeight != 0.0f; mAppearFraction = appearFraction; if (mTrackedHeadsUp != null) { @@ -258,6 +286,7 @@ public class NotificationRoundnessManager implements Dumpable { } public void updateRoundedChildren(NotificationSection[] sections) { + if (mUseRoundnessSourceTypes) return; boolean anyChanged = false; for (int i = 0; i < sections.length; i++) { mTmpFirstInSectionViews[i] = mFirstInSectionViews[i]; @@ -280,6 +309,7 @@ public class NotificationRoundnessManager implements Dumpable { NotificationSection[] sections, ExpandableView[] oldViews, boolean first) { + if (mUseRoundnessSourceTypes) return false; boolean anyChanged = false; for (ExpandableView oldView : oldViews) { if (oldView != null) { @@ -313,6 +343,7 @@ public class NotificationRoundnessManager implements Dumpable { NotificationSection[] sections, ExpandableView[] oldViews, boolean first) { + if (mUseRoundnessSourceTypes) return false; boolean anyChanged = false; for (NotificationSection section : sections) { ExpandableView newView = @@ -339,6 +370,15 @@ public class NotificationRoundnessManager implements Dumpable { mAnimatedChildren = animatedChildren; } + /** + * Check if the view should be animated + * @param view target view + * @return true, if is in the AnimatedChildren set + */ + public boolean isAnimatedChild(ExpandableView view) { + return mAnimatedChildren.contains(view); + } + public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) { mRoundingChangedCallback = roundingChangedCallback; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index a1b77acb9a5e..070b4394291f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -19,8 +19,11 @@ import android.annotation.ColorInt import android.util.Log import android.view.View import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.media.controls.ui.KeyguardMediaController import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager +import com.android.systemui.statusbar.notification.SourceType import com.android.systemui.statusbar.notification.collection.render.MediaContainerController import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController import com.android.systemui.statusbar.notification.dagger.AlertingHeader @@ -44,12 +47,16 @@ class NotificationSectionsManager @Inject internal constructor( private val keyguardMediaController: KeyguardMediaController, private val sectionsFeatureManager: NotificationSectionsFeatureManager, private val mediaContainerController: MediaContainerController, + private val notificationRoundnessManager: NotificationRoundnessManager, @IncomingHeader private val incomingHeaderController: SectionHeaderController, @PeopleHeader private val peopleHeaderController: SectionHeaderController, @AlertingHeader private val alertingHeaderController: SectionHeaderController, - @SilentHeader private val silentHeaderController: SectionHeaderController + @SilentHeader private val silentHeaderController: SectionHeaderController, + featureFlags: FeatureFlags ) : SectionProvider { + private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES) + private val configurationListener = object : ConfigurationController.ConfigurationListener { override fun onLocaleListChanged() { reinflateViews() @@ -177,11 +184,49 @@ class NotificationSectionsManager @Inject internal constructor( size = sections.size, operation = SectionBounds::addNotif ) + + // Build a set of the old first/last Views of the sections + val oldFirstChildren = sections.mapNotNull { it.firstVisibleChild }.toSet().toMutableSet() + val oldLastChildren = sections.mapNotNull { it.lastVisibleChild }.toSet().toMutableSet() + // Update each section with the associated boundary, tracking if there was a change val changed = sections.fold(false) { changed, section -> val bounds = sectionBounds[section.bucket] ?: SectionBounds.None - bounds.updateSection(section) || changed + val isSectionChanged = bounds.updateSection(section) + isSectionChanged || changed + } + + if (useRoundnessSourceTypes) { + val newFirstChildren = sections.mapNotNull { it.firstVisibleChild } + val newLastChildren = sections.mapNotNull { it.lastVisibleChild } + + // Update the roundness of Views that weren't already in the first/last position + newFirstChildren.forEach { firstChild -> + val wasFirstChild = oldFirstChildren.remove(firstChild) + if (!wasFirstChild) { + val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild) + val animated = firstChild.isShown && notAnimatedChild + firstChild.requestTopRoundness(1f, SECTION, animated) + } + } + newLastChildren.forEach { lastChild -> + val wasLastChild = oldLastChildren.remove(lastChild) + if (!wasLastChild) { + val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild) + val animated = lastChild.isShown && notAnimatedChild + lastChild.requestBottomRoundness(1f, SECTION, animated) + } + } + + // The Views left in the set are no longer in the first/last position + oldFirstChildren.forEach { noMoreFirstChild -> + noMoreFirstChild.requestTopRoundness(0f, SECTION) + } + oldLastChildren.forEach { noMoreLastChild -> + noMoreLastChild.requestBottomRoundness(0f, SECTION) + } } + if (DEBUG) { logSections(sections) } @@ -215,5 +260,6 @@ class NotificationSectionsManager @Inject internal constructor( companion object { private const val TAG = "NotifSectionsManager" private const val DEBUG = false + private val SECTION = SourceType.from("Section") } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 7c3e52cf9630..21e2bd877bae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -191,10 +191,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final boolean mDebugLines; private Paint mDebugPaint; - /** Used to track the Y positions that were already used to draw debug text labels. */ + /** + * Used to track the Y positions that were already used to draw debug text labels. + */ private Set<Integer> mDebugTextUsedYPositions; private final boolean mDebugRemoveAnimation; private final boolean mSimplifiedAppearFraction; + private final boolean mUseRoundnessSourceTypes; private int mContentHeight; private float mIntrinsicContentHeight; @@ -355,15 +358,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final Rect mQsHeaderBound = new Rect(); private boolean mContinuousShadowUpdate; private boolean mContinuousBackgroundUpdate; - private final ViewTreeObserver.OnPreDrawListener mShadowUpdater - = () -> { - updateViewShadows(); - return true; - }; + private final ViewTreeObserver.OnPreDrawListener mShadowUpdater = () -> { + updateViewShadows(); + return true; + }; private final ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> { - updateBackground(); - return true; - }; + updateBackground(); + return true; + }; private final Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> { float endY = view.getTranslationY() + view.getActualHeight(); float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); @@ -419,7 +421,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; - @VisibleForTesting int mStatusBarHeight; + @VisibleForTesting + int mStatusBarHeight; private int mMinInteractionHeight; private final Rect mClipRect = new Rect(); private boolean mIsClipped; @@ -568,6 +571,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @Nullable private OnClickListener mManageButtonClickListener; + @Nullable + private OnNotificationRemovedListener mOnNotificationRemovedListener; public NotificationStackScrollLayout(Context context, AttributeSet attrs) { super(context, attrs, 0, 0); @@ -576,6 +581,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES); mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION); + mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); mSectionsManager = Dependency.get(NotificationSectionsManager.class); mScreenOffAnimationController = Dependency.get(ScreenOffAnimationController.class); @@ -741,7 +747,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private void logHunSkippedForUnexpectedState(ExpandableNotificationRow enr, - boolean expected, boolean actual) { + boolean expected, boolean actual) { if (mLogger == null) return; mLogger.hunSkippedForUnexpectedState(enr.getEntry(), expected, actual); } @@ -868,13 +874,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * Draws round rects for each background section. - * + * <p> * We want to draw a round rect for each background section as defined by {@link #mSections}. * However, if two sections are directly adjacent with no gap between them (e.g. on the * lockscreen where the shelf can appear directly below the high priority section, or while * scrolling the shade so that the top of the shelf is right at the bottom of the high priority * section), we don't want to round the adjacent corners. - * + * <p> * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect. * This method tracks the top of each rect we need to draw, then iterates through the visible @@ -883,7 +889,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * the current section. When we're done iterating we will always have one rect left to draw. */ private void drawBackgroundRects(Canvas canvas, int left, int right, int top, - int animationYOffset) { + int animationYOffset) { int backgroundRectTop = top; int lastSectionBottom = mSections[0].getCurrentBounds().bottom + animationYOffset; @@ -974,7 +980,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) void initView(Context context, NotificationSwipeHelper swipeHelper, - NotificationStackSizeCalculator notificationStackSizeCalculator) { + NotificationStackSizeCalculator notificationStackSizeCalculator) { mScroller = new OverScroller(getContext()); mSwipeHelper = swipeHelper; mNotificationStackSizeCalculator = notificationStackSizeCalculator; @@ -1304,18 +1310,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * @return Whether we should skip stack height updates. * True when - * 1) Unlock hint is running - * 2) Swiping up on lockscreen or flinging down after swipe up + * 1) Unlock hint is running + * 2) Swiping up on lockscreen or flinging down after swipe up */ private boolean shouldSkipHeightUpdate() { return mAmbientState.isOnKeyguard() && (mAmbientState.isUnlockHintRunning() - || mAmbientState.isSwipingUp() - || mAmbientState.isFlingingAfterSwipeUpOnLockscreen()); + || mAmbientState.isSwipingUp() + || mAmbientState.isFlingingAfterSwipeUpOnLockscreen()); } /** * Apply expansion fraction to the y position and height of the notifications panel. + * * @param listenerNeedsAnimation does the listener need to animate? */ private void updateStackPosition(boolean listenerNeedsAnimation) { @@ -1708,7 +1715,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) ExpandableView getChildAtPosition(float touchX, float touchY, - boolean requireMinHeight, boolean ignoreDecors) { + boolean requireMinHeight, boolean ignoreDecors) { // find the view under the pointer, accounting for GONE views final int count = getChildCount(); for (int childIdx = 0; childIdx < count; childIdx++) { @@ -1868,9 +1875,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public void dismissViewAnimated( View child, Consumer<Boolean> endRunnable, int delay, long duration) { if (child instanceof SectionHeaderView) { - ((StackScrollerDecorView) child).setContentVisible( - false /* visible */, true /* animate */, endRunnable); - return; + ((StackScrollerDecorView) child).setContentVisible( + false /* visible */, true /* animate */, endRunnable); + return; } mSwipeHelper.dismissChild( child, @@ -2032,10 +2039,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta * would cause us to exceed the provided maximum overscroll, springs back instead. - * + * <p> * This method performs the determination of whether we're exceeding the overscroll and clamps * the scroll amount if so. The actual scrolling/overscrolling happens in * {@link #onCustomOverScrolled(int, boolean)} + * * @param deltaY The (signed) number of pixels to scroll. * @param scrollY The current scroll position (absolute scrolling only). * @param scrollRangeY The maximum allowable scroll position (absolute scrolling only). @@ -2098,7 +2106,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setOverScrollAmount(float amount, boolean onTop, boolean animate, - boolean cancelAnimators) { + boolean cancelAnimators) { setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop)); } @@ -2114,7 +2122,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setOverScrollAmount(float amount, boolean onTop, boolean animate, - boolean cancelAnimators, boolean isRubberbanded) { + boolean cancelAnimators, boolean isRubberbanded) { if (cancelAnimators) { mStateAnimator.cancelOverScrollAnimators(onTop); } @@ -2123,7 +2131,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate, - boolean isRubberbanded) { + boolean isRubberbanded) { amount = Math.max(0, amount); if (animate) { mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded); @@ -2179,7 +2187,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * Scrolls to the given position, overscrolling if needed. If called during a fling and the * position exceeds the provided maximum overscroll, springs back instead. * - * @param scrollY The target scroll position. + * @param scrollY The target scroll position. * @param clampedY Whether this value was clamped by the calling method, meaning we've reached * the overscroll limit. */ @@ -2288,8 +2296,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { List<ExpandableNotificationRow> notificationChildren = row.getAttachedChildren(); - for (int childIndex = 0; childIndex < notificationChildren.size(); - childIndex++) { + int childrenSize = notificationChildren.size(); + for (int childIndex = 0; childIndex < childrenSize; childIndex++) { ExpandableNotificationRow rowChild = notificationChildren.get(childIndex); if (rowChild.getTranslationY() + rowTranslation >= translationY) { return rowChild; @@ -2365,10 +2373,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * Calculate the gap height between two different views * - * @param previous the previousView - * @param current the currentView + * @param previous the previousView + * @param current the currentView * @param visibleIndex the visible index in the list - * * @return the gap height needed before the current view */ public float calculateGapHeight( @@ -2376,7 +2383,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable ExpandableView current, int visibleIndex ) { - return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current, + return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current, previous, mAmbientState.getFractionToShade(), mAmbientState.isOnKeyguard()); } @@ -2642,15 +2649,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return mScrolledToTopOnFirstDown && !mExpandedInThisMotion && (initialVelocity > mMinimumVelocity - || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0)); + || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0)); } /** * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into * account. * - * @param qsHeight the top padding imposed by the quick settings panel - * @param animate whether to animate the change + * @param qsHeight the top padding imposed by the quick settings panel + * @param animate whether to animate the change */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) public void updateTopPadding(float qsHeight, boolean animate) { @@ -2724,21 +2731,34 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setChildTransferInProgress(boolean childTransferInProgress) { Assert.isMainThread(); mChildTransferInProgress = childTransferInProgress; } + /** + * Set the remove notification listener + * @param listener callback for notification removed + */ + public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) { + mOnNotificationRemovedListener = listener; + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @Override public void onViewRemoved(View child) { super.onViewRemoved(child); // we only call our internal methods if this is actually a removal and not just a // notification which becomes a child notification + ExpandableView expandableView = (ExpandableView) child; if (!mChildTransferInProgress) { - onViewRemovedInternal((ExpandableView) child, this); + onViewRemovedInternal(expandableView, this); + } + if (mOnNotificationRemovedListener != null) { + mOnNotificationRemovedListener.onNotificationRemoved( + expandableView, + mChildTransferInProgress); } } @@ -2998,8 +3018,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAnimateNextSectionBoundsChange = false; } mAmbientState.setLastVisibleBackgroundChild(lastChild); - // TODO: Refactor SectionManager and put the RoundnessManager there. - mController.getNotificationRoundnessManager().updateRoundedChildren(mSections); + if (!mUseRoundnessSourceTypes) { + // TODO: Refactor SectionManager and put the RoundnessManager there. + mController.getNotificationRoundnessManager().updateRoundedChildren(mSections); + } mAnimateBottomOnLayout = false; invalidate(); } @@ -3206,7 +3228,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // Only animate if we still have pinned heads up, otherwise we just have the // regular collapse animation of the lock screen || (mKeyguardBypassEnabled && onKeyguard() - && mInHeadsUpPinnedMode); + && mInHeadsUpPinnedMode); if (performDisappearAnimation && !isHeadsUp) { type = row.wasJustClicked() ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK @@ -3351,7 +3373,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable AnimationEvent animEvent = duration == null ? new AnimationEvent(child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION) : new AnimationEvent( - child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION, duration); + child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION, duration); mAnimationEvents.add(animEvent); } mChildrenChangingPositions.clear(); @@ -3442,7 +3464,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) { - return new StackScrollAlgorithm(context, this); + StackScrollAlgorithm stackScrollAlgorithm = new StackScrollAlgorithm(context, this); + stackScrollAlgorithm.useRoundnessSourceTypes(mUseRoundnessSourceTypes); + return stackScrollAlgorithm; } /** @@ -3772,7 +3796,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private void logEmptySpaceClick(MotionEvent ev, boolean isTouchBelowLastNotification, - int statusBarState, boolean touchIsClick) { + int statusBarState, boolean touchIsClick) { if (mLogger == null) { return; } @@ -3957,7 +3981,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mOnEmptySpaceClickListener = listener; } - /** @hide */ + /** + * @hide + */ @Override @ShadeViewRefactor(RefactorComponent.INPUT) public boolean performAccessibilityActionInternal(int action, Bundle arguments) { @@ -4728,7 +4754,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return touchY > mTopPadding + mStackTranslation; } - /** @hide */ + /** + * @hide + */ @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { @@ -5353,7 +5381,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return canChildBeCleared(row) && matchesSelection(row, selection); } - /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */ + /** + * Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. + */ public void setManageButtonClickListener(@Nullable OnClickListener listener) { mManageButtonClickListener = listener; if (mFooterView != null) { @@ -5418,6 +5448,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the * notification positions accordingly. + * * @param height the new wake up height * @return the overflow how much the height is further than he lowest notification */ @@ -5649,7 +5680,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * Set rounded rect clipping bounds on this view. */ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius, - int bottomRadius) { + int bottomRadius) { if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right && mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top && mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) { @@ -5710,7 +5741,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mLaunchingNotification = launching; mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null && (mLaunchAnimationParams.getStartRoundedTopClipping() > 0 - || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0); + || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0); if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) { mLaunchedNotificationClipPath.reset(); } @@ -5748,7 +5779,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mLaunchAnimationParams.getProgress(0, NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop, - mLaunchAnimationParams.getTop(), expandProgress), + mLaunchAnimationParams.getTop(), expandProgress), mRoundedRectClippingTop); float topRadius = mLaunchAnimationParams.getTopCornerRadius(); float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius(); @@ -5872,25 +5903,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public interface OnOverscrollTopChangedListener { - /** - * Notifies a listener that the overscroll has changed. - * - * @param amount the amount of overscroll, in pixels - * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an - * unrubberbanded motion to directly expand overscroll view (e.g - * expand - * QS) - */ - void onOverscrollTopChanged(float amount, boolean isRubberbanded); + /** + * Notifies a listener that the overscroll has changed. + * + * @param amount the amount of overscroll, in pixels + * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an + * unrubberbanded motion to directly expand overscroll view (e.g + * expand + * QS) + */ + void onOverscrollTopChanged(float amount, boolean isRubberbanded); - /** - * Notify a listener that the scroller wants to escape from the scrolling motion and - * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS) - * - * @param velocity The velocity that the Scroller had when over flinging - * @param open Should the fling open or close the overscroll view. - */ - void flingTopOverscroll(float velocity, boolean open); + /** + * Notify a listener that the scroller wants to escape from the scrolling motion and + * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS) + * + * @param velocity The velocity that the Scroller had when over flinging + * @param open Should the fling open or close the overscroll view. + */ + void flingTopOverscroll(float velocity, boolean open); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -6252,7 +6283,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }; - public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; } + public HeadsUpTouchHelper.Callback getHeadsUpCallback() { + return mHeadsUpCallback; + } void onGroupExpandChanged(ExpandableNotificationRow changedRow, boolean expanded) { boolean animated = mAnimationsEnabled && (mIsExpanded || changedRow.isPinned()); @@ -6357,15 +6390,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return mLastSentExpandedHeight; } - /** Enum for selecting some or all notification rows (does not included non-notif views). */ + /** + * Enum for selecting some or all notification rows (does not included non-notif views). + */ @Retention(SOURCE) @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE}) - @interface SelectedRows {} - /** All rows representing notifs. */ + @interface SelectedRows { + } + + /** + * All rows representing notifs. + */ public static final int ROWS_ALL = 0; - /** Only rows where entry.isHighPriority() is true. */ + /** + * Only rows where entry.isHighPriority() is true. + */ public static final int ROWS_HIGH_PRIORITY = 1; - /** Only rows where entry.isHighPriority() is false. */ + /** + * Only rows where entry.isHighPriority() is false. + */ public static final int ROWS_GENTLE = 2; interface ClearAllListener { @@ -6380,4 +6423,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable void onAnimationEnd( List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows); } + + /** + * + */ + public interface OnNotificationRemovedListener { + /** + * + * @param child + * @param isTransferInProgress + */ + void onNotificationRemoved(ExpandableView child, boolean isTransferInProgress); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 2d6d0a9cbeca..4bcc0b6923a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -182,10 +182,12 @@ public class NotificationStackScrollLayoutController { private NotificationStackScrollLayout mView; private boolean mFadeNotificationsOnDismiss; private NotificationSwipeHelper mSwipeHelper; - @Nullable private Boolean mHistoryEnabled; + @Nullable + private Boolean mHistoryEnabled; private int mBarState; private HeadsUpAppearanceController mHeadsUpAppearanceController; private final FeatureFlags mFeatureFlags; + private final boolean mUseRoundnessSourceTypes; private final NotificationTargetsHelper mNotificationTargetsHelper; private View mLongPressedView; @@ -391,7 +393,7 @@ public class NotificationStackScrollLayoutController { if (item != null) { Point origin = provider.getRevealAnimationOrigin(); mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); - } else { + } else { Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " + "menu item in menuItemtoExposeOnSnap. Skipping."); } @@ -420,7 +422,7 @@ public class NotificationStackScrollLayoutController { @Override public void onSnooze(StatusBarNotification sbn, - NotificationSwipeActionHelper.SnoozeOption snoozeOption) { + NotificationSwipeActionHelper.SnoozeOption snoozeOption) { mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption); } @@ -544,7 +546,7 @@ public class NotificationStackScrollLayoutController { @Override public boolean updateSwipeProgress(View animView, boolean dismissable, - float swipeProgress) { + float swipeProgress) { // Returning true prevents alpha fading. return !mFadeNotificationsOnDismiss; } @@ -584,16 +586,22 @@ public class NotificationStackScrollLayoutController { @Override public void onHeadsUpPinned(NotificationEntry entry) { - mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */); + if (!mUseRoundnessSourceTypes) { + mNotificationRoundnessManager.updateView( + entry.getRow(), + /* animate = */ false); + } } @Override public void onHeadsUpUnPinned(NotificationEntry entry) { - ExpandableNotificationRow row = entry.getRow(); - // update the roundedness posted, because we might be animating away the - // headsup soon, so no need to set the roundedness to 0 and then back to 1. - row.post(() -> mNotificationRoundnessManager.updateView(row, - true /* animate */)); + if (!mUseRoundnessSourceTypes) { + ExpandableNotificationRow row = entry.getRow(); + // update the roundedness posted, because we might be animating away the + // headsup soon, so no need to set the roundedness to 0 and then back to 1. + row.post(() -> mNotificationRoundnessManager.updateView(row, + true /* animate */)); + } } @Override @@ -603,8 +611,10 @@ public class NotificationStackScrollLayoutController { mView.setNumHeadsUp(numEntries); mView.setTopHeadsUpEntry(topEntry); generateHeadsUpAnimation(entry, isHeadsUp); - ExpandableNotificationRow row = entry.getRow(); - mNotificationRoundnessManager.updateView(row, true /* animate */); + if (!mUseRoundnessSourceTypes) { + ExpandableNotificationRow row = entry.getRow(); + mNotificationRoundnessManager.updateView(row, true /* animate */); + } } }; @@ -697,6 +707,7 @@ public class NotificationStackScrollLayoutController { mSeenNotificationsProvider = seenNotificationsProvider; mShadeController = shadeController; mFeatureFlags = featureFlags; + mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); mNotificationTargetsHelper = notificationTargetsHelper; updateResources(); } @@ -763,8 +774,10 @@ public class NotificationStackScrollLayoutController { mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mFadeNotificationsOnDismiss = mFeatureFlags.isEnabled(Flags.NOTIFICATION_DISMISSAL_FADE); - mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); - mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); + if (!mUseRoundnessSourceTypes) { + mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); + mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); + } mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation); @@ -997,9 +1010,11 @@ public class NotificationStackScrollLayoutController { Log.wtf(TAG, "isHistoryEnabled failed to initialize its value"); return false; } - mHistoryEnabled = historyEnabled = - Settings.Secure.getIntForUser(mView.getContext().getContentResolver(), - Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + mHistoryEnabled = historyEnabled = Settings.Secure.getIntForUser( + mView.getContext().getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, + 0, + UserHandle.USER_CURRENT) == 1; } return historyEnabled; } @@ -1029,7 +1044,7 @@ public class NotificationStackScrollLayoutController { } public void setOverScrollAmount(float amount, boolean onTop, boolean animate, - boolean cancelAnimators) { + boolean cancelAnimators) { mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators); } @@ -1207,7 +1222,7 @@ public class NotificationStackScrollLayoutController { /** * Update whether we should show the empty shade view ("no notifications" in the shade). - * + * <p> * When in split mode, notifications are always visible regardless of the state of the * QuickSettings panel. That being the case, empty view is always shown if the other conditions * are true. @@ -1233,7 +1248,7 @@ public class NotificationStackScrollLayoutController { /** * @return true if {@link StatusBarStateController} is in transition to the KEYGUARD - * and false otherwise. + * and false otherwise. */ private boolean isInTransitionToKeyguard() { final int currentState = mStatusBarStateController.getState(); @@ -1265,7 +1280,9 @@ public class NotificationStackScrollLayoutController { mView.setExpandedHeight(expandedHeight); } - /** Sets the QS header. Used to check if a touch is within its bounds. */ + /** + * Sets the QS header. Used to check if a touch is within its bounds. + */ public void setQsHeader(ViewGroup view) { mView.setQsHeader(view); } @@ -1328,7 +1345,7 @@ public class NotificationStackScrollLayoutController { public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationEntry entry, - boolean remoteInputActive) { + boolean remoteInputActive) { mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); entry.notifyHeightChanged(true /* needsAnimation */); updateFooter(); @@ -1459,7 +1476,7 @@ public class NotificationStackScrollLayoutController { } private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove, - @SelectedRows int selectedRows) { + @SelectedRows int selectedRows) { if (selectedRows == ROWS_ALL) { mNotifCollection.dismissAllNotifications( mLockscreenUserManager.getCurrentUserId()); @@ -1502,8 +1519,8 @@ public class NotificationStackScrollLayoutController { /** * @return the inset during the full shade transition, that needs to be added to the position - * of the quick settings edge. This is relevant for media, that is transitioning - * from the keyguard host to the quick settings one. + * of the quick settings edge. This is relevant for media, that is transitioning + * from the keyguard host to the quick settings one. */ public int getFullShadeTransitionInset() { MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer(); @@ -1517,10 +1534,10 @@ public class NotificationStackScrollLayoutController { /** * @param fraction The fraction of lockscreen to shade transition. * 0f for all other states. - * - * Once the lockscreen to shade transition completes and the shade is 100% open, - * LockscreenShadeTransitionController resets amount and fraction to 0, where they remain - * until the next lockscreen-to-shade transition. + * <p> + * Once the lockscreen to shade transition completes and the shade is 100% open, + * LockscreenShadeTransitionController resets amount and fraction to 0, where + * they remain until the next lockscreen-to-shade transition. */ public void setTransitionToFullShadeAmount(float fraction) { mView.setFractionToShade(fraction); @@ -1533,7 +1550,9 @@ public class NotificationStackScrollLayoutController { mView.setExtraTopInsetForFullShadeTransition(overScrollAmount); } - /** */ + /** + * + */ public void setWillExpand(boolean willExpand) { mView.setWillExpand(willExpand); } @@ -1549,7 +1568,7 @@ public class NotificationStackScrollLayoutController { * Set rounded rect clipping bounds on this view. */ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius, - int bottomRadius) { + int bottomRadius) { mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius); } @@ -1569,6 +1588,15 @@ public class NotificationStackScrollLayoutController { } /** + * Set the remove notification listener + * @param listener callback for notification removed + */ + public void setOnNotificationRemovedListener( + NotificationStackScrollLayout.OnNotificationRemovedListener listener) { + mView.setOnNotificationRemovedListener(listener); + } + + /** * Enum for UiEvent logged from this class */ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { @@ -1578,10 +1606,13 @@ public class NotificationStackScrollLayoutController { @UiEvent(doc = "User dismissed all silent notifications from notification panel.") DISMISS_SILENT_NOTIFICATIONS_PANEL(314); private final int mId; + NotificationPanelEvent(int id) { mId = id; } - @Override public int getId() { + + @Override + public int getId() { return mId; } @@ -1722,8 +1753,12 @@ public class NotificationStackScrollLayoutController { @Override public void bindRow(ExpandableNotificationRow row) { row.setHeadsUpAnimatingAwayListener(animatingAway -> { - mNotificationRoundnessManager.updateView(row, false); - mHeadsUpAppearanceController.updateHeader(row.getEntry()); + if (!mUseRoundnessSourceTypes) { + mNotificationRoundnessManager.updateView(row, false); + } + NotificationEntry entry = row.getEntry(); + mHeadsUpAppearanceController.updateHeader(entry); + mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry); }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index ee57411cb495..aaf9300e7cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.res.Resources; import android.graphics.Rect; @@ -34,9 +35,11 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.SwipeHelper; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -49,6 +52,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc @VisibleForTesting protected static final long COVER_MENU_DELAY = 4000; private static final String TAG = "NotificationSwipeHelper"; + private static final SourceType SWIPE_DISMISS = SourceType.from("SwipeDismiss"); private final Runnable mFalsingCheck; private View mTranslatingParentView; private View mMenuExposedView; @@ -64,13 +68,21 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef; private boolean mIsExpanded; private boolean mPulsing; + private final NotificationRoundnessManager mNotificationRoundnessManager; + private final boolean mUseRoundnessSourceTypes; NotificationSwipeHelper( - Resources resources, ViewConfiguration viewConfiguration, - FalsingManager falsingManager, FeatureFlags featureFlags, - int swipeDirection, NotificationCallback callback, - NotificationMenuRowPlugin.OnMenuEventListener menuListener) { + Resources resources, + ViewConfiguration viewConfiguration, + FalsingManager falsingManager, + FeatureFlags featureFlags, + int swipeDirection, + NotificationCallback callback, + NotificationMenuRowPlugin.OnMenuEventListener menuListener, + NotificationRoundnessManager notificationRoundnessManager) { super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags); + mNotificationRoundnessManager = notificationRoundnessManager; + mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); mMenuListener = menuListener; mCallback = callback; mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */); @@ -304,6 +316,33 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc handleMenuCoveredOrDismissed(); } + @Override + protected void prepareDismissAnimation(View view, Animator anim) { + super.prepareDismissAnimation(view, anim); + + if (mUseRoundnessSourceTypes + && view instanceof ExpandableNotificationRow + && mNotificationRoundnessManager.isClearAllInProgress()) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, SWIPE_DISMISS); + } + + @Override + public void onAnimationCancel(Animator animation) { + row.requestRoundnessReset(SWIPE_DISMISS); + } + + @Override + public void onAnimationEnd(Animator animation) { + row.requestRoundnessReset(SWIPE_DISMISS); + } + }); + } + } + @VisibleForTesting protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) { super.dismissChild(view, velocity, useAccelerateInterpolator); @@ -521,14 +560,17 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc private int mSwipeDirection; private NotificationCallback mNotificationCallback; private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener; + private NotificationRoundnessManager mNotificationRoundnessManager; @Inject Builder(@Main Resources resources, ViewConfiguration viewConfiguration, - FalsingManager falsingManager, FeatureFlags featureFlags) { + FalsingManager falsingManager, FeatureFlags featureFlags, + NotificationRoundnessManager notificationRoundnessManager) { mResources = resources; mViewConfiguration = viewConfiguration; mFalsingManager = falsingManager; mFeatureFlags = featureFlags; + mNotificationRoundnessManager = notificationRoundnessManager; } Builder setSwipeDirection(int swipeDirection) { @@ -549,7 +591,8 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc NotificationSwipeHelper build() { return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager, - mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener); + mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener, + mNotificationRoundnessManager); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt index 991a14bb9c2a..548d1a135948 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt @@ -20,8 +20,7 @@ class NotificationTargetsHelper constructor( featureFlags: FeatureFlags, ) { - private val isNotificationGroupCornerEnabled = - featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_CORNER) + private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES) /** * This method looks for views that can be rounded (and implement [Roundable]) during a @@ -48,7 +47,7 @@ constructor( if (notificationParent != null && childrenContainer != null) { // We are inside a notification group - if (!isNotificationGroupCornerEnabled) { + if (!useRoundnessSourceTypes) { return RoundableTargets(null, null, null) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index d8c68780951a..aff7b4c6c515 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -31,6 +31,7 @@ import com.android.systemui.R; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; +import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -51,6 +52,7 @@ public class StackScrollAlgorithm { private static final String TAG = "StackScrollAlgorithm"; private static final Boolean DEBUG = false; + private static final SourceType STACK_SCROLL_ALGO = SourceType.from("StackScrollAlgorithm"); private final ViewGroup mHostView; private float mPaddingBetweenElements; @@ -70,6 +72,7 @@ public class StackScrollAlgorithm { private float mQuickQsOffsetHeight; private float mSmallCornerRadius; private float mLargeCornerRadius; + private boolean mUseRoundnessSourceTypes; public StackScrollAlgorithm( Context context, @@ -129,7 +132,7 @@ public class StackScrollAlgorithm { } private void updateAlphaState(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { for (ExpandableView view : algorithmState.visibleChildren) { final ViewState viewState = view.getViewState(); @@ -229,7 +232,7 @@ public class StackScrollAlgorithm { } private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { ExpandableView v = algorithmState.visibleChildren.get(i); @@ -241,7 +244,7 @@ public class StackScrollAlgorithm { } private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState, - int speedBumpIndex) { + int speedBumpIndex) { int childCount = algorithmState.visibleChildren.size(); int belowSpeedBump = speedBumpIndex; for (int i = 0; i < childCount; i++) { @@ -268,7 +271,7 @@ public class StackScrollAlgorithm { } private void updateClipping(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { float drawStart = ambientState.isOnKeyguard() ? 0 : ambientState.getStackY() - ambientState.getScrollY(); float clipStart = 0; @@ -314,7 +317,7 @@ public class StackScrollAlgorithm { * Updates the dimmed, activated and hiding sensitive states of the children. */ private void updateDimmedActivatedHideSensitive(AmbientState ambientState, - StackScrollAlgorithmState algorithmState) { + StackScrollAlgorithmState algorithmState) { boolean dimmed = ambientState.isDimmed(); boolean hideSensitive = ambientState.isHideSensitive(); View activatedChild = ambientState.getActivatedChild(); @@ -408,7 +411,7 @@ public class StackScrollAlgorithm { } private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex, - ExpandableView v) { + ExpandableView v) { ExpandableViewState viewState = v.getViewState(); viewState.notGoneIndex = notGoneIndex; state.visibleChildren.add(v); @@ -434,7 +437,7 @@ public class StackScrollAlgorithm { * @param ambientState The current ambient state */ protected void updatePositionsForState(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { if (!ambientState.isOnKeyguard() || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) { algorithmState.mCurrentYPosition += mNotificationScrimPadding; @@ -448,7 +451,7 @@ public class StackScrollAlgorithm { } private void setLocation(ExpandableViewState expandableViewState, float currentYPosition, - int i) { + int i) { expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA; if (currentYPosition <= 0) { expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP; @@ -496,9 +499,13 @@ public class StackScrollAlgorithm { } @VisibleForTesting - void maybeUpdateHeadsUpIsVisible(ExpandableViewState viewState, boolean isShadeExpanded, - boolean mustStayOnScreen, boolean topVisible, float viewEnd, float hunMax) { - + void maybeUpdateHeadsUpIsVisible( + ExpandableViewState viewState, + boolean isShadeExpanded, + boolean mustStayOnScreen, + boolean topVisible, + float viewEnd, + float hunMax) { if (isShadeExpanded && mustStayOnScreen && topVisible) { viewState.headsUpIsVisible = viewEnd < hunMax; } @@ -676,7 +683,7 @@ public class StackScrollAlgorithm { } private void updatePulsingStates(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { View child = algorithmState.visibleChildren.get(i); @@ -693,7 +700,7 @@ public class StackScrollAlgorithm { } private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); // Move the tracked heads up into position during the appear animation, by interpolating @@ -777,7 +784,7 @@ public class StackScrollAlgorithm { */ @VisibleForTesting void clampHunToTop(float quickQsOffsetHeight, float stackTranslation, float collapsedHeight, - ExpandableViewState viewState) { + ExpandableViewState viewState) { final float newTranslation = Math.max(quickQsOffsetHeight + stackTranslation, viewState.getYTranslation()); @@ -792,7 +799,7 @@ public class StackScrollAlgorithm { // Pin HUN to bottom of expanded QS // while the rest of notifications are scrolled offscreen. private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row, - ExpandableViewState childState) { + ExpandableViewState childState) { float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation(); final float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding() + ambientState.getStackTranslation(); @@ -807,14 +814,19 @@ public class StackScrollAlgorithm { // Animate pinned HUN bottom corners to and from original roundness. final float originalCornerRadius = row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius); - final float roundness = computeCornerRoundnessForPinnedHun(mHostView.getHeight(), + final float bottomValue = computeCornerRoundnessForPinnedHun(mHostView.getHeight(), ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius); - row.requestBottomRoundness(roundness, /* animate = */ false, SourceType.OnScroll); + if (mUseRoundnessSourceTypes) { + row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO); + row.addOnDetachResetRoundness(STACK_SCROLL_ALGO); + } else { + row.requestBottomRoundness(bottomValue, LegacySourceType.OnScroll); + } } @VisibleForTesting float computeCornerRoundnessForPinnedHun(float hostViewHeight, float stackY, - float viewMaxHeight, float originalCornerRadius) { + float viewMaxHeight, float originalCornerRadius) { // Compute y where corner roundness should be in its original unpinned state. // We use view max height because the pinned collapsed HUN expands to max height @@ -844,7 +856,7 @@ public class StackScrollAlgorithm { * @param ambientState The ambient state of the algorithm */ private void updateZValuesForState(StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { + AmbientState ambientState) { int childCount = algorithmState.visibleChildren.size(); float childrenOnTop = 0.0f; @@ -876,9 +888,9 @@ public class StackScrollAlgorithm { * previous HUNs whose Z positions are greater than 0. */ protected float updateChildZValue(int i, float childrenOnTop, - StackScrollAlgorithmState algorithmState, - AmbientState ambientState, - boolean isTopHun) { + StackScrollAlgorithmState algorithmState, + AmbientState ambientState, + boolean isTopHun) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableViewState childViewState = child.getViewState(); float baseZ = ambientState.getBaseZHeight(); @@ -950,6 +962,14 @@ public class StackScrollAlgorithm { this.mIsExpanded = isExpanded; } + /** + * Enable the support for rounded corner based on the SourceType + * @param enabled true if is supported + */ + public void useRoundnessSourceTypes(boolean enabled) { + mUseRoundnessSourceTypes = enabled; + } + public static class StackScrollAlgorithmState { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 484441a1e76b..c217ab3b42e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -19,11 +19,14 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_FRAME_VIEW; import android.graphics.Rect; +import android.util.MathUtils; import android.view.View; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.NotificationPanelViewController; @@ -32,8 +35,10 @@ import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; import com.android.systemui.statusbar.policy.Clock; @@ -51,6 +56,7 @@ import javax.inject.Named; /** * Controls the appearance of heads up notifications in the icon area and the header itself. + * It also controls the roundness of the heads up notifications and the pulsing notifications. */ @StatusBarFragmentScope public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView> @@ -59,12 +65,17 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar NotificationWakeUpCoordinator.WakeUpListener { public static final int CONTENT_FADE_DURATION = 110; public static final int CONTENT_FADE_DELAY = 100; + + private static final SourceType HEADS_UP = SourceType.from("HeadsUp"); + private static final SourceType PULSING = SourceType.from("Pulsing"); private final NotificationIconAreaController mNotificationIconAreaController; private final HeadsUpManagerPhone mHeadsUpManager; private final NotificationStackScrollLayoutController mStackScrollerController; private final DarkIconDispatcher mDarkIconDispatcher; private final NotificationPanelViewController mNotificationPanelViewController; + private final NotificationRoundnessManager mNotificationRoundnessManager; + private final boolean mUseRoundnessSourceTypes; private final Consumer<ExpandableNotificationRow> mSetTrackingHeadsUp = this::setTrackingHeadsUp; private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction; @@ -105,11 +116,15 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar CommandQueue commandQueue, NotificationStackScrollLayoutController stackScrollerController, NotificationPanelViewController notificationPanelViewController, + NotificationRoundnessManager notificationRoundnessManager, + FeatureFlags featureFlags, HeadsUpStatusBarView headsUpStatusBarView, Clock clockView, @Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) { super(headsUpStatusBarView); mNotificationIconAreaController = notificationIconAreaController; + mNotificationRoundnessManager = notificationRoundnessManager; + mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES); mHeadsUpManager = headsUpManager; // We may be mid-HUN-expansion when this controller is re-created (for example, if the user @@ -179,6 +194,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar public void onHeadsUpPinned(NotificationEntry entry) { updateTopEntry(); updateHeader(entry); + updateHeadsUpAndPulsingRoundness(entry); } private void updateTopEntry() { @@ -316,6 +332,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar public void onHeadsUpUnPinned(NotificationEntry entry) { updateTopEntry(); updateHeader(entry); + updateHeadsUpAndPulsingRoundness(entry); } public void setAppearFraction(float expandedHeight, float appearFraction) { @@ -346,7 +363,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar ExpandableNotificationRow previousTracked = mTrackedChild; mTrackedChild = trackedChild; if (previousTracked != null) { - updateHeader(previousTracked.getEntry()); + NotificationEntry entry = previousTracked.getEntry(); + updateHeader(entry); + updateHeadsUpAndPulsingRoundness(entry); } } @@ -357,6 +376,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar private void updateHeadsUpHeaders() { mHeadsUpManager.getAllEntries().forEach(entry -> { updateHeader(entry); + updateHeadsUpAndPulsingRoundness(entry); }); } @@ -370,6 +390,31 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar row.setHeaderVisibleAmount(headerVisibleAmount); } + /** + * Update the HeadsUp and the Pulsing roundness based on current state + * @param entry target notification + */ + public void updateHeadsUpAndPulsingRoundness(NotificationEntry entry) { + if (mUseRoundnessSourceTypes) { + ExpandableNotificationRow row = entry.getRow(); + boolean isTrackedChild = row == mTrackedChild; + if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) { + float roundness = MathUtils.saturate(1f - mAppearFraction); + row.requestRoundness(roundness, roundness, HEADS_UP); + } else { + row.requestRoundnessReset(HEADS_UP); + } + if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) { + if (row.showingPulsing()) { + row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING); + } else { + row.requestRoundnessReset(PULSING); + } + } + } + } + + @Override public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { mView.onDarkChanged(areas, darkIntensity, tint); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt new file mode 100644 index 000000000000..89faa239c5a2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt @@ -0,0 +1,164 @@ +package com.android.systemui.statusbar.notification + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(JUnit4::class) +class RoundableTest : SysuiTestCase() { + val targetView: View = mock() + val roundable = FakeRoundable(targetView) + + @Test + fun defaultConfig_shouldNotHaveRoundedCorner() { + // the expected default value for the roundness is top = 0f, bottom = 0f + assertEquals(0f, roundable.roundableState.topRoundness) + assertEquals(0f, roundable.roundableState.bottomRoundness) + assertEquals(false, roundable.hasRoundedCorner()) + } + + @Test + fun applyRoundnessAndInvalidate_should_invalidate_targetView() { + roundable.applyRoundnessAndInvalidate() + + verify(targetView, times(1)).invalidate() + } + + @Test + fun requestTopRoundness_update_and_invalidate_targetView() { + roundable.requestTopRoundness(value = 1f, sourceType = SOURCE1) + + assertEquals(1f, roundable.roundableState.topRoundness) + verify(targetView, times(1)).invalidate() + } + + @Test + fun requestBottomRoundness_update_and_invalidate_targetView() { + roundable.requestBottomRoundness(value = 1f, sourceType = SOURCE1) + + assertEquals(1f, roundable.roundableState.bottomRoundness) + verify(targetView, times(1)).invalidate() + } + + @Test + fun requestRoundness_update_and_invalidate_targetView() { + roundable.requestRoundness(top = 1f, bottom = 1f, sourceType = SOURCE1) + + assertEquals(1f, roundable.roundableState.topRoundness) + assertEquals(1f, roundable.roundableState.bottomRoundness) + verify(targetView, atLeastOnce()).invalidate() + } + + @Test + fun requestRoundnessReset_update_and_invalidate_targetView() { + roundable.requestRoundness(1f, 1f, SOURCE1) + assertEquals(1f, roundable.roundableState.topRoundness) + assertEquals(1f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundnessReset(sourceType = SOURCE1) + + assertEquals(0f, roundable.roundableState.topRoundness) + assertEquals(0f, roundable.roundableState.bottomRoundness) + verify(targetView, atLeastOnce()).invalidate() + } + + @Test + fun hasRoundedCorner_return_true_ifRoundnessIsGreaterThenZero() { + roundable.requestRoundness(top = 1f, bottom = 1f, sourceType = SOURCE1) + assertEquals(true, roundable.hasRoundedCorner()) + + roundable.requestRoundness(top = 1f, bottom = 0f, sourceType = SOURCE1) + assertEquals(true, roundable.hasRoundedCorner()) + + roundable.requestRoundness(top = 0f, bottom = 1f, sourceType = SOURCE1) + assertEquals(true, roundable.hasRoundedCorner()) + + roundable.requestRoundness(top = 0f, bottom = 0f, sourceType = SOURCE1) + assertEquals(false, roundable.hasRoundedCorner()) + } + + @Test + fun roundness_take_maxValue_onMultipleSources_first_lower() { + roundable.requestRoundness(0.1f, 0.1f, SOURCE1) + assertEquals(0.1f, roundable.roundableState.topRoundness) + assertEquals(0.1f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundness(0.2f, 0.2f, SOURCE2) + // SOURCE1 has 0.1f - SOURCE2 has 0.2f + assertEquals(0.2f, roundable.roundableState.topRoundness) + assertEquals(0.2f, roundable.roundableState.bottomRoundness) + } + + @Test + fun roundness_take_maxValue_onMultipleSources_first_higher() { + roundable.requestRoundness(0.5f, 0.5f, SOURCE1) + assertEquals(0.5f, roundable.roundableState.topRoundness) + assertEquals(0.5f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundness(0.1f, 0.1f, SOURCE2) + // SOURCE1 has 0.5f - SOURCE2 has 0.1f + assertEquals(0.5f, roundable.roundableState.topRoundness) + assertEquals(0.5f, roundable.roundableState.bottomRoundness) + } + + @Test + fun roundness_take_maxValue_onMultipleSources_first_higher_second_step() { + roundable.requestRoundness(0.1f, 0.1f, SOURCE1) + assertEquals(0.1f, roundable.roundableState.topRoundness) + assertEquals(0.1f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundness(0.2f, 0.2f, SOURCE2) + // SOURCE1 has 0.1f - SOURCE2 has 0.2f + assertEquals(0.2f, roundable.roundableState.topRoundness) + assertEquals(0.2f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundness(0.3f, 0.3f, SOURCE1) + // SOURCE1 has 0.3f - SOURCE2 has 0.2f + assertEquals(0.3f, roundable.roundableState.topRoundness) + assertEquals(0.3f, roundable.roundableState.bottomRoundness) + } + + @Test + fun roundness_take_maxValue_onMultipleSources_first_lower_second_step() { + roundable.requestRoundness(0.5f, 0.5f, SOURCE1) + assertEquals(0.5f, roundable.roundableState.topRoundness) + assertEquals(0.5f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundness(0.2f, 0.2f, SOURCE2) + // SOURCE1 has 0.5f - SOURCE2 has 0.2f + assertEquals(0.5f, roundable.roundableState.topRoundness) + assertEquals(0.5f, roundable.roundableState.bottomRoundness) + + roundable.requestRoundness(0.1f, 0.1f, SOURCE1) + // SOURCE1 has 0.1f - SOURCE2 has 0.2f + assertEquals(0.2f, roundable.roundableState.topRoundness) + assertEquals(0.2f, roundable.roundableState.bottomRoundness) + } + + class FakeRoundable( + targetView: View, + radius: Float = MAX_RADIUS, + ) : Roundable { + override val roundableState = + RoundableState( + targetView = targetView, + roundable = this, + maxRadius = radius, + ) + } + + companion object { + private const val MAX_RADIUS = 10f + private val SOURCE1 = SourceType.from("Source1") + private val SOURCE2 = SourceType.from("Source2") + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 088d1654d1f0..59d4720fdb6d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -213,8 +213,7 @@ public class NotificationTestHelper { SourceType sourceType ) throws Exception { ExpandableNotificationRow row = createRow(); - row.requestTopRoundness(topRoundness, false, sourceType); - row.requestBottomRoundness(bottomRoundness, /*animate = */ false, sourceType); + row.requestRoundness(topRoundness, bottomRoundness, sourceType, /*animate = */ false); assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f); assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f); return row; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index 438b528944be..fd1944e7478d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -25,7 +25,7 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.SourceType; +import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -158,7 +158,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness( /* topRoundness = */ 1f, /* bottomRoundness = */ 1f, - /* sourceType = */ SourceType.OnScroll); + /* sourceType = */ LegacySourceType.OnScroll); mChildrenContainer.addNotification(row, 0); @@ -171,11 +171,11 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness( /* topRoundness = */ 1f, /* bottomRoundness = */ 1f, - /* sourceType = */ SourceType.DefaultValue); + /* sourceType = */ LegacySourceType.DefaultValue); ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness( /* topRoundness = */ 1f, /* bottomRoundness = */ 1f, - /* sourceType = */ SourceType.OnDismissAnimation); + /* sourceType = */ LegacySourceType.OnDismissAnimation); mChildrenContainer.addNotification(row1, 0); mChildrenContainer.addNotification(row2, 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java index 8c8b64424814..bd0a556ac670 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java @@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger; @@ -73,7 +74,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase { mRoundnessManager = new NotificationRoundnessManager( new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext), mLogger, - mock(DumpManager.class)); + mock(DumpManager.class), + mock(FeatureFlags.class)); allowTestableLooperAsMainThread(); NotificationTestHelper testHelper = new NotificationTestHelper( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index ecc02246ea1d..30da08ebfe7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -30,6 +30,7 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; @@ -59,10 +60,12 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Mock private KeyguardMediaController mKeyguardMediaController; @Mock private NotificationSectionsFeatureManager mSectionsFeatureManager; @Mock private MediaContainerController mMediaContainerController; + @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private SectionHeaderController mIncomingHeaderController; @Mock private SectionHeaderController mPeopleHeaderController; @Mock private SectionHeaderController mAlertingHeaderController; @Mock private SectionHeaderController mSilentHeaderController; + @Mock private FeatureFlags mFeatureFlag; private NotificationSectionsManager mSectionsManager; @@ -89,10 +92,12 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { mKeyguardMediaController, mSectionsFeatureManager, mMediaContainerController, + mNotificationRoundnessManager, mIncomingHeaderController, mPeopleHeaderController, mAlertingHeaderController, - mSilentHeaderController + mSilentHeaderController, + mFeatureFlag ); // Required in order for the header inflation to work properly when(mNssl.generateLayoutParams(any(AttributeSet.class))) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index bda233611158..9d759c4b6016 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -9,7 +9,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.SourceType +import com.android.systemui.statusbar.notification.LegacySourceType import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.row.NotificationTestHelper @@ -314,9 +314,9 @@ class NotificationShelfTest : SysuiTestCase() { val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( /* topRoundness = */ 1f, /* bottomRoundness = */ 1f, - /* sourceType = */ SourceType.OnScroll) + /* sourceType = */ LegacySourceType.OnScroll) - NotificationShelf.resetOnScrollRoundness(row) + NotificationShelf.resetLegacyOnScrollRoundness(row) assertEquals(0f, row.topRoundness) assertEquals(0f, row.bottomRoundness) @@ -327,14 +327,14 @@ class NotificationShelfTest : SysuiTestCase() { val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( /* topRoundness = */ 1f, /* bottomRoundness = */ 1f, - /* sourceType = */ SourceType.DefaultValue) + /* sourceType = */ LegacySourceType.DefaultValue) val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( /* topRoundness = */ 1f, /* bottomRoundness = */ 1f, - /* sourceType = */ SourceType.OnDismissAnimation) + /* sourceType = */ LegacySourceType.OnDismissAnimation) - NotificationShelf.resetOnScrollRoundness(row1) - NotificationShelf.resetOnScrollRoundness(row2) + NotificationShelf.resetLegacyOnScrollRoundness(row1) + NotificationShelf.resetLegacyOnScrollRoundness(row2) assertEquals(1f, row1.topRoundness) assertEquals(1f, row1.bottomRoundness) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 4ea1c7100557..680a32375988 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -74,6 +74,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { private NotificationSwipeHelper mSwipeHelper; private NotificationSwipeHelper.NotificationCallback mCallback; private NotificationMenuRowPlugin.OnMenuEventListener mListener; + private NotificationRoundnessManager mNotificationRoundnessManager; private View mView; private MotionEvent mEvent; private NotificationMenuRowPlugin mMenuRow; @@ -92,10 +93,17 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { public void setUp() throws Exception { mCallback = mock(NotificationSwipeHelper.NotificationCallback.class); mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class); + mNotificationRoundnessManager = mock(NotificationRoundnessManager.class); mFeatureFlags = mock(FeatureFlags.class); mSwipeHelper = spy(new NotificationSwipeHelper( - mContext.getResources(), ViewConfiguration.get(mContext), - new FalsingManagerFake(), mFeatureFlags, SwipeHelper.X, mCallback, mListener)); + mContext.getResources(), + ViewConfiguration.get(mContext), + new FalsingManagerFake(), + mFeatureFlags, + SwipeHelper.X, + mCallback, + mListener, + mNotificationRoundnessManager)); mView = mock(View.class); mEvent = mock(MotionEvent.class); mMenuRow = mock(NotificationMenuRowPlugin.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt index a2e92305bf27..81a3f1245efe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt @@ -35,7 +35,7 @@ class NotificationTargetsHelperTest : SysuiTestCase() { ) = NotificationTargetsHelper( FakeFeatureFlags().apply { - set(Flags.NOTIFICATION_GROUP_CORNER, notificationGroupCorner) + set(Flags.USE_ROUNDNESS_SOURCETYPES, notificationGroupCorner) } ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index 103b7b4268de..9727b6c5eb83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -32,6 +32,7 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.NotificationPanelViewController; @@ -40,6 +41,7 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -71,6 +73,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private NotificationWakeUpCoordinator mWakeUpCoordinator; private KeyguardStateController mKeyguardStateController; private CommandQueue mCommandQueue; + private NotificationRoundnessManager mNotificationRoundnessManager; + private FeatureFlags mFeatureFlag; @Before public void setUp() throws Exception { @@ -89,6 +93,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class); mKeyguardStateController = mock(KeyguardStateController.class); mCommandQueue = mock(CommandQueue.class); + mNotificationRoundnessManager = mock(NotificationRoundnessManager.class); + mFeatureFlag = mock(FeatureFlags.class); mHeadsUpAppearanceController = new HeadsUpAppearanceController( mock(NotificationIconAreaController.class), mHeadsUpManager, @@ -100,6 +106,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mCommandQueue, mStackScrollerController, mPanelView, + mNotificationRoundnessManager, + mFeatureFlag, mHeadsUpStatusBarView, new Clock(mContext, null), Optional.of(mOperatorNameView)); @@ -182,6 +190,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mCommandQueue, mStackScrollerController, mPanelView, + mNotificationRoundnessManager, + mFeatureFlag, mHeadsUpStatusBarView, new Clock(mContext, null), Optional.empty()); |