Merge "Define clear ownership for roundness SourceType" into tm-qpr-dev am: 446cb95aa1

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20494945

Change-Id: I71376cbc2b732ba8fcbe817171984793cd72b3ce
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Omar Miatello
2022-12-06 17:48:15 +00:00
committed by Automerger Merge Worker
27 changed files with 1072 additions and 302 deletions

View File

@@ -83,8 +83,7 @@ object Flags {
val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true) val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)
@JvmField @JvmField
val NOTIFICATION_GROUP_CORNER = val USE_ROUNDNESS_SOURCETYPES = unreleasedFlag(116, "use_roundness_sourcetype", teamfood = true)
unreleasedFlag(116, "notification_group_corner", teamfood = true)
// TODO(b/259217907) // TODO(b/259217907)
@JvmField @JvmField

View File

@@ -39,6 +39,7 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; 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.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; 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. // the next icon has translated out of the way, to avoid overlapping.
private static final Interpolator ICON_ALPHA_INTERPOLATOR = private static final Interpolator ICON_ALPHA_INTERPOLATOR =
new PathInterpolator(0.6f, 0f, 0.6f, 0f); 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 NotificationIconContainer mShelfIcons;
private int[] mTmp = new int[2]; private int[] mTmp = new int[2];
@@ -112,19 +115,24 @@ public class NotificationShelf extends ActivatableNotificationView implements
setClipChildren(false); setClipChildren(false);
setClipToPadding(false); setClipToPadding(false);
mShelfIcons.setIsStaticLayout(false); mShelfIcons.setIsStaticLayout(false);
requestBottomRoundness(1.0f, /* animate = */ false, SourceType.DefaultValue); requestRoundness(/* top = */ 1f, /* bottom = */ 1f, BASE_VALUE, /* animate = */ false);
requestTopRoundness(1f, false, SourceType.DefaultValue);
// Setting this to first in section to get the clipping to the top roundness correct. This if (!mUseRoundnessSourceTypes) {
// value determines the way we are clipping to the top roundness of the overall shade // Setting this to first in section to get the clipping to the top roundness correct.
setFirstInSection(true); // This value determines the way we are clipping to the top roundness of the overall
// shade
setFirstInSection(true);
}
updateResources(); updateResources();
} }
public void bind(AmbientState ambientState, public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) { NotificationStackScrollLayoutController hostLayoutController) {
mAmbientState = ambientState; mAmbientState = ambientState;
mHostLayoutController = hostLayoutController; mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
child.requestRoundnessReset(SHELF_SCROLL);
});
} }
private void updateResources() { private void updateResources() {
@@ -185,9 +193,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
+ " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')'; + " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')';
} }
/** Update the state of the shelf. */ /**
* Update the state of the shelf.
*/
public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState, public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
ExpandableView lastView = ambientState.getLastVisibleBackgroundChild(); ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
ShelfState viewState = (ShelfState) getViewState(); ShelfState viewState = (ShelfState) getViewState();
if (mShowNotificationShelf && lastView != null) { if (mShowNotificationShelf && lastView != null) {
@@ -246,7 +256,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
/** /**
* @param fractionToShade Fraction of lockscreen to shade transition * @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 @VisibleForTesting
public void updateActualWidth(float fractionToShade, float shortestWidth) { 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 localX Click x from left of screen
* @param slop Margin of error within which we count x for valid click * @param slop Margin of error within which we count x for valid click
* @param left Left of shelf, from left of screen * @param left Left of shelf, from left of screen
* @param right Right of shelf, from left of screen * @param right Right of shelf, from left of screen
* @return Whether click x was in view * @return Whether click x was in view
*/ */
@VisibleForTesting @VisibleForTesting
@@ -293,8 +303,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
/** /**
* @param localY Click y from top of shelf * @param localY Click y from top of shelf
* @param slop Margin of error within which we count y for valid click * @param slop Margin of error within which we count y for valid click
* @param top Top of shelf * @param top Top of shelf
* @param bottom Height of shelf * @param bottom Height of shelf
* @return Whether click y was in view * @return Whether click y was in view
*/ */
@@ -306,7 +316,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
/** /**
* @param localX Click x * @param localX Click x
* @param localY Click y * @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 * @return Whether this click was on the visible (non-clipped) part of the shelf
*/ */
@Override @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) { float shelfStart) {
final boolean isUnlockedHeadsUp = !mAmbientState.isOnKeyguard() final boolean isUnlockedHeadsUp = !mAmbientState.isOnKeyguard()
&& !mAmbientState.isShadeExpanded() && !mAmbientState.isShadeExpanded()
&& anv instanceof ExpandableNotificationRow && anv instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) anv).isHeadsUp(); && anv.isHeadsUp();
final boolean isHunGoingToShade = mAmbientState.isShadeExpanded() final boolean isHunGoingToShade = mAmbientState.isShadeExpanded()
&& anv == mAmbientState.getTrackedHeadsUpRow(); && anv == mAmbientState.getTrackedHeadsUpRow();
@@ -506,41 +518,40 @@ public class NotificationShelf extends ActivatableNotificationView implements
* mAmbientState.getExpansionFraction(); * mAmbientState.getExpansionFraction();
final float cornerAnimationTop = shelfStart - cornerAnimationDistance; final float cornerAnimationTop = shelfStart - cornerAnimationDistance;
if (viewEnd >= cornerAnimationTop) { final SourceType sourceType;
// Round bottom corners within animation bounds if (mUseRoundnessSourceTypes) {
final float changeFraction = MathUtils.saturate( sourceType = SHELF_SCROLL;
(viewEnd - cornerAnimationTop) / cornerAnimationDistance); } else {
anv.requestBottomRoundness( sourceType = LegacySourceType.OnScroll;
/* 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);
} }
if (viewStart >= cornerAnimationTop) { final float topValue;
if (!mUseRoundnessSourceTypes && anv.isFirstInSection()) {
topValue = 1f;
} else if (viewStart >= cornerAnimationTop) {
// Round top corners within animation bounds // Round top corners within animation bounds
final float changeFraction = MathUtils.saturate( topValue = MathUtils.saturate(
(viewStart - cornerAnimationTop) / cornerAnimationDistance); (viewStart - cornerAnimationTop) / cornerAnimationDistance);
anv.requestTopRoundness( } else {
/* value = */ anv.isFirstInSection() ? 1f : changeFraction,
/* animate = */ false,
SourceType.OnScroll);
} else if (viewStart < cornerAnimationTop) {
// Fast scroll skips frames and leaves corners with unfinished rounding. // Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds. // Reset top and bottom corners outside of animation bounds.
anv.requestTopRoundness( topValue = 0f;
/* value = */ anv.isFirstInSection() ? 1f : 0f,
/* animate = */ false,
SourceType.OnScroll);
} }
anv.requestTopRoundness(topValue, sourceType, /* animate = */ false);
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.
bottomValue = 0f;
}
anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false);
} }
/** /**
@@ -626,10 +637,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
/** /**
* Update the clipping of this view. * Update the clipping of this view.
*
* @return the amount that our own top should be clipped * @return the amount that our own top should be clipped
*/ */
private int updateNotificationClipHeight(ExpandableView view, private int updateNotificationClipHeight(ExpandableView view,
float notificationClipEnd, int childIndex) { float notificationClipEnd, int childIndex) {
float viewEnd = view.getTranslationY() + view.getActualHeight(); float viewEnd = view.getTranslationY() + view.getActualHeight();
boolean isPinned = (view.isPinned() || view.isHeadsUpAnimatingAway()) boolean isPinned = (view.isPinned() || view.isHeadsUpAnimatingAway())
&& !mAmbientState.isDozingAndNotPulsing(view); && !mAmbientState.isDozingAndNotPulsing(view);
@@ -657,7 +669,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
@Override @Override
public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd, public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
int outlineTranslation) { int outlineTranslation) {
if (!mHasItemsInStableShelf) { if (!mHasItemsInStableShelf) {
shadowIntensity = 0.0f; shadowIntensity = 0.0f;
} }
@@ -665,18 +677,24 @@ public class NotificationShelf extends ActivatableNotificationView implements
} }
/** /**
* @param i Index of the view in the host layout. * @param i Index of the view in the host layout.
* @param view The current ExpandableView. * @param view The current ExpandableView.
* @param scrollingFast Whether we are scrolling fast. * @param scrollingFast Whether we are scrolling fast.
* @param expandingAnimated Whether we are expanding a notification. * @param expandingAnimated Whether we are expanding a notification.
* @param isLastChild Whether this is the last view. * @param isLastChild Whether this is the last view.
* @param shelfClipStart The point at which notifications start getting clipped by the shelf. * @param shelfClipStart The point at which notifications start getting clipped by the shelf.
* @return The amount how much this notification is in 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 @VisibleForTesting
public float getAmountInShelf(int i, ExpandableView view, boolean scrollingFast, public float getAmountInShelf(
boolean expandingAnimated, boolean isLastChild, float shelfClipStart) { int i,
ExpandableView view,
boolean scrollingFast,
boolean expandingAnimated,
boolean isLastChild,
float shelfClipStart
) {
// Let's calculate how much the view is in the shelf // Let's calculate how much the view is in the shelf
float viewStart = view.getTranslationY(); float viewStart = view.getTranslationY();
@@ -755,8 +773,13 @@ public class NotificationShelf extends ActivatableNotificationView implements
return start; return start;
} }
private void updateIconPositioning(ExpandableView view, float iconTransitionAmount, private void updateIconPositioning(
boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) { ExpandableView view,
float iconTransitionAmount,
boolean scrollingFast,
boolean expandingAnimated,
boolean isLastChild
) {
StatusBarIconView icon = view.getShelfIcon(); StatusBarIconView icon = view.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon); NotificationIconContainer.IconState iconState = getIconState(icon);
if (iconState == null) { if (iconState == null) {
@@ -817,7 +840,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
|| row.showingPulsing() || row.showingPulsing()
|| row.getTranslationZ() > mAmbientState.getBaseZHeight(); || row.getTranslationZ() > mAmbientState.getBaseZHeight();
iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount; iconState.iconAppearAmount = iconState.hidden ? 0f : transitionAmount;
// Fade in icons at shelf start // Fade in icons at shelf start
// This is important for conversation icons, which are badged and need x reset // 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() { private float getFullyClosedTranslation() {
return - (getIntrinsicHeight() - mStatusBarHeight) / 2; return -(getIntrinsicHeight() - mStatusBarHeight) / 2;
} }
@Override @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 * @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() { public boolean hasItemsInStableShelf() {
return mHasItemsInStableShelf; return mHasItemsInStableShelf;
@@ -962,7 +985,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
@Override @Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 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(); updateRelativeOffset();
} }
@@ -981,12 +1004,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
/** /**
* This method resets the OnScroll roundness of a view to 0f * 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} * Note: This should be the only class that handles roundness {@code SourceType.OnScroll}
*/ */
public static void resetOnScrollRoundness(ExpandableView expandableView) { public static void resetLegacyOnScrollRoundness(ExpandableView expandableView) {
expandableView.requestTopRoundness(0f, false, SourceType.OnScroll); expandableView.requestRoundnessReset(LegacySourceType.OnScroll);
expandableView.requestBottomRoundness(0f, false, SourceType.OnScroll);
} }
public class ShelfState extends ExpandableViewState { public class ShelfState extends ExpandableViewState {

View File

@@ -18,6 +18,8 @@ package com.android.systemui.statusbar;
import android.view.View; 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.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
@@ -42,14 +44,17 @@ public class NotificationShelfController {
private AmbientState mAmbientState; private AmbientState mAmbientState;
@Inject @Inject
public NotificationShelfController(NotificationShelf notificationShelf, public NotificationShelfController(
NotificationShelf notificationShelf,
ActivatableNotificationViewController activatableNotificationViewController, ActivatableNotificationViewController activatableNotificationViewController,
KeyguardBypassController keyguardBypassController, KeyguardBypassController keyguardBypassController,
SysuiStatusBarStateController statusBarStateController) { SysuiStatusBarStateController statusBarStateController,
FeatureFlags featureFlags) {
mView = notificationShelf; mView = notificationShelf;
mActivatableNotificationViewController = activatableNotificationViewController; mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController; mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController; mStatusBarStateController = statusBarStateController;
mView.useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override @Override
public void onViewAttachedToWindow(View v) { public void onViewAttachedToWindow(View v) {
@@ -88,7 +93,7 @@ public class NotificationShelfController {
public @View.Visibility int getVisibility() { public @View.Visibility int getVisibility() {
return mView.getVisibility(); return mView.getVisibility();
}; }
public void setCollapsedIcons(NotificationIconContainer notificationIcons) { public void setCollapsedIcons(NotificationIconContainer notificationIcons) {
mView.setCollapsedIcons(notificationIcons); mView.setCollapsedIcons(notificationIcons);

View File

@@ -74,8 +74,8 @@ interface Roundable {
@JvmDefault @JvmDefault
fun requestTopRoundness( fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float, @FloatRange(from = 0.0, to = 1.0) value: Float,
animate: Boolean,
sourceType: SourceType, sourceType: SourceType,
animate: Boolean,
): Boolean { ): Boolean {
val roundnessMap = roundableState.topRoundnessMap val roundnessMap = roundableState.topRoundnessMap
val lastValue = roundnessMap.values.maxOrNull() ?: 0f val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -104,6 +104,30 @@ interface Roundable {
return false return false
} }
/**
* 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]. * Request the bottom roundness [value] for a specific [sourceType].
* *
@@ -119,8 +143,8 @@ interface Roundable {
@JvmDefault @JvmDefault
fun requestBottomRoundness( fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float, @FloatRange(from = 0.0, to = 1.0) value: Float,
animate: Boolean,
sourceType: SourceType, sourceType: SourceType,
animate: Boolean,
): Boolean { ): Boolean {
val roundnessMap = roundableState.bottomRoundnessMap val roundnessMap = roundableState.bottomRoundnessMap
val lastValue = roundnessMap.values.maxOrNull() ?: 0f val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -149,9 +173,101 @@ interface Roundable {
return false 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]. */ /** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
@JvmDefault @JvmDefault
fun applyRoundness() { fun applyRoundnessAndInvalidate() {
roundableState.targetView.invalidate() roundableState.targetView.invalidate()
} }
@@ -227,7 +343,7 @@ class RoundableState(
/** Set the current top roundness */ /** Set the current top roundness */
internal fun setTopRoundness( internal fun setTopRoundness(
value: Float, value: Float,
animated: Boolean = targetView.isShown, animated: Boolean,
) { ) {
PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated) PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated)
} }
@@ -235,11 +351,19 @@ class RoundableState(
/** Set the current bottom roundness */ /** Set the current bottom roundness */
internal fun setBottomRoundness( internal fun setBottomRoundness(
value: Float, value: Float,
animated: Boolean = targetView.isShown, animated: Boolean,
) { ) {
PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated) 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 { companion object {
private val DURATION: AnimationProperties = private val DURATION: AnimationProperties =
AnimationProperties() AnimationProperties()
@@ -252,7 +376,7 @@ class RoundableState(
override fun setValue(view: View, value: Float) { override fun setValue(view: View, value: Float) {
roundable.roundableState.topRoundness = value roundable.roundableState.topRoundness = value
roundable.applyRoundness() roundable.applyRoundnessAndInvalidate()
} }
}, },
R.id.top_roundess_animator_tag, R.id.top_roundess_animator_tag,
@@ -267,7 +391,7 @@ class RoundableState(
override fun setValue(view: View, value: Float) { override fun setValue(view: View, value: Float) {
roundable.roundableState.bottomRoundness = value roundable.roundableState.bottomRoundness = value
roundable.applyRoundness() roundable.applyRoundnessAndInvalidate()
} }
}, },
R.id.bottom_roundess_animator_tag, 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, DefaultValue,
OnDismissAnimation, OnDismissAnimation,
OnScroll, OnScroll,

View File

@@ -39,9 +39,13 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils; 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.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 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} * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
* to implement dimming/activating on Keyguard for the double-tap gesture * 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); = new PathInterpolator(0.6f, 0, 0.5f, 1);
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
= new PathInterpolator(0, 0, 0.5f, 1); = new PathInterpolator(0, 0, 0.5f, 1);
private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>();
private int mTintedRippleColor; private int mTintedRippleColor;
private int mNormalRippleColor; private int mNormalRippleColor;
private Gefingerpoken mTouchHandler; private Gefingerpoken mTouchHandler;
@@ -134,6 +139,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private boolean mDismissed; private boolean mDismissed;
private boolean mRefocusOnDismiss; private boolean mRefocusOnDismiss;
private AccessibilityManager mAccessibilityManager; private AccessibilityManager mAccessibilityManager;
protected boolean mUseRoundnessSourceTypes;
public ActivatableNotificationView(Context context, AttributeSet attrs) { public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@@ -613,9 +619,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
protected void resetAllContentAlphas() {} protected void resetAllContentAlphas() {}
@Override @Override
public void applyRoundness() { public void applyRoundnessAndInvalidate() {
super.applyRoundness();
applyBackgroundRoundness(getTopCornerRadius(), getBottomCornerRadius()); applyBackgroundRoundness(getTopCornerRadius(), getBottomCornerRadius());
super.applyRoundnessAndInvalidate();
} }
@Override @Override
@@ -775,6 +781,33 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mAccessibilityManager = accessibilityManager; 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 { public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view); void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view); void onActivationReset(ActivatableNotificationView view);

View File

@@ -91,6 +91,7 @@ import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters; 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.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -143,6 +144,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private static final int MENU_VIEW_INDEX = 0; private static final int MENU_VIEW_INDEX = 0;
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f; 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 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 // 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. // 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 mNotificationTranslationFinished = false;
private boolean mIsSnoozed; private boolean mIsSnoozed;
private boolean mIsFaded; private boolean mIsFaded;
private boolean mAnimatePinnedRoundness = false;
/** /**
* Listener for when {@link ExpandableNotificationRow} is laid out. * Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -376,7 +381,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private float mTopRoundnessDuringLaunchAnimation; private float mTopRoundnessDuringLaunchAnimation;
private float mBottomRoundnessDuringLaunchAnimation; private float mBottomRoundnessDuringLaunchAnimation;
private boolean mIsNotificationGroupCornerEnabled; private float mSmallRoundness;
/** /**
* Returns whether the given {@code statusBarNotification} is a system notification. * Returns whether the given {@code statusBarNotification} is a system notification.
@@ -844,7 +849,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} }
onAttachedChildrenCountChanged(); onAttachedChildrenCountChanged();
row.setIsChildInGroup(false, null); 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()) { if (child.keepInParentForDismissAnimation()) {
mChildrenContainer.removeNotification(child); mChildrenContainer.removeNotification(child);
child.setIsChildInGroup(false, null); 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); child.setKeepInParentForDismissAnimation(false);
logKeepInParentChildDetached(child); logKeepInParentChildDetached(child);
childCountChanged = true; childCountChanged = true;
@@ -915,6 +925,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationParent.updateBackgroundForGroupState(); mNotificationParent.updateBackgroundForGroupState();
} }
updateBackgroundClipping(); updateBackgroundClipping();
if (mUseRoundnessSourceTypes) {
updateBaseRoundness();
}
} }
@Override @Override
@@ -1033,6 +1046,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (isAboveShelf() != wasAboveShelf) { if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!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 @Override
@@ -1607,6 +1630,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
super(context, attrs); super(context, attrs);
mImageResolver = new NotificationInlineImageResolver(context, mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache()); new NotificationInlineImageCache());
float radius = getResources().getDimension(R.dimen.notification_corner_radius_small);
mSmallRoundness = radius / getMaxRadius();
initDimens(); initDimens();
} }
@@ -1839,7 +1864,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setIsLowPriority(mIsLowPriority); mChildrenContainer.setIsLowPriority(mIsLowPriority);
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated(); mChildrenContainer.onNotificationUpdated();
mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled); mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
mTranslateableViews.add(mChildrenContainer); mTranslateableViews.add(mChildrenContainer);
}); });
@@ -2271,7 +2296,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override @Override
public float getTopRoundness() { public float getTopRoundness() {
if (mExpandAnimationRunning) { if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
return mTopRoundnessDuringLaunchAnimation; return mTopRoundnessDuringLaunchAnimation;
} }
@@ -2280,7 +2305,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override @Override
public float getBottomRoundness() { public float getBottomRoundness() {
if (mExpandAnimationRunning) { if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
return mBottomRoundnessDuringLaunchAnimation; return mBottomRoundnessDuringLaunchAnimation;
} }
@@ -3436,17 +3461,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} }
@Override @Override
public void applyRoundness() { public void applyRoundnessAndInvalidate() {
super.applyRoundness();
applyChildrenRoundness(); applyChildrenRoundness();
super.applyRoundnessAndInvalidate();
} }
private void applyChildrenRoundness() { private void applyChildrenRoundness() {
if (mIsSummaryWithChildren) { if (mIsSummaryWithChildren) {
mChildrenContainer.requestBottomRoundness( if (mUseRoundnessSourceTypes) {
getBottomRoundness(), mChildrenContainer.requestRoundness(
/* animate = */ false, /* top = */ getTopRoundness(),
SourceType.DefaultValue); /* bottom = */ getBottomRoundness(),
FROM_PARENT);
} else {
mChildrenContainer.requestBottomRoundness(
getBottomRoundness(),
LegacySourceType.DefaultValue,
/* animate = */ false);
}
} }
} }
@@ -3605,6 +3637,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else { } else {
pw.println("no viewState!!!"); pw.println("no viewState!!!");
} }
pw.println("Roundness: " + getRoundableState().debugString());
if (mIsSummaryWithChildren) { if (mIsSummaryWithChildren) {
pw.println(); pw.println();
@@ -3649,14 +3682,38 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mTargetPoint; 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 * @param enabled true if is supported
*/ */
public void enableNotificationGroupCorner(boolean enabled) { @Override
mIsNotificationGroupCornerEnabled = enabled; public void useRoundnessSourceTypes(boolean enabled) {
super.useRoundnessSourceTypes(enabled);
if (mChildrenContainer != null) { 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();
} }
} }
} }

