diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 2334a4c27af8b..9421524c73a76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -90,8 +90,13 @@ object LiftReveal : LightRevealEffect { class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect { // Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster - private val interpolator = PathInterpolator(/* controlX1= */ 0.4f, /* controlY1= */ 0f, - /* controlX2= */ 0.2f, /* controlY2= */ 1f) + private val interpolator = + PathInterpolator( + /* controlX1= */ 0.4f, + /* controlY1= */ 0f, + /* controlX2= */ 0.2f, + /* controlY2= */ 1f + ) override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) { val interpolatedAmount = interpolator.getInterpolation(amount) @@ -116,17 +121,17 @@ class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffe if (isVertical) { scrim.setRevealGradientBounds( - left = scrim.width / 2 - (scrim.width / 2) * gradientBoundsAmount, + left = scrim.viewWidth / 2 - (scrim.viewWidth / 2) * gradientBoundsAmount, top = 0f, - right = scrim.width / 2 + (scrim.width / 2) * gradientBoundsAmount, - bottom = scrim.height.toFloat() + right = scrim.viewWidth / 2 + (scrim.viewWidth / 2) * gradientBoundsAmount, + bottom = scrim.viewHeight.toFloat() ) } else { scrim.setRevealGradientBounds( left = 0f, - top = scrim.height / 2 - (scrim.height / 2) * gradientBoundsAmount, - right = scrim.width.toFloat(), - bottom = scrim.height / 2 + (scrim.height / 2) * gradientBoundsAmount + top = scrim.viewHeight / 2 - (scrim.viewHeight / 2) * gradientBoundsAmount, + right = scrim.viewWidth.toFloat(), + bottom = scrim.viewHeight / 2 + (scrim.viewHeight / 2) * gradientBoundsAmount ) } } @@ -234,7 +239,14 @@ class PowerButtonReveal( * transparent center. The center position, size, and stops of the gradient can be manipulated to * reveal views below the scrim as if they are being 'lit up'. */ -class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, attrs) { +class LightRevealScrim +@JvmOverloads +constructor( + context: Context?, + attrs: AttributeSet?, + initialWidth: Int? = null, + initialHeight: Int? = null +) : View(context, attrs) { /** Listener that is called if the scrim's opaqueness changes */ lateinit var isScrimOpaqueChangedListener: Consumer @@ -277,6 +289,17 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, var revealGradientWidth: Float = 0f var revealGradientHeight: Float = 0f + /** + * Keeps the initial value until the view is measured. See [LightRevealScrim.onMeasure]. + * + * Needed as the view dimensions are used before the onMeasure pass happens, and without preset + * width and height some flicker during fold/unfold happens. + */ + internal var viewWidth: Int = initialWidth ?: 0 + private set + internal var viewHeight: Int = initialHeight ?: 0 + private set + /** * Alpha of the fill that can be used in the beginning of the animation to hide the content. * Normally the gradient bounds are animated from small size so the content is not visible, but @@ -375,6 +398,11 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, invalidate() } + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + viewWidth = measuredWidth + viewHeight = measuredHeight + } /** * Sets bounds for the transparent oval gradient that reveals the views below the scrim. This is * simply a helper method that sets [revealGradientCenter], [revealGradientWidth], and diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt index 9cca950287296..523cf68891a08 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt @@ -159,18 +159,24 @@ constructor( ensureOverlayRemoved() val newRoot = SurfaceControlViewHost(context, context.display!!, wwm) - val newView = - LightRevealScrim(context, null).apply { - revealEffect = createLightRevealEffect() - isScrimOpaqueChangedListener = Consumer {} - revealAmount = - when (reason) { - FOLD -> TRANSPARENT - UNFOLD -> BLACK - } - } - val params = getLayoutParams() + val newView = + LightRevealScrim( + context, + attrs = null, + initialWidth = params.width, + initialHeight = params.height + ) + .apply { + revealEffect = createLightRevealEffect() + isScrimOpaqueChangedListener = Consumer {} + revealAmount = + when (reason) { + FOLD -> TRANSPARENT + UNFOLD -> BLACK + } + } + newRoot.setView(newView, params) if (onOverlayReady != null) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt index 97fe25d9a6193..d3befb4ad4cd2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt @@ -20,6 +20,7 @@ import android.testing.AndroidTestingRunner import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -36,7 +37,7 @@ class LightRevealScrimTest : SysuiTestCase() { @Before fun setUp() { - scrim = LightRevealScrim(context, null) + scrim = LightRevealScrim(context, null, DEFAULT_WIDTH, DEFAULT_HEIGHT) scrim.isScrimOpaqueChangedListener = Consumer { opaque -> isOpaque = opaque } @@ -63,4 +64,25 @@ class LightRevealScrimTest : SysuiTestCase() { scrim.revealAmount = 0.5f assertFalse("Scrim is opaque even though it's revealed", scrim.isScrimOpaque) } + + @Test + fun testBeforeOnMeasure_defaultDimensions() { + assertThat(scrim.viewWidth).isEqualTo(DEFAULT_WIDTH) + assertThat(scrim.viewHeight).isEqualTo(DEFAULT_HEIGHT) + } + + @Test + fun testAfterOnMeasure_measuredDimensions() { + scrim.measure(/* widthMeasureSpec= */ exact(1), /* heightMeasureSpec= */ exact(2)) + + assertThat(scrim.viewWidth).isEqualTo(1) + assertThat(scrim.viewHeight).isEqualTo(2) + } + + private fun exact(value: Int) = View.MeasureSpec.makeMeasureSpec(value, View.MeasureSpec.EXACTLY) + + private companion object { + private const val DEFAULT_WIDTH = 42 + private const val DEFAULT_HEIGHT = 24 + } } \ No newline at end of file