Move "max to height" roundness logic to Roundable.kt

The sum of top and bottom corner radii should be at max the clipped
height of the roundable view.

This logic was spread out between the ExpandableOutlineView and the
ActivateableNotificationView. This CL moves it to the Roundable.kt.

Bug: b/243302608
Test: post HUNs and observe the appear/disappear animations
Test: see the corners changing as Notifs scroll into the Shelf
Change-Id: I3a615e81f7bfbb9e9f512d0b3ce745c019ea637f
This commit is contained in:
András Kurucz
2023-06-30 10:05:58 +00:00
parent e266393051
commit e9817b700d
8 changed files with 152 additions and 7 deletions

View File

@@ -3,7 +3,10 @@ package com.android.systemui.statusbar.notification
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -20,6 +23,8 @@ interface Roundable {
/** Properties required for a Roundable */
val roundableState: RoundableState
val clipHeight: Int
/** Current top roundness */
@get:FloatRange(from = 0.0, to = 1.0)
@JvmDefault
@@ -40,12 +45,16 @@ interface Roundable {
/** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
@JvmDefault
val topCornerRadius: Float
get() = topRoundness * maxRadius
get() =
if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
@JvmDefault
val bottomCornerRadius: Float
get() = bottomRoundness * maxRadius
get() =
if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
@JvmDefault
@@ -320,14 +329,20 @@ interface Roundable {
* @param roundable Target of the radius animation
* @param maxRadius Max corner radius in pixels
*/
class RoundableState(
class RoundableState
@JvmOverloads
constructor(
internal val targetView: View,
private val roundable: Roundable,
maxRadius: Float,
private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java)
) {
internal var maxRadius = maxRadius
private set
internal val newHeadsUpAnimFlagEnabled
get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS)
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
@@ -344,6 +359,41 @@ class RoundableState(
internal var bottomRoundness = 0f
private set
internal val topCornerRadius: Float
get() {
val height = roundable.clipHeight
val topRadius = topRoundness * maxRadius
val bottomRadius = bottomRoundness * maxRadius
if (height == 0) {
return 0f
} else if (topRadius + bottomRadius > height) {
// The sum of top and bottom corner radii should be at max the clipped height
val overShoot = topRadius + bottomRadius - height
return topRadius - (overShoot * topRoundness / (topRoundness + bottomRoundness))
}
return topRadius
}
internal val bottomCornerRadius: Float
get() {
val height = roundable.clipHeight
val topRadius = topRoundness * maxRadius
val bottomRadius = bottomRoundness * maxRadius
if (height == 0) {
return 0f
} else if (topRadius + bottomRadius > height) {
// The sum of top and bottom corner radii should be at max the clipped height
val overShoot = topRadius + bottomRadius - height
return bottomRadius -
(overShoot * bottomRoundness / (topRoundness + bottomRoundness))
}
return bottomRadius
}
/** Last requested top roundness associated by [SourceType] */
internal val topRoundnessMap = mutableMapOf<SourceType, Float>()

View File

@@ -566,12 +566,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public float getTopCornerRadius() {
if (isNewHeadsUpAnimFlagEnabled()) {
return super.getTopCornerRadius();
}
float fraction = getInterpolatedAppearAnimationFraction();
return MathUtils.lerp(0, super.getTopCornerRadius(), fraction);
}
@Override
public float getBottomCornerRadius() {
if (isNewHeadsUpAnimFlagEnabled()) {
return super.getBottomCornerRadius();
}
float fraction = getInterpolatedAppearAnimationFraction();
return MathUtils.lerp(0, super.getBottomCornerRadius(), fraction);
}

View File

@@ -28,7 +28,10 @@ import android.util.IndentingPrintWriter;
import android.view.View;
import android.view.ViewOutlineProvider;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -47,12 +50,14 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
private final FeatureFlags mFeatureFlags;
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
*/
protected boolean mDismissUsingRowTranslationX = true;
private float[] mTmpCornerRadii = new float[8];
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@@ -81,6 +86,15 @@ public abstract class ExpandableOutlineView extends ExpandableView {
return mRoundableState;
}
@Override
public int getClipHeight() {
if (mCustomOutline) {
return mOutlineRect.height();
}
return super.getClipHeight();
}
protected Path getClipPath(boolean ignoreTranslation) {
int left;
int top;
@@ -112,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
if (topRadius + bottomRadius > height) {
if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) {
float overShoot = topRadius + bottomRadius - height;
float currentTopRoundness = getTopRoundness();
float currentBottomRoundness = getBottomRoundness();
@@ -153,6 +167,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
super(context, attrs);
setOutlineProvider(mProvider);
initDimens();
mFeatureFlags = Dependency.get(FeatureFlags.class);
}
@Override
@@ -360,4 +375,9 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
});
}
// TODO(b/290365128) replace with ViewRefactorFlag
protected boolean isNewHeadsUpAnimFlagEnabled() {
return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS);
}
}