View File

@@ -255,8 +255,8 @@ public class ExpandableNotificationRowController implements NotifViewController
mStatusBarStateController.removeCallback(mStatusBarStateListener); mStatusBarStateController.removeCallback(mStatusBarStateListener);
} }
}); });
mView.enableNotificationGroupCorner( mView.useRoundnessSourceTypes(
mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_CORNER)); mFeatureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
} }
private final StatusBarStateController.StateListener mStatusBarStateListener = private final StatusBarStateController.StateListener mStatusBarStateListener =

View File

@@ -219,9 +219,9 @@ public abstract class ExpandableOutlineView extends ExpandableView {
} }
@Override @Override
public void applyRoundness() { public void applyRoundnessAndInvalidate() {
invalidateOutline(); invalidateOutline();
super.applyRoundness(); super.applyRoundnessAndInvalidate();
} }
protected void setBackgroundTop(int backgroundTop) { protected void setBackgroundTop(int backgroundTop) {
@@ -233,7 +233,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
public void onDensityOrFontScaleChanged() { public void onDensityOrFontScaleChanged() {
initDimens(); initDimens();
applyRoundness(); applyRoundnessAndInvalidate();
} }
@Override @Override
@@ -241,7 +241,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int previousHeight = getActualHeight(); int previousHeight = getActualHeight();
super.setActualHeight(actualHeight, notifyListeners); super.setActualHeight(actualHeight, notifyListeners);
if (previousHeight != actualHeight) { if (previousHeight != actualHeight) {
applyRoundness(); applyRoundnessAndInvalidate();
} }
} }
@@ -250,7 +250,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int previousAmount = getClipTopAmount(); int previousAmount = getClipTopAmount();
super.setClipTopAmount(clipTopAmount); super.setClipTopAmount(clipTopAmount);
if (previousAmount != clipTopAmount) { if (previousAmount != clipTopAmount) {
applyRoundness(); applyRoundnessAndInvalidate();
} }
} }
@@ -259,14 +259,14 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int previousAmount = getClipBottomAmount(); int previousAmount = getClipBottomAmount();
super.setClipBottomAmount(clipBottomAmount); super.setClipBottomAmount(clipBottomAmount);
if (previousAmount != clipBottomAmount) { if (previousAmount != clipBottomAmount) {
applyRoundness(); applyRoundnessAndInvalidate();
} }
} }
protected void setOutlineAlpha(float alpha) { protected void setOutlineAlpha(float alpha) {
if (alpha != mOutlineAlpha) { if (alpha != mOutlineAlpha) {
mOutlineAlpha = alpha; mOutlineAlpha = alpha;
applyRoundness(); applyRoundnessAndInvalidate();
} }
} }
@@ -280,7 +280,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
setOutlineRect(rect.left, rect.top, rect.right, rect.bottom); setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
} else { } else {
mCustomOutline = false; mCustomOutline = false;
applyRoundness(); applyRoundnessAndInvalidate();
} }
} }
@@ -340,7 +340,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
// Outlines need to be at least 1 dp // Outlines need to be at least 1 dp
mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom); mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom);
mOutlineRect.right = (int) Math.max(left, mOutlineRect.right); mOutlineRect.right = (int) Math.max(left, mOutlineRect.right);
applyRoundness(); applyRoundnessAndInvalidate();
} }
public Path getCustomClipPath(View child) { public Path getCustomClipPath(View child) {

View File

@@ -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();
}
} }

