Fix flicker when starting folding

The dimensions of LightRevealScrim were used before the view was being measured, causing the entire screen to become black for one frame sometimes (especially after boot.

Now, LightRevealScrim accepts the initial dimensions what are used before the view is measured.

Bug: 261560634
Test: LightRevealScrimTest && many unfolds soon after rebooting
Change-Id: I91d1f2de82c563f085c9565c6e7116abb1e43e1d
This commit is contained in:
Nicolo' Mazzucato
2022-12-20 15:37:13 +00:00
parent 2ea532629c
commit d976bb4a23
3 changed files with 77 additions and 21 deletions

View File

@@ -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<Boolean>
@@ -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

View File

@@ -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) {

View File

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