View File

@@ -93,6 +93,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
return mRoundableState;
}
@Override
public int getClipHeight() {
int clipHeight = Math.max(mActualHeight - mClipTopAmount - mClipBottomAmount, 0);
return Math.max(clipHeight, mMinimumHeightForClipping);
}
private void initDimens() {
mContentShift = getResources().getDimensionPixelSize(
R.dimen.shelf_transform_content_shift);

View File

@@ -119,6 +119,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
return mRoundableState;
}
@Override
public int getClipHeight() {
return mView.getHeight();
}
@Override
public void applyRoundnessAndInvalidate() {
if (mRoundnessChangedListener != null) {

View File

@@ -30,8 +30,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView
*/
class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) {
override var clipHeight = 0
var cornerRadius = 0f
var clipHeight = 0
var clipRect = RectF()
var clipPath = Path()

View File

@@ -191,6 +191,11 @@ public class NotificationChildrenContainer extends ViewGroup
return mRoundableState;
}
@Override
public int getClipHeight() {
return Math.max(mActualHeight - mClipBottomAmount, 0);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount =

View File

@@ -3,7 +3,11 @@ package com.android.systemui.statusbar.notification
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -15,8 +19,9 @@ import org.mockito.Mockito.verify
@SmallTest
@RunWith(JUnit4::class)
class RoundableTest : SysuiTestCase() {
val targetView: View = mock()
val roundable = FakeRoundable(targetView)
private val targetView: View = mock()
private val featureFlags = FakeFeatureFlags()
private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags)
@Test
fun defaultConfig_shouldNotHaveRoundedCorner() {
@@ -144,16 +149,62 @@ class RoundableTest : SysuiTestCase() {
assertEquals(0.2f, roundable.roundableState.bottomRoundness)
}
@Test
fun getCornerRadii_radius_maxed_to_height() {
whenever(targetView.height).thenReturn(10)
featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(1f, 1f, SOURCE1)
assertCornerRadiiEquals(5f, 5f)
}
@Test
fun getCornerRadii_topRadius_maxed_to_height() {
whenever(targetView.height).thenReturn(5)
featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(1f, 0f, SOURCE1)
assertCornerRadiiEquals(5f, 0f)
}
@Test
fun getCornerRadii_bottomRadius_maxed_to_height() {
whenever(targetView.height).thenReturn(5)
featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(0f, 1f, SOURCE1)
assertCornerRadiiEquals(0f, 5f)
}
@Test
fun getCornerRadii_radii_kept() {
whenever(targetView.height).thenReturn(100)
featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(1f, 1f, SOURCE1)
assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS)
}
private fun assertCornerRadiiEquals(top: Float, bottom: Float) {
assertEquals("topCornerRadius", top, roundable.topCornerRadius)
assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius)
}
class FakeRoundable(
targetView: View,
radius: Float = MAX_RADIUS,
featureFlags: FeatureFlags
) : Roundable {
override val roundableState =
RoundableState(
targetView = targetView,
roundable = this,
maxRadius = radius,
featureFlags = featureFlags
)
override val clipHeight: Int
get() = roundableState.targetView.height
}
companion object {