View File

@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil; import com.android.systemui.statusbar.NotificationGroupingUtil;
import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FeedbackIcon; 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.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.Roundable;
@@ -83,6 +84,7 @@ public class NotificationChildrenContainer extends ViewGroup
return mAnimationFilter; return mAnimationFilter;
} }
}.setDuration(200); }.setDuration(200);
private static final SourceType FROM_PARENT = SourceType.from("FromParent(NCC)");
private final List<View> mDividers = new ArrayList<>(); private final List<View> mDividers = new ArrayList<>();
private final List<ExpandableNotificationRow> mAttachedChildren = new ArrayList<>(); private final List<ExpandableNotificationRow> mAttachedChildren = new ArrayList<>();
@@ -131,7 +133,7 @@ public class NotificationChildrenContainer extends ViewGroup
private int mUntruncatedChildCount; private int mUntruncatedChildCount;
private boolean mContainingNotificationIsFaded = false; private boolean mContainingNotificationIsFaded = false;
private RoundableState mRoundableState; private RoundableState mRoundableState;
private boolean mIsNotificationGroupCornerEnabled; private boolean mUseRoundnessSourceTypes;
public NotificationChildrenContainer(Context context) { public NotificationChildrenContainer(Context context) {
this(context, null); this(context, null);
@@ -313,9 +315,12 @@ public class NotificationChildrenContainer extends ViewGroup
row.setContentTransformationAmount(0, false /* isLastChild */); row.setContentTransformationAmount(0, false /* isLastChild */);
row.setNotificationFaded(mContainingNotificationIsFaded); row.setNotificationFaded(mContainingNotificationIsFaded);
// This is a workaround, the NotificationShelf should be the owner of `OnScroll` roundness. if (!mUseRoundnessSourceTypes) {
// Here we should reset the `OnScroll` roundness only on top-level rows. // This is a workaround, the NotificationShelf should be the owner of `OnScroll`
NotificationShelf.resetOnScrollRoundness(row); // 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! // It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState(); ExpandableViewState viewState = row.getViewState();
@@ -323,6 +328,10 @@ public class NotificationChildrenContainer extends ViewGroup
viewState.cancelAnimations(row); viewState.cancelAnimations(row);
row.cancelAppearDrawing(); row.cancelAppearDrawing();
} }
if (mUseRoundnessSourceTypes) {
applyRoundnessAndInvalidate();
}
} }
private void ensureRemovedFromTransientContainer(View v) { private void ensureRemovedFromTransientContainer(View v) {
@@ -356,6 +365,11 @@ public class NotificationChildrenContainer extends ViewGroup
if (!row.isRemoved()) { if (!row.isRemoved()) {
mGroupingUtil.restoreChildNotification(row); mGroupingUtil.restoreChildNotification(row);
} }
if (mUseRoundnessSourceTypes) {
row.requestRoundnessReset(FROM_PARENT);
applyRoundnessAndInvalidate();
}
} }
/** /**
@@ -841,7 +855,7 @@ public class NotificationChildrenContainer extends ViewGroup
isCanvasChanged = true; isCanvasChanged = true;
canvas.save(); canvas.save();
if (mIsNotificationGroupCornerEnabled && translation != 0f) { if (mUseRoundnessSourceTypes && translation != 0f) {
clipPath.offset(translation, 0f); clipPath.offset(translation, 0f);
canvas.clipPath(clipPath); canvas.clipPath(clipPath);
clipPath.offset(-translation, 0f); clipPath.offset(-translation, 0f);
@@ -1392,24 +1406,28 @@ public class NotificationChildrenContainer extends ViewGroup
} }
@Override @Override
public void applyRoundness() { public void applyRoundnessAndInvalidate() {
Roundable.super.applyRoundness();
boolean last = true; boolean last = true;
for (int i = mAttachedChildren.size() - 1; i >= 0; i--) { for (int i = mAttachedChildren.size() - 1; i >= 0; i--) {
ExpandableNotificationRow child = mAttachedChildren.get(i); ExpandableNotificationRow child = mAttachedChildren.get(i);
if (child.getVisibility() == View.GONE) { if (child.getVisibility() == View.GONE) {
continue; continue;
} }
child.requestTopRoundness( if (mUseRoundnessSourceTypes) {
/* value = */ 0f, child.requestRoundness(
/* animate = */ isShown(), /* top = */ 0f,
SourceType.DefaultValue); /* bottom = */ last ? getBottomRoundness() : 0f,
child.requestBottomRoundness( FROM_PARENT);
/* value = */ last ? getBottomRoundness() : 0f, } else {
/* animate = */ isShown(), child.requestRoundness(
SourceType.DefaultValue); /* top = */ 0f,
/* bottom = */ last ? getBottomRoundness() : 0f,
LegacySourceType.DefaultValue,
/* animate = */ isShown());
}
last = false; last = false;
} }
Roundable.super.applyRoundnessAndInvalidate();
} }
public void setHeaderVisibleAmount(float headerVisibleAmount) { 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 * @param enabled true if is supported
*/ */
public void enableNotificationGroupCorner(boolean enabled) { public void useRoundnessSourceTypes(boolean enabled) {
mIsNotificationGroupCornerEnabled = enabled; mUseRoundnessSourceTypes = enabled;
}
@Override
public String toString() {
String roundableStateDebug = "RoundableState = " + getRoundableState().debugString();
return "NotificationChildrenContainer:" + hashCode() + " { " + roundableStateDebug + " }";
} }
} }

View File

