Merge "Define clear ownership for roundness SourceType" into tm-qpr-dev

This commit is contained in:
Omar Miatello
2022-12-06 17:34:59 +00:00
committed by Android (Google) Code Review
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)
@JvmField
val NOTIFICATION_GROUP_CORNER =
unreleasedFlag(116, "notification_group_corner", teamfood = true)
val USE_ROUNDNESS_SOURCETYPES = unreleasedFlag(116, "use_roundness_sourcetype", teamfood = true)
// TODO(b/259217907)
@JvmField

View File

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

View File

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

View File

@@ -74,8 +74,8 @@ interface Roundable {
@JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
animate: Boolean,
sourceType: SourceType,
animate: Boolean,
): Boolean {
val roundnessMap = roundableState.topRoundnessMap
val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -104,6 +104,30 @@ interface Roundable {
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].
*
@@ -119,8 +143,8 @@ interface Roundable {
@JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
animate: Boolean,
sourceType: SourceType,
animate: Boolean,
): Boolean {
val roundnessMap = roundableState.bottomRoundnessMap
val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -149,9 +173,101 @@ interface Roundable {
return false
}
/**
* Request the bottom roundness [value] for a specific [sourceType]. Animate the roundness if
* the view is shown.
*
* The bottom roundness of a [Roundable] can be defined by different [sourceType]. In case more
* origins require different roundness, for the same property, the maximum value will always be
* chosen.
*
* @param value value between 0f and 1f.
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
@JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
): Boolean {
return requestBottomRoundness(
value = value,
sourceType = sourceType,
animate = roundableState.targetView.isShown
)
}
/**
* Request the roundness [value] for a specific [sourceType].
*
* The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
* more origins require different roundness, for the same property, the maximum value will
* always be chosen.
*
* @param top top value between 0f and 1f.
* @param bottom bottom value between 0f and 1f.
* @param sourceType the source from which the request for roundness comes.
* @param animate true if it should animate to that value.
* @return Whether the roundness was changed.
*/
@JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
sourceType: SourceType,
animate: Boolean,
): Boolean {
val hasTopChanged =
requestTopRoundness(value = top, sourceType = sourceType, animate = animate)
val hasBottomChanged =
requestBottomRoundness(value = bottom, sourceType = sourceType, animate = animate)
return hasTopChanged || hasBottomChanged
}
/**
* Request the roundness [value] for a specific [sourceType]. Animate the roundness if the view
* is shown.
*
* The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
* more origins require different roundness, for the same property, the maximum value will
* always be chosen.
*
* @param top top value between 0f and 1f.
* @param bottom bottom value between 0f and 1f.
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
@JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
sourceType: SourceType,
): Boolean {
return requestRoundness(
top = top,
bottom = bottom,
sourceType = sourceType,
animate = roundableState.targetView.isShown,
)
}
/**
* Request the roundness 0f for a [SourceType]. Animate the roundness if the view is shown.
*
* The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
* more origins require different roundness, for the same property, the maximum value will
* always be chosen.
*
* @param sourceType the source from which the request for roundness comes.
*/
@JvmDefault
fun requestRoundnessReset(sourceType: SourceType) {
requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType)
}
/** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
@JvmDefault
fun applyRoundness() {
fun applyRoundnessAndInvalidate() {
roundableState.targetView.invalidate()
}
@@ -227,7 +343,7 @@ class RoundableState(
/** Set the current top roundness */
internal fun setTopRoundness(
value: Float,
animated: Boolean = targetView.isShown,
animated: Boolean,
) {
PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated)
}
@@ -235,11 +351,19 @@ class RoundableState(
/** Set the current bottom roundness */
internal fun setBottomRoundness(
value: Float,
animated: Boolean = targetView.isShown,
animated: Boolean,
) {
PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated)
}
fun debugString() = buildString {
append("TargetView: ${targetView.hashCode()} ")
append("Top: $topRoundness ")
append(topRoundnessMap.map { "${it.key} ${it.value}" })
append(" Bottom: $bottomRoundness ")
append(bottomRoundnessMap.map { "${it.key} ${it.value}" })
}
companion object {
private val DURATION: AnimationProperties =
AnimationProperties()
@@ -252,7 +376,7 @@ class RoundableState(
override fun setValue(view: View, value: Float) {
roundable.roundableState.topRoundness = value
roundable.applyRoundness()
roundable.applyRoundnessAndInvalidate()
}
},
R.id.top_roundess_animator_tag,
@@ -267,7 +391,7 @@ class RoundableState(
override fun setValue(view: View, value: Float) {
roundable.roundableState.bottomRoundness = value
roundable.applyRoundness()
roundable.applyRoundnessAndInvalidate()
}
},
R.id.bottom_roundess_animator_tag,
@@ -277,7 +401,31 @@ class RoundableState(
}
}
enum class SourceType {
/**
* Interface used to define the owner of a roundness. Usually the [SourceType] is defined as a
* private property of a class.
*/
interface SourceType {
companion object {
/**
* This is the most convenient way to define a new [SourceType].
*
* For example:
*
* ```kotlin
* private val SECTION = SourceType.from("Section")
* ```
*/
@JvmStatic
fun from(name: String) =
object : SourceType {
override fun toString() = name
}
}
}
@Deprecated("Use SourceType.from() instead", ReplaceWith("SourceType.from()"))
enum class LegacySourceType : SourceType {
DefaultValue,
OnDismissAnimation,
OnScroll,

View File

@@ -39,9 +39,13 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.HashSet;
import java.util.Set;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
* to implement dimming/activating on Keyguard for the double-tap gesture
@@ -91,6 +95,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
= new PathInterpolator(0.6f, 0, 0.5f, 1);
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
= new PathInterpolator(0, 0, 0.5f, 1);
private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>();
private int mTintedRippleColor;
private int mNormalRippleColor;
private Gefingerpoken mTouchHandler;
@@ -134,6 +139,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private boolean mDismissed;
private boolean mRefocusOnDismiss;
private AccessibilityManager mAccessibilityManager;
protected boolean mUseRoundnessSourceTypes;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -613,9 +619,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
protected void resetAllContentAlphas() {}
@Override
public void applyRoundness() {
super.applyRoundness();
public void applyRoundnessAndInvalidate() {
applyBackgroundRoundness(getTopCornerRadius(), getBottomCornerRadius());
super.applyRoundnessAndInvalidate();
}
@Override
@@ -775,6 +781,33 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mAccessibilityManager = accessibilityManager;
}
/**
* Enable the support for rounded corner based on the SourceType
* @param enabled true if is supported
*/
public void useRoundnessSourceTypes(boolean enabled) {
mUseRoundnessSourceTypes = enabled;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mUseRoundnessSourceTypes && !mOnDetachResetRoundness.isEmpty()) {
for (SourceType sourceType : mOnDetachResetRoundness) {
requestRoundnessReset(sourceType);
}
mOnDetachResetRoundness.clear();
}
}
/**
* SourceType which should be reset when this View is detached
* @param sourceType will be reset on View detached
*/
public void addOnDetachResetRoundness(SourceType sourceType) {
mOnDetachResetRoundness.add(sourceType);
}
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);

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.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -143,6 +144,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private static final int MENU_VIEW_INDEX = 0;
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
private static final SourceType FROM_PARENT = SourceType.from("FromParent(ENR)");
private static final SourceType PINNED = SourceType.from("Pinned");
// We don't correctly track dark mode until the content views are inflated, so always update
// the background on first content update just in case it happens to be during a theme change.
@@ -150,6 +154,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mNotificationTranslationFinished = false;
private boolean mIsSnoozed;
private boolean mIsFaded;
private boolean mAnimatePinnedRoundness = false;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -376,7 +381,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private float mTopRoundnessDuringLaunchAnimation;
private float mBottomRoundnessDuringLaunchAnimation;
private boolean mIsNotificationGroupCornerEnabled;
private float mSmallRoundness;
/**
* Returns whether the given {@code statusBarNotification} is a system notification.
@@ -844,7 +849,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
onAttachedChildrenCountChanged();
row.setIsChildInGroup(false, null);
row.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
if (!mUseRoundnessSourceTypes) {
row.requestBottomRoundness(0.0f, LegacySourceType.DefaultValue, /* animate = */ false);
}
}
/**
@@ -860,7 +867,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (child.keepInParentForDismissAnimation()) {
mChildrenContainer.removeNotification(child);
child.setIsChildInGroup(false, null);
child.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
if (!mUseRoundnessSourceTypes) {
LegacySourceType sourceType = LegacySourceType.DefaultValue;
child.requestBottomRoundness(0f, sourceType, /* animate = */ false);
}
child.setKeepInParentForDismissAnimation(false);
logKeepInParentChildDetached(child);
childCountChanged = true;
@@ -915,6 +925,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationParent.updateBackgroundForGroupState();
}
updateBackgroundClipping();
if (mUseRoundnessSourceTypes) {
updateBaseRoundness();
}
}
@Override
@@ -1033,6 +1046,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
if (mUseRoundnessSourceTypes) {
if (pinned) {
// Should be animated if someone explicitly set it to 0 and the row is shown.
boolean animated = mAnimatePinnedRoundness && isShown();
requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated);
} else {
requestRoundnessReset(PINNED);
mAnimatePinnedRoundness = true;
}
}
}
@Override
@@ -1607,6 +1630,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
super(context, attrs);
mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache());
float radius = getResources().getDimension(R.dimen.notification_corner_radius_small);
mSmallRoundness = radius / getMaxRadius();
initDimens();
}
@@ -1839,7 +1864,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setIsLowPriority(mIsLowPriority);
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled);
mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
mTranslateableViews.add(mChildrenContainer);
});
@@ -2271,7 +2296,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public float getTopRoundness() {
if (mExpandAnimationRunning) {
if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
return mTopRoundnessDuringLaunchAnimation;
}
@@ -2280,7 +2305,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public float getBottomRoundness() {
if (mExpandAnimationRunning) {
if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
return mBottomRoundnessDuringLaunchAnimation;
}
@@ -3436,17 +3461,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
public void applyRoundness() {
super.applyRoundness();
public void applyRoundnessAndInvalidate() {
applyChildrenRoundness();
super.applyRoundnessAndInvalidate();
}
private void applyChildrenRoundness() {
if (mIsSummaryWithChildren) {
mChildrenContainer.requestBottomRoundness(
getBottomRoundness(),
/* animate = */ false,
SourceType.DefaultValue);
if (mUseRoundnessSourceTypes) {
mChildrenContainer.requestRoundness(
/* top = */ getTopRoundness(),
/* bottom = */ getBottomRoundness(),
FROM_PARENT);
} else {
mChildrenContainer.requestBottomRoundness(
getBottomRoundness(),
LegacySourceType.DefaultValue,
/* animate = */ false);
}
}
}
@@ -3605,6 +3637,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else {
pw.println("no viewState!!!");
}
pw.println("Roundness: " + getRoundableState().debugString());
if (mIsSummaryWithChildren) {
pw.println();
@@ -3649,14 +3682,38 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mTargetPoint;
}
/** Update the minimum roundness based on current state */
private void updateBaseRoundness() {
if (isChildInGroup()) {
requestRoundnessReset(BASE_VALUE);
} else {
requestRoundness(mSmallRoundness, mSmallRoundness, BASE_VALUE);
}
}
/**
* Enable the support for rounded corner in notification group
* Enable the support for rounded corner based on the SourceType
* @param enabled true if is supported
*/
public void enableNotificationGroupCorner(boolean enabled) {
mIsNotificationGroupCornerEnabled = enabled;
@Override
public void useRoundnessSourceTypes(boolean enabled) {
super.useRoundnessSourceTypes(enabled);
if (mChildrenContainer != null) {
mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled);
mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
}
}
@Override
public String toString() {
String roundableStateDebug = "RoundableState = " + getRoundableState().debugString();
return "ExpandableNotificationRow:" + hashCode() + " { " + roundableStateDebug + " }";
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mUseRoundnessSourceTypes) {
updateBaseRoundness();
}
}
}

View File

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

View File

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

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

View File

@@ -25,6 +25,9 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.SourceType;
@@ -45,6 +48,7 @@ import javax.inject.Inject;
public class NotificationRoundnessManager implements Dumpable {
private static final String TAG = "NotificationRoundnessManager";
private static final SourceType DISMISS_ANIMATION = SourceType.from("DismissAnimation");
private final ExpandableView[] mFirstInSectionViews;
private final ExpandableView[] mLastInSectionViews;
@@ -63,12 +67,14 @@ public class NotificationRoundnessManager implements Dumpable {
private ExpandableView mSwipedView = null;
private Roundable mViewBeforeSwipedView = null;
private Roundable mViewAfterSwipedView = null;
private boolean mUseRoundnessSourceTypes;
@Inject
NotificationRoundnessManager(
NotificationSectionsFeatureManager sectionsFeatureManager,
NotificationRoundnessLogger notifLogger,
DumpManager dumpManager) {
DumpManager dumpManager,
FeatureFlags featureFlags) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections];
@@ -76,6 +82,7 @@ public class NotificationRoundnessManager implements Dumpable {
mTmpLastInSectionViews = new ExpandableView[numberOfSections];
mNotifLogger = notifLogger;
mDumpManager = dumpManager;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mDumpManager.registerDumpable(TAG, this);
}
@@ -94,6 +101,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
public void updateView(ExpandableView view, boolean animate) {
if (mUseRoundnessSourceTypes) return;
boolean changed = updateViewWithoutCallback(view, animate);
if (changed) {
mRoundingChangedCallback.run();
@@ -110,6 +118,7 @@ public class NotificationRoundnessManager implements Dumpable {
boolean updateViewWithoutCallback(
ExpandableView view,
boolean animate) {
if (mUseRoundnessSourceTypes) return false;
if (view == null
|| view == mViewBeforeSwipedView
|| view == mViewAfterSwipedView) {
@@ -118,13 +127,13 @@ public class NotificationRoundnessManager implements Dumpable {
final boolean isTopChanged = view.requestTopRoundness(
getRoundnessDefaultValue(view, true /* top */),
animate,
SourceType.DefaultValue);
LegacySourceType.DefaultValue,
animate);
final boolean isBottomChanged = view.requestBottomRoundness(
getRoundnessDefaultValue(view, /* top = */ false),
animate,
SourceType.DefaultValue);
LegacySourceType.DefaultValue,
animate);
final boolean isFirstInSection = isFirstInSection(view);
final boolean isLastInSection = isLastInSection(view);
@@ -139,6 +148,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
private boolean isFirstInSection(ExpandableView view) {
if (mUseRoundnessSourceTypes) return false;
for (int i = 0; i < mFirstInSectionViews.length; i++) {
if (view == mFirstInSectionViews[i]) {
return true;
@@ -148,6 +158,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
private boolean isLastInSection(ExpandableView view) {
if (mUseRoundnessSourceTypes) return false;
for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
if (view == mLastInSectionViews[i]) {
return true;
@@ -160,9 +171,6 @@ public class NotificationRoundnessManager implements Dumpable {
Roundable viewBefore,
ExpandableView viewSwiped,
Roundable viewAfter) {
final boolean animate = true;
final SourceType source = SourceType.OnDismissAnimation;
// This method requires you to change the roundness of the current View targets and reset
// the roundness of the old View targets (if any) to 0f.
// To avoid conflicts, it generates a set of old Views and removes the current Views
@@ -172,31 +180,34 @@ public class NotificationRoundnessManager implements Dumpable {
if (mSwipedView != null) oldViews.add(mSwipedView);
if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView);
final SourceType source;
if (mUseRoundnessSourceTypes) {
source = DISMISS_ANIMATION;
} else {
source = LegacySourceType.OnDismissAnimation;
}
mViewBeforeSwipedView = viewBefore;
if (viewBefore != null) {
oldViews.remove(viewBefore);
viewBefore.requestTopRoundness(0f, animate, source);
viewBefore.requestBottomRoundness(1f, animate, source);
viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, source);
}
mSwipedView = viewSwiped;
if (viewSwiped != null) {
oldViews.remove(viewSwiped);
viewSwiped.requestTopRoundness(1f, animate, source);
viewSwiped.requestBottomRoundness(1f, animate, source);
viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, source);
}
mViewAfterSwipedView = viewAfter;
if (viewAfter != null) {
oldViews.remove(viewAfter);
viewAfter.requestTopRoundness(1f, animate, source);
viewAfter.requestBottomRoundness(0f, animate, source);
viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, source);
}
// After setting the current Views, reset the views that are still present in the set.
for (Roundable oldView : oldViews) {
oldView.requestTopRoundness(0f, animate, source);
oldView.requestBottomRoundness(0f, animate, source);
oldView.requestRoundnessReset(source);
}
}
@@ -204,7 +215,23 @@ public class NotificationRoundnessManager implements Dumpable {
mIsClearAllInProgress = isClearingAll;
}
/**
* Check if "Clear all" notifications is in progress.
*/
public boolean isClearAllInProgress() {
return mIsClearAllInProgress;
}
/**
* Check if we can request the `Pulsing` roundness for notification.
*/
public boolean shouldRoundNotificationPulsing() {
return mRoundForPulsingViews;
}
private float getRoundnessDefaultValue(Roundable view, boolean top) {
if (mUseRoundnessSourceTypes) return 0f;
if (view == null) {
return 0f;
}
@@ -250,6 +277,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
public void setExpanded(float expandedHeight, float appearFraction) {
if (mUseRoundnessSourceTypes) return;
mExpanded = expandedHeight != 0.0f;
mAppearFraction = appearFraction;
if (mTrackedHeadsUp != null) {
@@ -258,6 +286,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
public void updateRoundedChildren(NotificationSection[] sections) {
if (mUseRoundnessSourceTypes) return;
boolean anyChanged = false;
for (int i = 0; i < sections.length; i++) {
mTmpFirstInSectionViews[i] = mFirstInSectionViews[i];
@@ -280,6 +309,7 @@ public class NotificationRoundnessManager implements Dumpable {
NotificationSection[] sections,
ExpandableView[] oldViews,
boolean first) {
if (mUseRoundnessSourceTypes) return false;
boolean anyChanged = false;
for (ExpandableView oldView : oldViews) {
if (oldView != null) {
@@ -313,6 +343,7 @@ public class NotificationRoundnessManager implements Dumpable {
NotificationSection[] sections,
ExpandableView[] oldViews,
boolean first) {
if (mUseRoundnessSourceTypes) return false;
boolean anyChanged = false;
for (NotificationSection section : sections) {
ExpandableView newView =
@@ -339,6 +370,15 @@ public class NotificationRoundnessManager implements Dumpable {
mAnimatedChildren = animatedChildren;
}
/**
* Check if the view should be animated
* @param view target view
* @return true, if is in the AnimatedChildren set
*/
public boolean isAnimatedChild(ExpandableView view) {
return mAnimatedChildren.contains(view);
}
public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
mRoundingChangedCallback = roundingChangedCallback;
}

View File

@@ -19,8 +19,11 @@ import android.annotation.ColorInt
import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.ui.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
import com.android.systemui.statusbar.notification.dagger.AlertingHeader
@@ -44,12 +47,16 @@ class NotificationSectionsManager @Inject internal constructor(
private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val mediaContainerController: MediaContainerController,
private val notificationRoundnessManager: NotificationRoundnessManager,
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController,
@SilentHeader private val silentHeaderController: SectionHeaderController
@SilentHeader private val silentHeaderController: SectionHeaderController,
featureFlags: FeatureFlags
) : SectionProvider {
private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() {
reinflateViews()
@@ -177,11 +184,49 @@ class NotificationSectionsManager @Inject internal constructor(
size = sections.size,
operation = SectionBounds::addNotif
)
// Build a set of the old first/last Views of the sections
val oldFirstChildren = sections.mapNotNull { it.firstVisibleChild }.toSet().toMutableSet()
val oldLastChildren = sections.mapNotNull { it.lastVisibleChild }.toSet().toMutableSet()
// Update each section with the associated boundary, tracking if there was a change
val changed = sections.fold(false) { changed, section ->
val bounds = sectionBounds[section.bucket] ?: SectionBounds.None
bounds.updateSection(section) || changed
val isSectionChanged = bounds.updateSection(section)
isSectionChanged || changed
}
if (useRoundnessSourceTypes) {
val newFirstChildren = sections.mapNotNull { it.firstVisibleChild }
val newLastChildren = sections.mapNotNull { it.lastVisibleChild }
// Update the roundness of Views that weren't already in the first/last position
newFirstChildren.forEach { firstChild ->
val wasFirstChild = oldFirstChildren.remove(firstChild)
if (!wasFirstChild) {
val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild)
val animated = firstChild.isShown && notAnimatedChild
firstChild.requestTopRoundness(1f, SECTION, animated)
}
}
newLastChildren.forEach { lastChild ->
val wasLastChild = oldLastChildren.remove(lastChild)
if (!wasLastChild) {
val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild)
val animated = lastChild.isShown && notAnimatedChild
lastChild.requestBottomRoundness(1f, SECTION, animated)
}
}
// The Views left in the set are no longer in the first/last position
oldFirstChildren.forEach { noMoreFirstChild ->
noMoreFirstChild.requestTopRoundness(0f, SECTION)
}
oldLastChildren.forEach { noMoreLastChild ->
noMoreLastChild.requestBottomRoundness(0f, SECTION)
}
}
if (DEBUG) {
logSections(sections)
}
@@ -215,5 +260,6 @@ class NotificationSectionsManager @Inject internal constructor(
companion object {
private const val TAG = "NotifSectionsManager"
private const val DEBUG = false
private val SECTION = SourceType.from("Section")
}
}

View File

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

View File

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

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

View File

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

View File

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

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 android.graphics.Rect;
import android.util.MathUtils;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -32,8 +35,10 @@ import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.Clock;
@@ -51,6 +56,7 @@ import javax.inject.Named;
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
* It also controls the roundness of the heads up notifications and the pulsing notifications.
*/
@StatusBarFragmentScope
public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
@@ -59,12 +65,17 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
NotificationWakeUpCoordinator.WakeUpListener {
public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100;
private static final SourceType HEADS_UP = SourceType.from("HeadsUp");
private static final SourceType PULSING = SourceType.from("Pulsing");
private final NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
private final DarkIconDispatcher mDarkIconDispatcher;
private final NotificationPanelViewController mNotificationPanelViewController;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final boolean mUseRoundnessSourceTypes;
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
@@ -105,11 +116,15 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
CommandQueue commandQueue,
NotificationStackScrollLayoutController stackScrollerController,
NotificationPanelViewController notificationPanelViewController,
NotificationRoundnessManager notificationRoundnessManager,
FeatureFlags featureFlags,
HeadsUpStatusBarView headsUpStatusBarView,
Clock clockView,
@Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) {
super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController;
mNotificationRoundnessManager = notificationRoundnessManager;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mHeadsUpManager = headsUpManager;
// We may be mid-HUN-expansion when this controller is re-created (for example, if the user
@@ -179,6 +194,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
public void onHeadsUpPinned(NotificationEntry entry) {
updateTopEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
private void updateTopEntry() {
@@ -316,6 +332,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
public void onHeadsUpUnPinned(NotificationEntry entry) {
updateTopEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
public void setAppearFraction(float expandedHeight, float appearFraction) {
@@ -346,7 +363,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
ExpandableNotificationRow previousTracked = mTrackedChild;
mTrackedChild = trackedChild;
if (previousTracked != null) {
updateHeader(previousTracked.getEntry());
NotificationEntry entry = previousTracked.getEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
}
@@ -357,6 +376,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
private void updateHeadsUpHeaders() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
});
}
@@ -370,6 +390,31 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
row.setHeaderVisibleAmount(headerVisibleAmount);
}
/**
* Update the HeadsUp and the Pulsing roundness based on current state
* @param entry target notification
*/
public void updateHeadsUpAndPulsingRoundness(NotificationEntry entry) {
if (mUseRoundnessSourceTypes) {
ExpandableNotificationRow row = entry.getRow();
boolean isTrackedChild = row == mTrackedChild;
if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) {
float roundness = MathUtils.saturate(1f - mAppearFraction);
row.requestRoundness(roundness, roundness, HEADS_UP);
} else {
row.requestRoundnessReset(HEADS_UP);
}
if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) {
if (row.showingPulsing()) {
row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING);
} else {
row.requestRoundnessReset(PULSING);
}
}
}
}
@Override
public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
mView.onDarkChanged(areas, darkIntensity, tint);

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
) throws Exception {
ExpandableNotificationRow row = createRow();
row.requestTopRoundness(topRoundness, false, sourceType);
row.requestBottomRoundness(bottomRoundness, /*animate = */ false, sourceType);
row.requestRoundness(topRoundness, bottomRoundness, sourceType, /*animate = */ false);
assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f);
assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f);
return row;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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