@@ -25,6 +25,9 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager; 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.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.SourceType;
@@ -45,6 +48,7 @@ import javax.inject.Inject;
public class NotificationRoundnessManager implements Dumpable { public class NotificationRoundnessManager implements Dumpable {
private static final String TAG = "NotificationRoundnessManager"; private static final String TAG = "NotificationRoundnessManager";
private static final SourceType DISMISS_ANIMATION = SourceType.from("DismissAnimation");
private final ExpandableView[] mFirstInSectionViews; private final ExpandableView[] mFirstInSectionViews;
private final ExpandableView[] mLastInSectionViews; private final ExpandableView[] mLastInSectionViews;
@@ -63,12 +67,14 @@ public class NotificationRoundnessManager implements Dumpable {
private ExpandableView mSwipedView = null; private ExpandableView mSwipedView = null;
private Roundable mViewBeforeSwipedView = null; private Roundable mViewBeforeSwipedView = null;
private Roundable mViewAfterSwipedView = null; private Roundable mViewAfterSwipedView = null;
private boolean mUseRoundnessSourceTypes;
@Inject @Inject
NotificationRoundnessManager( NotificationRoundnessManager(
NotificationSectionsFeatureManager sectionsFeatureManager, NotificationSectionsFeatureManager sectionsFeatureManager,
NotificationRoundnessLogger notifLogger, NotificationRoundnessLogger notifLogger,
DumpManager dumpManager) { DumpManager dumpManager,
FeatureFlags featureFlags) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets(); int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections]; mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections]; mLastInSectionViews = new ExpandableView[numberOfSections];
@@ -76,6 +82,7 @@ public class NotificationRoundnessManager implements Dumpable {
mTmpLastInSectionViews = new ExpandableView[numberOfSections]; mTmpLastInSectionViews = new ExpandableView[numberOfSections];
mNotifLogger = notifLogger; mNotifLogger = notifLogger;
mDumpManager = dumpManager; mDumpManager = dumpManager;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mDumpManager.registerDumpable(TAG, this); mDumpManager.registerDumpable(TAG, this);
} }
@@ -94,6 +101,7 @@ public class NotificationRoundnessManager implements Dumpable {
} }
public void updateView(ExpandableView view, boolean animate) { public void updateView(ExpandableView view, boolean animate) {
if (mUseRoundnessSourceTypes) return;
boolean changed = updateViewWithoutCallback(view, animate); boolean changed = updateViewWithoutCallback(view, animate);
if (changed) { if (changed) {
mRoundingChangedCallback.run(); mRoundingChangedCallback.run();
@@ -110,6 +118,7 @@ public class NotificationRoundnessManager implements Dumpable {
boolean updateViewWithoutCallback( boolean updateViewWithoutCallback(
ExpandableView view, ExpandableView view,
boolean animate) { boolean animate) {
if (mUseRoundnessSourceTypes) return false;
if (view == null if (view == null
|| view == mViewBeforeSwipedView || view == mViewBeforeSwipedView
|| view == mViewAfterSwipedView) { || view == mViewAfterSwipedView) {
@@ -118,13 +127,13 @@ public class NotificationRoundnessManager implements Dumpable {
final boolean isTopChanged = view.requestTopRoundness( final boolean isTopChanged = view.requestTopRoundness(
getRoundnessDefaultValue(view, true /* top */), getRoundnessDefaultValue(view, true /* top */),
animate, LegacySourceType.DefaultValue,
SourceType.DefaultValue); animate);
final boolean isBottomChanged = view.requestBottomRoundness( final boolean isBottomChanged = view.requestBottomRoundness(
getRoundnessDefaultValue(view, /* top = */ false), getRoundnessDefaultValue(view, /* top = */ false),
animate, LegacySourceType.DefaultValue,
SourceType.DefaultValue); animate);
final boolean isFirstInSection = isFirstInSection(view); final boolean isFirstInSection = isFirstInSection(view);
final boolean isLastInSection = isLastInSection(view); final boolean isLastInSection = isLastInSection(view);
@@ -139,6 +148,7 @@ public class NotificationRoundnessManager implements Dumpable {
} }
private boolean isFirstInSection(ExpandableView view) { private boolean isFirstInSection(ExpandableView view) {
if (mUseRoundnessSourceTypes) return false;
for (int i = 0; i < mFirstInSectionViews.length; i++) { for (int i = 0; i < mFirstInSectionViews.length; i++) {
if (view == mFirstInSectionViews[i]) { if (view == mFirstInSectionViews[i]) {
return true; return true;
@@ -148,6 +158,7 @@ public class NotificationRoundnessManager implements Dumpable {
} }
private boolean isLastInSection(ExpandableView view) { private boolean isLastInSection(ExpandableView view) {
if (mUseRoundnessSourceTypes) return false;
for (int i = mLastInSectionViews.length - 1; i >= 0; i--) { for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
if (view == mLastInSectionViews[i]) { if (view == mLastInSectionViews[i]) {
return true; return true;
@@ -160,9 +171,6 @@ public class NotificationRoundnessManager implements Dumpable {
Roundable viewBefore, Roundable viewBefore,
ExpandableView viewSwiped, ExpandableView viewSwiped,
Roundable viewAfter) { 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 // 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. // 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 // 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 (mSwipedView != null) oldViews.add(mSwipedView);
if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView); if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView);
final SourceType source;
if (mUseRoundnessSourceTypes) {
source = DISMISS_ANIMATION;
} else {
source = LegacySourceType.OnDismissAnimation;
}
mViewBeforeSwipedView = viewBefore; mViewBeforeSwipedView = viewBefore;
if (viewBefore != null) { if (viewBefore != null) {
oldViews.remove(viewBefore); oldViews.remove(viewBefore);
viewBefore.requestTopRoundness(0f, animate, source); viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, source);
viewBefore.requestBottomRoundness(1f, animate, source);
} }
mSwipedView = viewSwiped; mSwipedView = viewSwiped;
if (viewSwiped != null) { if (viewSwiped != null) {
oldViews.remove(viewSwiped); oldViews.remove(viewSwiped);
viewSwiped.requestTopRoundness(1f, animate, source); viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, source);
viewSwiped.requestBottomRoundness(1f, animate, source);
} }
mViewAfterSwipedView = viewAfter; mViewAfterSwipedView = viewAfter;
if (viewAfter != null) { if (viewAfter != null) {
oldViews.remove(viewAfter); oldViews.remove(viewAfter);
viewAfter.requestTopRoundness(1f, animate, source); viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, source);
viewAfter.requestBottomRoundness(0f, animate, source);
} }
// After setting the current Views, reset the views that are still present in the set. // After setting the current Views, reset the views that are still present in the set.
for (Roundable oldView : oldViews) { for (Roundable oldView : oldViews) {
oldView.requestTopRoundness(0f, animate, source); oldView.requestRoundnessReset(source);
oldView.requestBottomRoundness(0f, animate, source);
} }
} }
@@ -204,7 +215,23 @@ public class NotificationRoundnessManager implements Dumpable {
mIsClearAllInProgress = isClearingAll; 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) { private float getRoundnessDefaultValue(Roundable view, boolean top) {
if (mUseRoundnessSourceTypes) return 0f;
if (view == null) { if (view == null) {
return 0f; return 0f;
} }
@@ -250,6 +277,7 @@ public class NotificationRoundnessManager implements Dumpable {
} }
public void setExpanded(float expandedHeight, float appearFraction) { public void setExpanded(float expandedHeight, float appearFraction) {
if (mUseRoundnessSourceTypes) return;
mExpanded = expandedHeight != 0.0f; mExpanded = expandedHeight != 0.0f;
mAppearFraction = appearFraction; mAppearFraction = appearFraction;
if (mTrackedHeadsUp != null) { if (mTrackedHeadsUp != null) {
@@ -258,6 +286,7 @@ public class NotificationRoundnessManager implements Dumpable {
} }
public void updateRoundedChildren(NotificationSection[] sections) { public void updateRoundedChildren(NotificationSection[] sections) {
if (mUseRoundnessSourceTypes) return;
boolean anyChanged = false; boolean anyChanged = false;
for (int i = 0; i < sections.length; i++) { for (int i = 0; i < sections.length; i++) {
mTmpFirstInSectionViews[i] = mFirstInSectionViews[i]; mTmpFirstInSectionViews[i] = mFirstInSectionViews[i];
@@ -280,6 +309,7 @@ public class NotificationRoundnessManager implements Dumpable {
NotificationSection[] sections, NotificationSection[] sections,
ExpandableView[] oldViews, ExpandableView[] oldViews,
boolean first) { boolean first) {
if (mUseRoundnessSourceTypes) return false;
boolean anyChanged = false; boolean anyChanged = false;
for (ExpandableView oldView : oldViews) { for (ExpandableView oldView : oldViews) {
if (oldView != null) { if (oldView != null) {
@@ -313,6 +343,7 @@ public class NotificationRoundnessManager implements Dumpable {
NotificationSection[] sections, NotificationSection[] sections,
ExpandableView[] oldViews, ExpandableView[] oldViews,
boolean first) { boolean first) {
if (mUseRoundnessSourceTypes) return false;
boolean anyChanged = false; boolean anyChanged = false;
for (NotificationSection section : sections) { for (NotificationSection section : sections) {
ExpandableView newView = ExpandableView newView =
@@ -339,6 +370,15 @@ public class NotificationRoundnessManager implements Dumpable {
mAnimatedChildren = animatedChildren; 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) { public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
mRoundingChangedCallback = roundingChangedCallback; mRoundingChangedCallback = roundingChangedCallback;
} }

View File

@@ -19,8 +19,11 @@ import android.annotation.ColorInt
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import com.android.internal.annotations.VisibleForTesting 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.media.controls.ui.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager 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.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
import com.android.systemui.statusbar.notification.dagger.AlertingHeader import com.android.systemui.statusbar.notification.dagger.AlertingHeader
@@ -44,12 +47,16 @@ class NotificationSectionsManager @Inject internal constructor(
private val keyguardMediaController: KeyguardMediaController, private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager, private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val mediaContainerController: MediaContainerController, private val mediaContainerController: MediaContainerController,
private val notificationRoundnessManager: NotificationRoundnessManager,
@IncomingHeader private val incomingHeaderController: SectionHeaderController, @IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController, @PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController, @AlertingHeader private val alertingHeaderController: SectionHeaderController,
@SilentHeader private val silentHeaderController: SectionHeaderController @SilentHeader private val silentHeaderController: SectionHeaderController,
featureFlags: FeatureFlags
) : SectionProvider { ) : SectionProvider {
private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
private val configurationListener = object : ConfigurationController.ConfigurationListener { private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() { override fun onLocaleListChanged() {
reinflateViews() reinflateViews()
@@ -177,11 +184,49 @@ class NotificationSectionsManager @Inject internal constructor(
size = sections.size, size = sections.size,
operation = SectionBounds::addNotif 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 // Update each section with the associated boundary, tracking if there was a change
val changed = sections.fold(false) { changed, section -> val changed = sections.fold(false) { changed, section ->
val bounds = sectionBounds[section.bucket] ?: SectionBounds.None 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) { if (DEBUG) {
logSections(sections) logSections(sections)
} }
@@ -215,5 +260,6 @@ class NotificationSectionsManager @Inject internal constructor(
companion object { companion object {
private const val TAG = "NotifSectionsManager" private const val TAG = "NotifSectionsManager"
private const val DEBUG = false private const val DEBUG = false
private val SECTION = SourceType.from("Section")
} }
} }

View File

@@ -191,10 +191,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final boolean mDebugLines; private final boolean mDebugLines;
private Paint mDebugPaint; 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 Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation; private final boolean mDebugRemoveAnimation;
private final boolean mSimplifiedAppearFraction; private final boolean mSimplifiedAppearFraction;
private final boolean mUseRoundnessSourceTypes;
private int mContentHeight; private int mContentHeight;
private float mIntrinsicContentHeight; private float mIntrinsicContentHeight;
@@ -355,15 +358,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mQsHeaderBound = new Rect(); private final Rect mQsHeaderBound = new Rect();
private boolean mContinuousShadowUpdate; private boolean mContinuousShadowUpdate;
private boolean mContinuousBackgroundUpdate; private boolean mContinuousBackgroundUpdate;
private final ViewTreeObserver.OnPreDrawListener mShadowUpdater private final ViewTreeObserver.OnPreDrawListener mShadowUpdater = () -> {
= () -> { updateViewShadows();
updateViewShadows(); return true;
return true; };
};
private final ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> { private final ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
updateBackground(); updateBackground();
return true; return true;
}; };
private final Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> { private final Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> {
float endY = view.getTranslationY() + view.getActualHeight(); float endY = view.getTranslationY() + view.getActualHeight();
float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
@@ -419,7 +421,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/ */
private int mMaxDisplayedNotifications = -1; private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1; private float mKeyguardBottomPadding = -1;
@VisibleForTesting int mStatusBarHeight; @VisibleForTesting
int mStatusBarHeight;
private int mMinInteractionHeight; private int mMinInteractionHeight;
private final Rect mClipRect = new Rect(); private final Rect mClipRect = new Rect();
private boolean mIsClipped; private boolean mIsClipped;
@@ -568,6 +571,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Nullable @Nullable
private OnClickListener mManageButtonClickListener; private OnClickListener mManageButtonClickListener;
@Nullable
private OnNotificationRemovedListener mOnNotificationRemovedListener;
public NotificationStackScrollLayout(Context context, AttributeSet attrs) { public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0); super(context, attrs, 0, 0);
@@ -576,6 +581,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES); mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION); mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION);
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mSectionsManager = Dependency.get(NotificationSectionsManager.class); mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController = mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class); Dependency.get(ScreenOffAnimationController.class);
@@ -741,7 +747,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} }
private void logHunSkippedForUnexpectedState(ExpandableNotificationRow enr, private void logHunSkippedForUnexpectedState(ExpandableNotificationRow enr,
boolean expected, boolean actual) { boolean expected, boolean actual) {
if (mLogger == null) return; if (mLogger == null) return;
mLogger.hunSkippedForUnexpectedState(enr.getEntry(), expected, actual); mLogger.hunSkippedForUnexpectedState(enr.getEntry(), expected, actual);
} }
@@ -868,13 +874,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/** /**
* Draws round rects for each background section. * Draws round rects for each background section.
* * <p>
* We want to draw a round rect for each background section as defined by {@link #mSections}. * 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 * 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 * 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 * 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. * 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 * 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. * 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 * 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. * 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, private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
int animationYOffset) { int animationYOffset) {
int backgroundRectTop = top; int backgroundRectTop = top;
int lastSectionBottom = int lastSectionBottom =
mSections[0].getCurrentBounds().bottom + animationYOffset; mSections[0].getCurrentBounds().bottom + animationYOffset;
@@ -974,7 +980,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void initView(Context context, NotificationSwipeHelper swipeHelper, void initView(Context context, NotificationSwipeHelper swipeHelper,
NotificationStackSizeCalculator notificationStackSizeCalculator) { NotificationStackSizeCalculator notificationStackSizeCalculator) {
mScroller = new OverScroller(getContext()); mScroller = new OverScroller(getContext());
mSwipeHelper = swipeHelper; mSwipeHelper = swipeHelper;
mNotificationStackSizeCalculator = notificationStackSizeCalculator; mNotificationStackSizeCalculator = notificationStackSizeCalculator;
@@ -1304,18 +1310,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/** /**
* @return Whether we should skip stack height updates. * @return Whether we should skip stack height updates.
* True when * True when
* 1) Unlock hint is running * 1) Unlock hint is running
* 2) Swiping up on lockscreen or flinging down after swipe up * 2) Swiping up on lockscreen or flinging down after swipe up
*/ */
private boolean shouldSkipHeightUpdate() { private boolean shouldSkipHeightUpdate() {
return mAmbientState.isOnKeyguard() return mAmbientState.isOnKeyguard()
&& (mAmbientState.isUnlockHintRunning() && (mAmbientState.isUnlockHintRunning()
|| mAmbientState.isSwipingUp() || mAmbientState.isSwipingUp()
|| mAmbientState.isFlingingAfterSwipeUpOnLockscreen()); || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
} }
/** /**
* Apply expansion fraction to the y position and height of the notifications panel. * Apply expansion fraction to the y position and height of the notifications panel.
*
* @param listenerNeedsAnimation does the listener need to animate? * @param listenerNeedsAnimation does the listener need to animate?
*/ */
private void updateStackPosition(boolean listenerNeedsAnimation) { private void updateStackPosition(boolean listenerNeedsAnimation) {
@@ -1708,7 +1715,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/ */
@ShadeViewRefactor(RefactorComponent.COORDINATOR) @ShadeViewRefactor(RefactorComponent.COORDINATOR)
ExpandableView getChildAtPosition(float touchX, float touchY, ExpandableView getChildAtPosition(float touchX, float touchY,
boolean requireMinHeight, boolean ignoreDecors) { boolean requireMinHeight, boolean ignoreDecors) {
// find the view under the pointer, accounting for GONE views // find the view under the pointer, accounting for GONE views
final int count = getChildCount(); final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) { for (int childIdx = 0; childIdx < count; childIdx++) {
@@ -1868,9 +1875,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void dismissViewAnimated( public void dismissViewAnimated(
View child, Consumer<Boolean> endRunnable, int delay, long duration) { View child, Consumer<Boolean> endRunnable, int delay, long duration) {
if (child instanceof SectionHeaderView) { if (child instanceof SectionHeaderView) {
((StackScrollerDecorView) child).setContentVisible( ((StackScrollerDecorView) child).setContentVisible(
false /* visible */, true /* animate */, endRunnable); false /* visible */, true /* animate */, endRunnable);
return; return;
} }
mSwipeHelper.dismissChild( mSwipeHelper.dismissChild(
child, 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 * 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. * 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 * 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 * the scroll amount if so. The actual scrolling/overscrolling happens in
* {@link #onCustomOverScrolled(int, boolean)} * {@link #onCustomOverScrolled(int, boolean)}
*
* @param deltaY The (signed) number of pixels to scroll. * @param deltaY The (signed) number of pixels to scroll.
* @param scrollY The current scroll position (absolute scrolling only). * @param scrollY The current scroll position (absolute scrolling only).
* @param scrollRangeY The maximum allowable 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) @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate, public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators) { boolean cancelAnimators) {
setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop)); setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
} }
@@ -2114,7 +2122,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/ */
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate, public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators, boolean isRubberbanded) { boolean cancelAnimators, boolean isRubberbanded) {
if (cancelAnimators) { if (cancelAnimators) {
mStateAnimator.cancelOverScrollAnimators(onTop); mStateAnimator.cancelOverScrollAnimators(onTop);
} }
@@ -2123,7 +2131,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate, private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
boolean isRubberbanded) { boolean isRubberbanded) {
amount = Math.max(0, amount); amount = Math.max(0, amount);
if (animate) { if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded); 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 * Scrolls to the given position, overscrolling if needed. If called during a fling and the
* position exceeds the provided maximum overscroll, springs back instead. * 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 * @param clampedY Whether this value was clamped by the calling method, meaning we've reached
* the overscroll limit. * the overscroll limit.
*/ */
@@ -2288,8 +2296,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
List<ExpandableNotificationRow> notificationChildren = List<ExpandableNotificationRow> notificationChildren =
row.getAttachedChildren(); row.getAttachedChildren();
for (int childIndex = 0; childIndex < notificationChildren.size(); int childrenSize = notificationChildren.size();
childIndex++) { for (int childIndex = 0; childIndex < childrenSize; childIndex++) {
ExpandableNotificationRow rowChild = notificationChildren.get(childIndex); ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
if (rowChild.getTranslationY() + rowTranslation >= translationY) { if (rowChild.getTranslationY() + rowTranslation >= translationY) {
return rowChild; return rowChild;
@@ -2365,10 +2373,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/** /**
* Calculate the gap height between two different views * Calculate the gap height between two different views
* *
* @param previous the previousView * @param previous the previousView
* @param current the currentView * @param current the currentView
* @param visibleIndex the visible index in the list * @param visibleIndex the visible index in the list
*
* @return the gap height needed before the current view * @return the gap height needed before the current view
*/ */
public float calculateGapHeight( public float calculateGapHeight(
@@ -2376,7 +2383,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
ExpandableView current, ExpandableView current,
int visibleIndex int visibleIndex
) { ) {
return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current, return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current,
previous, mAmbientState.getFractionToShade(), mAmbientState.isOnKeyguard()); previous, mAmbientState.getFractionToShade(), mAmbientState.isOnKeyguard());
} }
@@ -2642,15 +2649,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mScrolledToTopOnFirstDown return mScrolledToTopOnFirstDown
&& !mExpandedInThisMotion && !mExpandedInThisMotion
&& (initialVelocity > mMinimumVelocity && (initialVelocity > mMinimumVelocity
|| (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0)); || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0));
} }
/** /**
* Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
* account. * account.
* *
* @param qsHeight the top padding imposed by the quick settings panel * @param qsHeight the top padding imposed by the quick settings panel
* @param animate whether to animate the change * @param animate whether to animate the change
*/ */
@ShadeViewRefactor(RefactorComponent.COORDINATOR) @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void updateTopPadding(float qsHeight, boolean animate) { public void updateTopPadding(float qsHeight, boolean animate) {
@@ -2724,21 +2731,34 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setChildTransferInProgress(boolean childTransferInProgress) { public void setChildTransferInProgress(boolean childTransferInProgress) {
Assert.isMainThread(); Assert.isMainThread();
mChildTransferInProgress = childTransferInProgress; mChildTransferInProgress = childTransferInProgress;
} }
/**
* Set the remove notification listener
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
mOnNotificationRemovedListener = listener;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@Override @Override
public void onViewRemoved(View child) { public void onViewRemoved(View child) {
super.onViewRemoved(child); super.onViewRemoved(child);
// we only call our internal methods if this is actually a removal and not just a // we only call our internal methods if this is actually a removal and not just a
// notification which becomes a child notification // notification which becomes a child notification
ExpandableView expandableView = (ExpandableView) child;
if (!mChildTransferInProgress) { 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; mAnimateNextSectionBoundsChange = false;
} }
mAmbientState.setLastVisibleBackgroundChild(lastChild); mAmbientState.setLastVisibleBackgroundChild(lastChild);
// TODO: Refactor SectionManager and put the RoundnessManager there. if (!mUseRoundnessSourceTypes) {
mController.getNotificationRoundnessManager().updateRoundedChildren(mSections); // TODO: Refactor SectionManager and put the RoundnessManager there.
mController.getNotificationRoundnessManager().updateRoundedChildren(mSections);
}
mAnimateBottomOnLayout = false; mAnimateBottomOnLayout = false;
invalidate(); 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 // Only animate if we still have pinned heads up, otherwise we just have the
// regular collapse animation of the lock screen // regular collapse animation of the lock screen
|| (mKeyguardBypassEnabled && onKeyguard() || (mKeyguardBypassEnabled && onKeyguard()
&& mInHeadsUpPinnedMode); && mInHeadsUpPinnedMode);
if (performDisappearAnimation && !isHeadsUp) { if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked() type = row.wasJustClicked()
? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
@@ -3351,7 +3373,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
AnimationEvent animEvent = duration == null AnimationEvent animEvent = duration == null
? new AnimationEvent(child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION) ? new AnimationEvent(child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION)
: new AnimationEvent( : new AnimationEvent(
child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION, duration); child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION, duration);
mAnimationEvents.add(animEvent); mAnimationEvents.add(animEvent);
} }
mChildrenChangingPositions.clear(); mChildrenChangingPositions.clear();
@@ -3442,7 +3464,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) { 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, private void logEmptySpaceClick(MotionEvent ev, boolean isTouchBelowLastNotification,
int statusBarState, boolean touchIsClick) { int statusBarState, boolean touchIsClick) {
if (mLogger == null) { if (mLogger == null) {
return; return;
} }
@@ -3957,7 +3981,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mOnEmptySpaceClickListener = listener; mOnEmptySpaceClickListener = listener;
} }
/** @hide */ /**
* @hide
*/
@Override @Override
@ShadeViewRefactor(RefactorComponent.INPUT) @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean performAccessibilityActionInternal(int action, Bundle arguments) { public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
@@ -4728,7 +4754,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return touchY > mTopPadding + mStackTranslation; return touchY > mTopPadding + mStackTranslation;
} }
/** @hide */ /**
* @hide
*/
@Override @Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
@@ -5353,7 +5381,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return canChildBeCleared(row) && matchesSelection(row, selection); 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) { public void setManageButtonClickListener(@Nullable OnClickListener listener) {
mManageButtonClickListener = listener; mManageButtonClickListener = listener;
if (mFooterView != null) { 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 * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
* notification positions accordingly. * notification positions accordingly.
*
* @param height the new wake up height * @param height the new wake up height
* @return the overflow how much the height is further than he lowest notification * @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. * Set rounded rect clipping bounds on this view.
*/ */
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius, public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
int bottomRadius) { int bottomRadius) {
if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
&& mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top && mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
&& mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) { && mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
@@ -5710,7 +5741,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mLaunchingNotification = launching; mLaunchingNotification = launching;
mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null
&& (mLaunchAnimationParams.getStartRoundedTopClipping() > 0 && (mLaunchAnimationParams.getStartRoundedTopClipping() > 0
|| mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0); || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) { if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) {
mLaunchedNotificationClipPath.reset(); mLaunchedNotificationClipPath.reset();
} }
@@ -5748,7 +5779,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mLaunchAnimationParams.getProgress(0, mLaunchAnimationParams.getProgress(0,
NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING)); NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop, int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop,
mLaunchAnimationParams.getTop(), expandProgress), mLaunchAnimationParams.getTop(), expandProgress),
mRoundedRectClippingTop); mRoundedRectClippingTop);
float topRadius = mLaunchAnimationParams.getTopCornerRadius(); float topRadius = mLaunchAnimationParams.getTopCornerRadius();
float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius(); float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius();
@@ -5872,25 +5903,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public interface OnOverscrollTopChangedListener { public interface OnOverscrollTopChangedListener {
/** /**
* Notifies a listener that the overscroll has changed. * Notifies a listener that the overscroll has changed.
* *
* @param amount the amount of overscroll, in pixels * @param amount the amount of overscroll, in pixels
* @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
* unrubberbanded motion to directly expand overscroll view (e.g * unrubberbanded motion to directly expand overscroll view (e.g
* expand * expand
* QS) * QS)
*/ */
void onOverscrollTopChanged(float amount, boolean isRubberbanded); void onOverscrollTopChanged(float amount, boolean isRubberbanded);
/** /**
* Notify a listener that the scroller wants to escape from the scrolling motion and * 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) * 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 velocity The velocity that the Scroller had when over flinging
* @param open Should the fling open or close the overscroll view. * @param open Should the fling open or close the overscroll view.
*/ */
void flingTopOverscroll(float velocity, boolean open); void flingTopOverscroll(float velocity, boolean open);
} }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @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) { void onGroupExpandChanged(ExpandableNotificationRow changedRow, boolean expanded) {
boolean animated = mAnimationsEnabled && (mIsExpanded || changedRow.isPinned()); boolean animated = mAnimationsEnabled && (mIsExpanded || changedRow.isPinned());
@@ -6357,15 +6390,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mLastSentExpandedHeight; 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) @Retention(SOURCE)
@IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE}) @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
@interface SelectedRows {} @interface SelectedRows {
/** All rows representing notifs. */ }
/**
* All rows representing notifs.
*/
public static final int ROWS_ALL = 0; 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; 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; public static final int ROWS_GENTLE = 2;
interface ClearAllListener { interface ClearAllListener {
@@ -6380,4 +6423,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
void onAnimationEnd( void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows); List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
} }
/**
*
*/
public interface OnNotificationRemovedListener {
/**
*
* @param child
* @param isTransferInProgress
*/
void onNotificationRemoved(ExpandableView child, boolean isTransferInProgress);
}
} }

View File

@@ -182,10 +182,12 @@ public class NotificationStackScrollLayoutController {
private NotificationStackScrollLayout mView; private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss; private boolean mFadeNotificationsOnDismiss;
private NotificationSwipeHelper mSwipeHelper; private NotificationSwipeHelper mSwipeHelper;
@Nullable private Boolean mHistoryEnabled; @Nullable
private Boolean mHistoryEnabled;
private int mBarState; private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController; private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final FeatureFlags mFeatureFlags; private final FeatureFlags mFeatureFlags;
private final boolean mUseRoundnessSourceTypes;
private final NotificationTargetsHelper mNotificationTargetsHelper; private final NotificationTargetsHelper mNotificationTargetsHelper;
private View mLongPressedView; private View mLongPressedView;
@@ -391,7 +393,7 @@ public class NotificationStackScrollLayoutController {
if (item != null) { if (item != null) {
Point origin = provider.getRevealAnimationOrigin(); Point origin = provider.getRevealAnimationOrigin();
mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
} else { } else {
Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
+ "menu item in menuItemtoExposeOnSnap. Skipping."); + "menu item in menuItemtoExposeOnSnap. Skipping.");
} }
@@ -420,7 +422,7 @@ public class NotificationStackScrollLayoutController {
@Override @Override
public void onSnooze(StatusBarNotification sbn, public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) { NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption); mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption);
} }
@@ -544,7 +546,7 @@ public class NotificationStackScrollLayoutController {
@Override @Override
public boolean updateSwipeProgress(View animView, boolean dismissable, public boolean updateSwipeProgress(View animView, boolean dismissable,
float swipeProgress) { float swipeProgress) {
// Returning true prevents alpha fading. // Returning true prevents alpha fading.
return !mFadeNotificationsOnDismiss; return !mFadeNotificationsOnDismiss;
} }
@@ -584,16 +586,22 @@ public class NotificationStackScrollLayoutController {
@Override @Override
public void onHeadsUpPinned(NotificationEntry entry) { public void onHeadsUpPinned(NotificationEntry entry) {
mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */); if (!mUseRoundnessSourceTypes) {
mNotificationRoundnessManager.updateView(
entry.getRow(),
/* animate = */ false);
}
} }
@Override @Override
public void onHeadsUpUnPinned(NotificationEntry entry) { public void onHeadsUpUnPinned(NotificationEntry entry) {
ExpandableNotificationRow row = entry.getRow(); if (!mUseRoundnessSourceTypes) {
// update the roundedness posted, because we might be animating away the ExpandableNotificationRow row = entry.getRow();
// headsup soon, so no need to set the roundedness to 0 and then back to 1. // update the roundedness posted, because we might be animating away the
row.post(() -> mNotificationRoundnessManager.updateView(row, // headsup soon, so no need to set the roundedness to 0 and then back to 1.
true /* animate */)); row.post(() -> mNotificationRoundnessManager.updateView(row,
true /* animate */));
}
} }
@Override @Override
@@ -603,8 +611,10 @@ public class NotificationStackScrollLayoutController {
mView.setNumHeadsUp(numEntries); mView.setNumHeadsUp(numEntries);
mView.setTopHeadsUpEntry(topEntry); mView.setTopHeadsUpEntry(topEntry);
generateHeadsUpAnimation(entry, isHeadsUp); generateHeadsUpAnimation(entry, isHeadsUp);
ExpandableNotificationRow row = entry.getRow(); if (!mUseRoundnessSourceTypes) {
mNotificationRoundnessManager.updateView(row, true /* animate */); ExpandableNotificationRow row = entry.getRow();
mNotificationRoundnessManager.updateView(row, true /* animate */);
}
} }
}; };
@@ -697,6 +707,7 @@ public class NotificationStackScrollLayoutController {
mSeenNotificationsProvider = seenNotificationsProvider; mSeenNotificationsProvider = seenNotificationsProvider;
mShadeController = shadeController; mShadeController = shadeController;
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mNotificationTargetsHelper = notificationTargetsHelper; mNotificationTargetsHelper = notificationTargetsHelper;
updateResources(); updateResources();
} }
@@ -763,8 +774,10 @@ public class NotificationStackScrollLayoutController {
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
mFadeNotificationsOnDismiss = mFeatureFlags.isEnabled(Flags.NOTIFICATION_DISMISSAL_FADE); mFadeNotificationsOnDismiss = mFeatureFlags.isEnabled(Flags.NOTIFICATION_DISMISSAL_FADE);
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); if (!mUseRoundnessSourceTypes) {
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
}
mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation); mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation);
@@ -997,9 +1010,11 @@ public class NotificationStackScrollLayoutController {
Log.wtf(TAG, "isHistoryEnabled failed to initialize its value"); Log.wtf(TAG, "isHistoryEnabled failed to initialize its value");
return false; return false;
} }
mHistoryEnabled = historyEnabled = mHistoryEnabled = historyEnabled = Settings.Secure.getIntForUser(
Settings.Secure.getIntForUser(mView.getContext().getContentResolver(), mView.getContext().getContentResolver(),
Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; Settings.Secure.NOTIFICATION_HISTORY_ENABLED,
0,
UserHandle.USER_CURRENT) == 1;
} }
return historyEnabled; return historyEnabled;
} }
@@ -1029,7 +1044,7 @@ public class NotificationStackScrollLayoutController {
} }
public void setOverScrollAmount(float amount, boolean onTop, boolean animate, public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators) { boolean cancelAnimators) {
mView.setOverScrollAmount(amount, onTop, animate, 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). * 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 * 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 * QuickSettings panel. That being the case, empty view is always shown if the other conditions
* are true. * are true.
@@ -1233,7 +1248,7 @@ public class NotificationStackScrollLayoutController {
/** /**
* @return true if {@link StatusBarStateController} is in transition to the KEYGUARD * @return true if {@link StatusBarStateController} is in transition to the KEYGUARD
* and false otherwise. * and false otherwise.
*/ */
private boolean isInTransitionToKeyguard() { private boolean isInTransitionToKeyguard() {
final int currentState = mStatusBarStateController.getState(); final int currentState = mStatusBarStateController.getState();
@@ -1265,7 +1280,9 @@ public class NotificationStackScrollLayoutController {
mView.setExpandedHeight(expandedHeight); 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) { public void setQsHeader(ViewGroup view) {
mView.setQsHeader(view); mView.setQsHeader(view);
} }
@@ -1328,7 +1345,7 @@ public class NotificationStackScrollLayoutController {
public RemoteInputController.Delegate createDelegate() { public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() { return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry, public void setRemoteInputActive(NotificationEntry entry,
boolean remoteInputActive) { boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */); entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter(); updateFooter();
@@ -1459,7 +1476,7 @@ public class NotificationStackScrollLayoutController {
} }
private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove, private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
@SelectedRows int selectedRows) { @SelectedRows int selectedRows) {
if (selectedRows == ROWS_ALL) { if (selectedRows == ROWS_ALL) {
mNotifCollection.dismissAllNotifications( mNotifCollection.dismissAllNotifications(
mLockscreenUserManager.getCurrentUserId()); 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 * @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 * of the quick settings edge. This is relevant for media, that is transitioning
* from the keyguard host to the quick settings one. * from the keyguard host to the quick settings one.
*/ */
public int getFullShadeTransitionInset() { public int getFullShadeTransitionInset() {
MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer(); MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer();
@@ -1517,10 +1534,10 @@ public class NotificationStackScrollLayoutController {
/** /**
* @param fraction The fraction of lockscreen to shade transition. * @param fraction The fraction of lockscreen to shade transition.
* 0f for all other states. * 0f for all other states.
* * <p>
* Once the lockscreen to shade transition completes and the shade is 100% open, * Once the lockscreen to shade transition completes and the shade is 100% open,
* LockscreenShadeTransitionController resets amount and fraction to 0, where they remain * LockscreenShadeTransitionController resets amount and fraction to 0, where
* until the next lockscreen-to-shade transition. * they remain until the next lockscreen-to-shade transition.
*/ */
public void setTransitionToFullShadeAmount(float fraction) { public void setTransitionToFullShadeAmount(float fraction) {
mView.setFractionToShade(fraction); mView.setFractionToShade(fraction);
@@ -1533,7 +1550,9 @@ public class NotificationStackScrollLayoutController {
mView.setExtraTopInsetForFullShadeTransition(overScrollAmount); mView.setExtraTopInsetForFullShadeTransition(overScrollAmount);
} }
/** */ /**
*
*/
public void setWillExpand(boolean willExpand) { public void setWillExpand(boolean willExpand) {
mView.setWillExpand(willExpand); mView.setWillExpand(willExpand);
} }
@@ -1549,7 +1568,7 @@ public class NotificationStackScrollLayoutController {
* Set rounded rect clipping bounds on this view. * Set rounded rect clipping bounds on this view.
*/ */
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius, 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); mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
} }
@@ -1568,6 +1587,15 @@ public class NotificationStackScrollLayoutController {
return mNotificationTargetsHelper; return mNotificationTargetsHelper;
} }
/**
* 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 for UiEvent logged from this class
*/ */
@@ -1578,10 +1606,13 @@ public class NotificationStackScrollLayoutController {
@UiEvent(doc = "User dismissed all silent notifications from notification panel.") @UiEvent(doc = "User dismissed all silent notifications from notification panel.")
DISMISS_SILENT_NOTIFICATIONS_PANEL(314); DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
private final int mId; private final int mId;
NotificationPanelEvent(int id) { NotificationPanelEvent(int id) {
mId = id; mId = id;
} }
@Override public int getId() {
@Override
public int getId() {
return mId; return mId;
} }
@@ -1722,8 +1753,12 @@ public class NotificationStackScrollLayoutController {
@Override @Override
public void bindRow(ExpandableNotificationRow row) { public void bindRow(ExpandableNotificationRow row) {
row.setHeadsUpAnimatingAwayListener(animatingAway -> { row.setHeadsUpAnimatingAwayListener(animatingAway -> {
mNotificationRoundnessManager.updateView(row, false); if (!mUseRoundnessSourceTypes) {
mHeadsUpAppearanceController.updateHeader(row.getEntry()); mNotificationRoundnessManager.updateView(row, false);
}
NotificationEntry entry = row.getEntry();
mHeadsUpAppearanceController.updateHeader(entry);
mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry);
}); });
} }

View File

@@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Rect; import android.graphics.Rect;
@@ -34,9 +35,11 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.SwipeHelper; import com.android.systemui.SwipeHelper;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; 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.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -49,6 +52,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
@VisibleForTesting @VisibleForTesting
protected static final long COVER_MENU_DELAY = 4000; protected static final long COVER_MENU_DELAY = 4000;
private static final String TAG = "NotificationSwipeHelper"; private static final String TAG = "NotificationSwipeHelper";
private static final SourceType SWIPE_DISMISS = SourceType.from("SwipeDismiss");
private final Runnable mFalsingCheck; private final Runnable mFalsingCheck;
private View mTranslatingParentView; private View mTranslatingParentView;
private View mMenuExposedView; private View mMenuExposedView;
@@ -64,13 +68,21 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef; private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef;
private boolean mIsExpanded; private boolean mIsExpanded;
private boolean mPulsing; private boolean mPulsing;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final boolean mUseRoundnessSourceTypes;
NotificationSwipeHelper( NotificationSwipeHelper(
Resources resources, ViewConfiguration viewConfiguration, Resources resources,
FalsingManager falsingManager, FeatureFlags featureFlags, ViewConfiguration viewConfiguration,
int swipeDirection, NotificationCallback callback, FalsingManager falsingManager,
NotificationMenuRowPlugin.OnMenuEventListener menuListener) { FeatureFlags featureFlags,
int swipeDirection,
NotificationCallback callback,
NotificationMenuRowPlugin.OnMenuEventListener menuListener,
NotificationRoundnessManager notificationRoundnessManager) {
super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags); super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags);
mNotificationRoundnessManager = notificationRoundnessManager;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mMenuListener = menuListener; mMenuListener = menuListener;
mCallback = callback; mCallback = callback;
mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */); mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */);
@@ -304,6 +316,33 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
handleMenuCoveredOrDismissed(); 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 @VisibleForTesting
protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) { protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
super.dismissChild(view, velocity, useAccelerateInterpolator); super.dismissChild(view, velocity, useAccelerateInterpolator);
@@ -521,14 +560,17 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
private int mSwipeDirection; private int mSwipeDirection;
private NotificationCallback mNotificationCallback; private NotificationCallback mNotificationCallback;
private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener; private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener;
private NotificationRoundnessManager mNotificationRoundnessManager;
@Inject @Inject
Builder(@Main Resources resources, ViewConfiguration viewConfiguration, Builder(@Main Resources resources, ViewConfiguration viewConfiguration,
FalsingManager falsingManager, FeatureFlags featureFlags) { FalsingManager falsingManager, FeatureFlags featureFlags,
NotificationRoundnessManager notificationRoundnessManager) {
mResources = resources; mResources = resources;
mViewConfiguration = viewConfiguration; mViewConfiguration = viewConfiguration;
mFalsingManager = falsingManager; mFalsingManager = falsingManager;
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mNotificationRoundnessManager = notificationRoundnessManager;
} }
Builder setSwipeDirection(int swipeDirection) { Builder setSwipeDirection(int swipeDirection) {
@@ -549,7 +591,8 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
NotificationSwipeHelper build() { NotificationSwipeHelper build() {
return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager, return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager,
mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener); mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener,
mNotificationRoundnessManager);
} }
} }
} }

View File

@@ -20,8 +20,7 @@ class NotificationTargetsHelper
constructor( constructor(
featureFlags: FeatureFlags, featureFlags: FeatureFlags,
) { ) {
private val isNotificationGroupCornerEnabled = private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_CORNER)
/** /**
* This method looks for views that can be rounded (and implement [Roundable]) during a * This method looks for views that can be rounded (and implement [Roundable]) during a
@@ -48,7 +47,7 @@ constructor(
if (notificationParent != null && childrenContainer != null) { if (notificationParent != null && childrenContainer != null) {
// We are inside a notification group // We are inside a notification group
if (!isNotificationGroupCornerEnabled) { if (!useRoundnessSourceTypes) {
return RoundableTargets(null, null, null) return RoundableTargets(null, null, null)
} }

View File

@@ -31,6 +31,7 @@ import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf; 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.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,6 +52,7 @@ public class StackScrollAlgorithm {
private static final String TAG = "StackScrollAlgorithm"; private static final String TAG = "StackScrollAlgorithm";
private static final Boolean DEBUG = false; private static final Boolean DEBUG = false;
private static final SourceType STACK_SCROLL_ALGO = SourceType.from("StackScrollAlgorithm");
private final ViewGroup mHostView; private final ViewGroup mHostView;
private float mPaddingBetweenElements; private float mPaddingBetweenElements;
@@ -70,6 +72,7 @@ public class StackScrollAlgorithm {
private float mQuickQsOffsetHeight; private float mQuickQsOffsetHeight;
private float mSmallCornerRadius; private float mSmallCornerRadius;
private float mLargeCornerRadius; private float mLargeCornerRadius;
private boolean mUseRoundnessSourceTypes;
public StackScrollAlgorithm( public StackScrollAlgorithm(
Context context, Context context,
@@ -129,7 +132,7 @@ public class StackScrollAlgorithm {
} }
private void updateAlphaState(StackScrollAlgorithmState algorithmState, private void updateAlphaState(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
for (ExpandableView view : algorithmState.visibleChildren) { for (ExpandableView view : algorithmState.visibleChildren) {
final ViewState viewState = view.getViewState(); final ViewState viewState = view.getViewState();
@@ -229,7 +232,7 @@ public class StackScrollAlgorithm {
} }
private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState, private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size(); int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
ExpandableView v = algorithmState.visibleChildren.get(i); ExpandableView v = algorithmState.visibleChildren.get(i);
@@ -241,7 +244,7 @@ public class StackScrollAlgorithm {
} }
private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState, private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
int speedBumpIndex) { int speedBumpIndex) {
int childCount = algorithmState.visibleChildren.size(); int childCount = algorithmState.visibleChildren.size();
int belowSpeedBump = speedBumpIndex; int belowSpeedBump = speedBumpIndex;
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
@@ -268,7 +271,7 @@ public class StackScrollAlgorithm {
} }
private void updateClipping(StackScrollAlgorithmState algorithmState, private void updateClipping(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
float drawStart = ambientState.isOnKeyguard() ? 0 float drawStart = ambientState.isOnKeyguard() ? 0
: ambientState.getStackY() - ambientState.getScrollY(); : ambientState.getStackY() - ambientState.getScrollY();
float clipStart = 0; float clipStart = 0;
@@ -314,7 +317,7 @@ public class StackScrollAlgorithm {
* Updates the dimmed, activated and hiding sensitive states of the children. * Updates the dimmed, activated and hiding sensitive states of the children.
*/ */
private void updateDimmedActivatedHideSensitive(AmbientState ambientState, private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
StackScrollAlgorithmState algorithmState) { StackScrollAlgorithmState algorithmState) {
boolean dimmed = ambientState.isDimmed(); boolean dimmed = ambientState.isDimmed();
boolean hideSensitive = ambientState.isHideSensitive(); boolean hideSensitive = ambientState.isHideSensitive();
View activatedChild = ambientState.getActivatedChild(); View activatedChild = ambientState.getActivatedChild();
@@ -408,7 +411,7 @@ public class StackScrollAlgorithm {
} }
private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex, private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
ExpandableView v) { ExpandableView v) {
ExpandableViewState viewState = v.getViewState(); ExpandableViewState viewState = v.getViewState();
viewState.notGoneIndex = notGoneIndex; viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v); state.visibleChildren.add(v);
@@ -434,7 +437,7 @@ public class StackScrollAlgorithm {
* @param ambientState The current ambient state * @param ambientState The current ambient state
*/ */
protected void updatePositionsForState(StackScrollAlgorithmState algorithmState, protected void updatePositionsForState(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
if (!ambientState.isOnKeyguard() if (!ambientState.isOnKeyguard()
|| (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) { || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
algorithmState.mCurrentYPosition += mNotificationScrimPadding; algorithmState.mCurrentYPosition += mNotificationScrimPadding;
@@ -448,7 +451,7 @@ public class StackScrollAlgorithm {
} }
private void setLocation(ExpandableViewState expandableViewState, float currentYPosition, private void setLocation(ExpandableViewState expandableViewState, float currentYPosition,
int i) { int i) {
expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA; expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
if (currentYPosition <= 0) { if (currentYPosition <= 0) {
expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP; expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
@@ -496,9 +499,13 @@ public class StackScrollAlgorithm {
} }
@VisibleForTesting @VisibleForTesting
void maybeUpdateHeadsUpIsVisible(ExpandableViewState viewState, boolean isShadeExpanded, void maybeUpdateHeadsUpIsVisible(
boolean mustStayOnScreen, boolean topVisible, float viewEnd, float hunMax) { ExpandableViewState viewState,
boolean isShadeExpanded,
boolean mustStayOnScreen,
boolean topVisible,
float viewEnd,
float hunMax) {
if (isShadeExpanded && mustStayOnScreen && topVisible) { if (isShadeExpanded && mustStayOnScreen && topVisible) {
viewState.headsUpIsVisible = viewEnd < hunMax; viewState.headsUpIsVisible = viewEnd < hunMax;
} }
@@ -676,7 +683,7 @@ public class StackScrollAlgorithm {
} }
private void updatePulsingStates(StackScrollAlgorithmState algorithmState, private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size(); int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i); View child = algorithmState.visibleChildren.get(i);
@@ -693,7 +700,7 @@ public class StackScrollAlgorithm {
} }
private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState, private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size(); int childCount = algorithmState.visibleChildren.size();
// Move the tracked heads up into position during the appear animation, by interpolating // Move the tracked heads up into position during the appear animation, by interpolating
@@ -777,7 +784,7 @@ public class StackScrollAlgorithm {
*/ */
@VisibleForTesting @VisibleForTesting
void clampHunToTop(float quickQsOffsetHeight, float stackTranslation, float collapsedHeight, void clampHunToTop(float quickQsOffsetHeight, float stackTranslation, float collapsedHeight,
ExpandableViewState viewState) { ExpandableViewState viewState) {
final float newTranslation = Math.max(quickQsOffsetHeight + stackTranslation, final float newTranslation = Math.max(quickQsOffsetHeight + stackTranslation,
viewState.getYTranslation()); viewState.getYTranslation());
@@ -792,7 +799,7 @@ public class StackScrollAlgorithm {
// Pin HUN to bottom of expanded QS // Pin HUN to bottom of expanded QS
// while the rest of notifications are scrolled offscreen. // while the rest of notifications are scrolled offscreen.
private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row, private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
ExpandableViewState childState) { ExpandableViewState childState) {
float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation(); float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
final float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding() final float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ ambientState.getStackTranslation(); + ambientState.getStackTranslation();
@@ -807,14 +814,19 @@ public class StackScrollAlgorithm {
// Animate pinned HUN bottom corners to and from original roundness. // Animate pinned HUN bottom corners to and from original roundness.
final float originalCornerRadius = final float originalCornerRadius =
row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius); row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius);
final float roundness = computeCornerRoundnessForPinnedHun(mHostView.getHeight(), final float bottomValue = computeCornerRoundnessForPinnedHun(mHostView.getHeight(),
ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius); 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 @VisibleForTesting
float computeCornerRoundnessForPinnedHun(float hostViewHeight, float stackY, 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. // 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 // 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 * @param ambientState The ambient state of the algorithm
*/ */
private void updateZValuesForState(StackScrollAlgorithmState algorithmState, private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) { AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size(); int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f; float childrenOnTop = 0.0f;
@@ -876,9 +888,9 @@ public class StackScrollAlgorithm {
* previous HUNs whose Z positions are greater than 0. * previous HUNs whose Z positions are greater than 0.
*/ */
protected float updateChildZValue(int i, float childrenOnTop, protected float updateChildZValue(int i, float childrenOnTop,
StackScrollAlgorithmState algorithmState, StackScrollAlgorithmState algorithmState,
AmbientState ambientState, AmbientState ambientState,
boolean isTopHun) { boolean isTopHun) {
ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = child.getViewState(); ExpandableViewState childViewState = child.getViewState();
float baseZ = ambientState.getBaseZHeight(); float baseZ = ambientState.getBaseZHeight();
@@ -950,6 +962,14 @@ public class StackScrollAlgorithm {
this.mIsExpanded = isExpanded; 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 { public static class StackScrollAlgorithmState {
/** /**

View File

@@ -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 static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_FRAME_VIEW;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.MathUtils;
import android.view.View; import android.view.View;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil; import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R; 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.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController; 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.HeadsUpStatusBarView;
import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 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.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 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.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.Clock; 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. * 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 @StatusBarFragmentScope
public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView> public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
@@ -59,12 +65,17 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
NotificationWakeUpCoordinator.WakeUpListener { NotificationWakeUpCoordinator.WakeUpListener {
public static final int CONTENT_FADE_DURATION = 110; public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100; 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 NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager; private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController; private final NotificationStackScrollLayoutController mStackScrollerController;
private final DarkIconDispatcher mDarkIconDispatcher; private final DarkIconDispatcher mDarkIconDispatcher;
private final NotificationPanelViewController mNotificationPanelViewController; private final NotificationPanelViewController mNotificationPanelViewController;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final boolean mUseRoundnessSourceTypes;
private final Consumer<ExpandableNotificationRow> private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp; mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction; private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
@@ -105,11 +116,15 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
CommandQueue commandQueue, CommandQueue commandQueue,
NotificationStackScrollLayoutController stackScrollerController, NotificationStackScrollLayoutController stackScrollerController,
NotificationPanelViewController notificationPanelViewController, NotificationPanelViewController notificationPanelViewController,
NotificationRoundnessManager notificationRoundnessManager,
FeatureFlags featureFlags,
HeadsUpStatusBarView headsUpStatusBarView, HeadsUpStatusBarView headsUpStatusBarView,
Clock clockView, Clock clockView,
@Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) { @Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) {
super(headsUpStatusBarView); super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController; mNotificationIconAreaController = notificationIconAreaController;
mNotificationRoundnessManager = notificationRoundnessManager;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mHeadsUpManager = headsUpManager; mHeadsUpManager = headsUpManager;
// We may be mid-HUN-expansion when this controller is re-created (for example, if the user // 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) { public void onHeadsUpPinned(NotificationEntry entry) {
updateTopEntry(); updateTopEntry();
updateHeader(entry); updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
} }
private void updateTopEntry() { private void updateTopEntry() {
@@ -316,6 +332,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
public void onHeadsUpUnPinned(NotificationEntry entry) { public void onHeadsUpUnPinned(NotificationEntry entry) {
updateTopEntry(); updateTopEntry();
updateHeader(entry); updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
} }
public void setAppearFraction(float expandedHeight, float appearFraction) { public void setAppearFraction(float expandedHeight, float appearFraction) {
@@ -346,7 +363,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
ExpandableNotificationRow previousTracked = mTrackedChild; ExpandableNotificationRow previousTracked = mTrackedChild;
mTrackedChild = trackedChild; mTrackedChild = trackedChild;
if (previousTracked != null) { 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() { private void updateHeadsUpHeaders() {
mHeadsUpManager.getAllEntries().forEach(entry -> { mHeadsUpManager.getAllEntries().forEach(entry -> {
updateHeader(entry); updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}); });
} }
@@ -370,6 +390,31 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
row.setHeaderVisibleAmount(headerVisibleAmount); 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 @Override
public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
mView.onDarkChanged(areas, darkIntensity, tint); mView.onDarkChanged(areas, darkIntensity, tint);

View File

@@ -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")
}
}

View File

@@ -215,8 +215,7 @@ public class NotificationTestHelper {
SourceType sourceType SourceType sourceType
) throws Exception { ) throws Exception {
ExpandableNotificationRow row = createRow(); ExpandableNotificationRow row = createRow();
row.requestTopRoundness(topRoundness, false, sourceType); row.requestRoundness(topRoundness, bottomRoundness, sourceType, /*animate = */ false);
row.requestBottomRoundness(bottomRoundness, /*animate = */ false, sourceType);
assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f); assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f);
assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f); assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f);
return row; return row;

View File

@@ -25,7 +25,7 @@ import android.view.View;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; 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.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -158,7 +158,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness( ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f, /* topRoundness = */ 1f,
/* bottomRoundness = */ 1f, /* bottomRoundness = */ 1f,
/* sourceType = */ SourceType.OnScroll); /* sourceType = */ LegacySourceType.OnScroll);
mChildrenContainer.addNotification(row, 0); mChildrenContainer.addNotification(row, 0);
@@ -171,11 +171,11 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness( ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f, /* topRoundness = */ 1f,
/* bottomRoundness = */ 1f, /* bottomRoundness = */ 1f,
/* sourceType = */ SourceType.DefaultValue); /* sourceType = */ LegacySourceType.DefaultValue);
ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness( ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f, /* topRoundness = */ 1f,
/* bottomRoundness = */ 1f, /* bottomRoundness = */ 1f,
/* sourceType = */ SourceType.OnDismissAnimation); /* sourceType = */ LegacySourceType.OnDismissAnimation);
mChildrenContainer.addNotification(row1, 0); mChildrenContainer.addNotification(row1, 0);
mChildrenContainer.addNotification(row2, 0); mChildrenContainer.addNotification(row2, 0);

View File

@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager; 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.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger; import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger;
@@ -73,7 +74,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mRoundnessManager = new NotificationRoundnessManager( mRoundnessManager = new NotificationRoundnessManager(
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext), new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext),
mLogger, mLogger,
mock(DumpManager.class)); mock(DumpManager.class),
mock(FeatureFlags.class));
allowTestableLooperAsMainThread(); allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper( NotificationTestHelper testHelper = new NotificationTestHelper(
mContext, mContext,

View File

@@ -30,6 +30,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarState;
@@ -59,10 +60,12 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private KeyguardMediaController mKeyguardMediaController; @Mock private KeyguardMediaController mKeyguardMediaController;
@Mock private NotificationSectionsFeatureManager mSectionsFeatureManager; @Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
@Mock private MediaContainerController mMediaContainerController; @Mock private MediaContainerController mMediaContainerController;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private SectionHeaderController mIncomingHeaderController; @Mock private SectionHeaderController mIncomingHeaderController;
@Mock private SectionHeaderController mPeopleHeaderController; @Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController; @Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController; @Mock private SectionHeaderController mSilentHeaderController;
@Mock private FeatureFlags mFeatureFlag;
private NotificationSectionsManager mSectionsManager; private NotificationSectionsManager mSectionsManager;
@@ -89,10 +92,12 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mKeyguardMediaController, mKeyguardMediaController,
mSectionsFeatureManager, mSectionsFeatureManager,
mMediaContainerController, mMediaContainerController,
mNotificationRoundnessManager,
mIncomingHeaderController, mIncomingHeaderController,
mPeopleHeaderController, mPeopleHeaderController,
mAlertingHeaderController, mAlertingHeaderController,
mSilentHeaderController mSilentHeaderController,
mFeatureFlag
); );
// Required in order for the header inflation to work properly // Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class))) when(mNssl.generateLayoutParams(any(AttributeSet.class)))

View File

@@ -9,7 +9,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView 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.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.row.NotificationTestHelper
@@ -314,9 +314,9 @@ class NotificationShelfTest : SysuiTestCase() {
val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f, /* topRoundness = */ 1f,
/* bottomRoundness = */ 1f, /* bottomRoundness = */ 1f,
/* sourceType = */ SourceType.OnScroll) /* sourceType = */ LegacySourceType.OnScroll)
NotificationShelf.resetOnScrollRoundness(row) NotificationShelf.resetLegacyOnScrollRoundness(row)
assertEquals(0f, row.topRoundness) assertEquals(0f, row.topRoundness)
assertEquals(0f, row.bottomRoundness) assertEquals(0f, row.bottomRoundness)
@@ -327,14 +327,14 @@ class NotificationShelfTest : SysuiTestCase() {
val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f, /* topRoundness = */ 1f,
/* bottomRoundness = */ 1f, /* bottomRoundness = */ 1f,
/* sourceType = */ SourceType.DefaultValue) /* sourceType = */ LegacySourceType.DefaultValue)
val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f, /* topRoundness = */ 1f,
/* bottomRoundness = */ 1f, /* bottomRoundness = */ 1f,
/* sourceType = */ SourceType.OnDismissAnimation) /* sourceType = */ LegacySourceType.OnDismissAnimation)
NotificationShelf.resetOnScrollRoundness(row1) NotificationShelf.resetLegacyOnScrollRoundness(row1)
NotificationShelf.resetOnScrollRoundness(row2) NotificationShelf.resetLegacyOnScrollRoundness(row2)
assertEquals(1f, row1.topRoundness) assertEquals(1f, row1.topRoundness)
assertEquals(1f, row1.bottomRoundness) assertEquals(1f, row1.bottomRoundness)

View File

@@ -74,6 +74,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
private NotificationSwipeHelper mSwipeHelper; private NotificationSwipeHelper mSwipeHelper;
private NotificationSwipeHelper.NotificationCallback mCallback; private NotificationSwipeHelper.NotificationCallback mCallback;
private NotificationMenuRowPlugin.OnMenuEventListener mListener; private NotificationMenuRowPlugin.OnMenuEventListener mListener;
private NotificationRoundnessManager mNotificationRoundnessManager;
private View mView; private View mView;
private MotionEvent mEvent; private MotionEvent mEvent;
private NotificationMenuRowPlugin mMenuRow; private NotificationMenuRowPlugin mMenuRow;
@@ -92,10 +93,17 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
public void setUp() throws Exception { public void setUp() throws Exception {
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class); mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class); mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
mFeatureFlags = mock(FeatureFlags.class); mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper( mSwipeHelper = spy(new NotificationSwipeHelper(
mContext.getResources(), ViewConfiguration.get(mContext), mContext.getResources(),
new FalsingManagerFake(), mFeatureFlags, SwipeHelper.X, mCallback, mListener)); ViewConfiguration.get(mContext),
new FalsingManagerFake(),
mFeatureFlags,
SwipeHelper.X,
mCallback,
mListener,
mNotificationRoundnessManager));
mView = mock(View.class); mView = mock(View.class);
mEvent = mock(MotionEvent.class); mEvent = mock(MotionEvent.class);
mMenuRow = mock(NotificationMenuRowPlugin.class); mMenuRow = mock(NotificationMenuRowPlugin.class);

View File

@@ -35,7 +35,7 @@ class NotificationTargetsHelperTest : SysuiTestCase() {
) = ) =
NotificationTargetsHelper( NotificationTargetsHelper(
FakeFeatureFlags().apply { FakeFeatureFlags().apply {
set(Flags.NOTIFICATION_GROUP_CORNER, notificationGroupCorner) set(Flags.USE_ROUNDNESS_SOURCETYPES, notificationGroupCorner)
} }
) )

View File

@@ -32,6 +32,7 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController; 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.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper; 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.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +73,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private NotificationWakeUpCoordinator mWakeUpCoordinator; private NotificationWakeUpCoordinator mWakeUpCoordinator;
private KeyguardStateController mKeyguardStateController; private KeyguardStateController mKeyguardStateController;
private CommandQueue mCommandQueue; private CommandQueue mCommandQueue;
private NotificationRoundnessManager mNotificationRoundnessManager;
private FeatureFlags mFeatureFlag;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -89,6 +93,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class); mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
mKeyguardStateController = mock(KeyguardStateController.class); mKeyguardStateController = mock(KeyguardStateController.class);
mCommandQueue = mock(CommandQueue.class); mCommandQueue = mock(CommandQueue.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
mFeatureFlag = mock(FeatureFlags.class);
mHeadsUpAppearanceController = new HeadsUpAppearanceController( mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class), mock(NotificationIconAreaController.class),
mHeadsUpManager, mHeadsUpManager,
@@ -100,6 +106,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mCommandQueue, mCommandQueue,
mStackScrollerController, mStackScrollerController,
mPanelView, mPanelView,
mNotificationRoundnessManager,
mFeatureFlag,
mHeadsUpStatusBarView, mHeadsUpStatusBarView,
new Clock(mContext, null), new Clock(mContext, null),
Optional.of(mOperatorNameView)); Optional.of(mOperatorNameView));
@@ -182,6 +190,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mCommandQueue, mCommandQueue,
mStackScrollerController, mStackScrollerController,
mPanelView, mPanelView,
mNotificationRoundnessManager,
mFeatureFlag,
mHeadsUpStatusBarView, mHeadsUpStatusBarView,
new Clock(mContext, null), new Clock(mContext, null),
Optional.empty()); Optional.empty());