summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yein Jo <yeinj@google.com> 2024-04-24 22:31:35 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-04-24 22:31:35 +0000
commit444433357fb193c802116a934310d89e9005f65c (patch)
tree8098c4a85349186129349fdd6aa05425b575f2c7
parentd0c84ab693d7e1e19824111f0d5575f891b1fc61 (diff)
parentc2d9a4ad6925be1e3dae7bf9cbcb299506ac284c (diff)
Merge changes I08d0d430,Ic0f9c588 into main
* changes: Extract GlowPie to avoid code repetition. Add GlowPieEffect (first pass)
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffect.kt209
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectConfig.kt36
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieShader.kt233
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectTest.kt129
4 files changed, 607 insertions, 0 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffect.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffect.kt
new file mode 100644
index 000000000000..c08afd3e1bda
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffect.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.gloweffect
+
+import android.animation.ValueAnimator
+import android.animation.ValueAnimator.INFINITE
+import android.graphics.RenderEffect
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.surfaceeffects.RenderEffectDrawCallback
+import com.android.systemui.surfaceeffects.utils.MathUtils
+
+/** Renders rotating pie with glow on top, masked with a rounded box. */
+class GlowPieEffect(
+ config: GlowPieEffectConfig,
+ private val renderEffectDrawCallback: RenderEffectDrawCallback
+) {
+
+ private val glowPieShader = GlowPieShader().apply { applyConfig(config) }
+
+ @VisibleForTesting
+ val mainAnimator: ValueAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ // We want to loop the full cycle.
+ duration = DURATION_MS
+ repeatMode = ValueAnimator.RESTART
+ repeatCount = INFINITE
+ }
+
+ /** Plays glow pie until [finish] is called. */
+ fun play() {
+ if (mainAnimator.isRunning) return
+
+ baseGlow.resetProgress()
+ firstGlowPie.resetProgress()
+ secondGlowPie.resetProgress()
+
+ mainAnimator.addUpdateListener { updateListener ->
+ val time = updateListener.currentPlayTime.toFloat() % mainAnimator.duration
+
+ // Remap each glow pie progress.
+ baseGlow.updateProgress(time)
+ firstGlowPie.updateProgress(time)
+ secondGlowPie.updateProgress(time)
+
+ // TODO(b/335315940): Consider passing in 2D Matrix.
+ glowPieShader.setAngles(baseGlow.angle(), firstGlowPie.angle(), secondGlowPie.angle())
+ glowPieShader.setBottomAngleThresholds(
+ baseGlow.bottomThreshold(),
+ firstGlowPie.bottomThreshold(),
+ secondGlowPie.bottomThreshold()
+ )
+ glowPieShader.setTopAngleThresholds(
+ baseGlow.topThreshold(),
+ firstGlowPie.topThreshold(),
+ secondGlowPie.topThreshold()
+ )
+ glowPieShader.setAlphas(baseGlow.alpha(), firstGlowPie.alpha(), secondGlowPie.alpha())
+
+ // Finally trigger the draw callback.
+ renderEffectDrawCallback.onDraw(
+ RenderEffect.createRuntimeShaderEffect(
+ glowPieShader,
+ GlowPieShader.BACKGROUND_UNIFORM
+ )
+ )
+ }
+
+ mainAnimator.start()
+ }
+
+ fun finish() {
+ // TODO(b/335315940) Add alpha fade.
+ mainAnimator.cancel()
+ }
+
+ companion object {
+ @VisibleForTesting const val PI = Math.PI.toFloat()
+ @VisibleForTesting const val FEATHER = 0.3f
+ @VisibleForTesting const val DURATION_MS = 3000L
+
+ private val baseGlow = BaseGlow()
+ private val firstGlowPie = FirstGlowPie()
+ private val secondGlowPie = SecondGlowPie()
+ }
+
+ /** Contains animation parameters for each layer of glow pie. */
+ interface GlowPie {
+ /**
+ * The start & end timestamps of the animation. Must be smaller than or equal to the full
+ * [DURATION_MS].
+ */
+ val startMs: Float
+ val endMs: Float
+ /**
+ * Start & end angles in radian. This determines how many cycles you want to rotate. e.g.
+ * startAngle = 0f endAngle = 4f * PI, will give you the 2 cycles.
+ */
+ val startAngle: Float
+ val endAngle: Float
+ /**
+ * Start & end timestamps of the fade out duration. You may want to override [alpha] if you
+ * want to make it fade in. See [BaseGlow].
+ */
+ val alphaFadeStartMs: Float
+ val alphaFadeEndMs: Float
+
+ /** Below two values are expected to be updated through [updateProgress]. */
+ /** Normalized progress. */
+ var progress: Float
+ /** current time of the animation in ms. */
+ var time: Float
+
+ // Must be called before retrieving angle, bottom & top thresholds, and alpha.
+ // Otherwise the values would be stale.
+ fun updateProgress(time: Float) {
+ progress = MathUtils.constrainedMap(0f, 1f, startMs, endMs, time)
+ this.time = time
+ }
+
+ fun resetProgress() {
+ progress = 0f
+ time = 0f
+ }
+
+ fun angle(): Float {
+ // Negate the angle since we want clock-wise rotation.
+ val angle =
+ MathUtils.constrainedMap(startAngle, endAngle, 0f, 1f, progress) + progress * PI
+ return -angle
+ }
+
+ fun bottomThreshold(): Float {
+ return MathUtils.lerp(1f, -FEATHER, progress)
+ }
+
+ fun topThreshold(): Float {
+ return MathUtils.lerp(1f + FEATHER, 0f, progress)
+ }
+
+ // By default, it fades "out".
+ fun alpha(): Float {
+ // Remap timestamps (in MS) to alpha [0, 1].
+ return MathUtils.constrainedMap(1f, 0f, alphaFadeStartMs, alphaFadeEndMs, time)
+ }
+ }
+
+ data class BaseGlow(
+ override val startMs: Float = 0f,
+ override val endMs: Float = 0f,
+ override val startAngle: Float = 0f,
+ override val endAngle: Float = 0f,
+ override val alphaFadeStartMs: Float = 2250f,
+ override val alphaFadeEndMs: Float = 2950f,
+ ) : GlowPie {
+
+ override var progress: Float = 1f
+ override var time: Float = 0f
+ override fun updateProgress(time: Float) {}
+
+ override fun resetProgress() {}
+
+ override fun angle(): Float = 0f
+
+ override fun bottomThreshold(): Float = 0f
+
+ override fun topThreshold(): Float = 0f
+
+ // Base glow fade "in" (i.e. reveals).
+ override fun alpha(): Float {
+ return MathUtils.constrainedMap(0f, 1f, alphaFadeStartMs, alphaFadeEndMs, time)
+ }
+ }
+
+ data class FirstGlowPie(
+ override val startMs: Float = 250f,
+ override val endMs: Float = 2500f,
+ override val startAngle: Float = -PI / 2f,
+ override val endAngle: Float = 4f * PI,
+ override val alphaFadeStartMs: Float = 2500f,
+ override val alphaFadeEndMs: Float = 2750f,
+ override var progress: Float = 0f,
+ override var time: Float = 0f
+ ) : GlowPie
+
+ data class SecondGlowPie(
+ override val startMs: Float = 350f,
+ override val endMs: Float = 2600f,
+ override val startAngle: Float = -PI / 2f,
+ override val endAngle: Float = 3f * PI,
+ override val alphaFadeStartMs: Float = 2600f,
+ override val alphaFadeEndMs: Float = 2850f,
+ override var progress: Float = 0f,
+ override var time: Float = 0f
+ ) : GlowPie
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectConfig.kt
new file mode 100644
index 000000000000..6c728c1b5a53
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectConfig.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.gloweffect
+
+/** Parameter values needed to draw [GlowPieEffect]. */
+data class GlowPieEffectConfig(
+ /** Center x position of the effect. */
+ val centerX: Float,
+ /** Center y position of the effect. */
+ val centerY: Float,
+ /** Width of the rounded box mask. */
+ val width: Float,
+ /** Height of the rounded box mask. */
+ val height: Float,
+ /** Corner radius of the rounded box mask. */
+ val cornerRadius: Float,
+ /**
+ * Colors of the effect. The number must match 3, which is defined in [GlowPieShader.NUM_PIE].
+ * Each color corresponds to baseColor (bottom), firstLayerColor, and secondLayerColor (top).
+ */
+ val colors: IntArray
+)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieShader.kt
new file mode 100644
index 000000000000..2dbc0b5e24aa
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieShader.kt
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.gloweffect
+
+import android.graphics.RuntimeShader
+import android.util.Log
+import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary
+import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
+
+/** Draws two glowing pies rotating around the center of a rounded box on a base. */
+class GlowPieShader : RuntimeShader(GLOW_PIE_SHADER_COMP) {
+ // language=AGSL
+ companion object {
+ const val BACKGROUND_UNIFORM = "in_dst"
+ const val NUM_PIE = 3
+
+ private const val UNIFORMS =
+ """
+ uniform shader ${BACKGROUND_UNIFORM};
+ uniform vec2 in_center;
+ uniform vec2 in_size;
+ uniform half in_cornerRad;
+ uniform float[${NUM_PIE}] in_angles;
+ uniform float[${NUM_PIE}] in_alphas;
+ uniform float[${NUM_PIE}] in_bottomThresholds;
+ uniform float[${NUM_PIE}] in_topThresholds;
+ layout(color) uniform vec4 in_colors0;
+ layout(color) uniform vec4 in_colors1;
+ layout(color) uniform vec4 in_colors2;
+ """
+
+ private const val GLOW_PIE_MAIN =
+ """
+ vec4 main(vec2 p) {
+ vec4 pie = vec4(0.);
+ vec4 glow = vec4(0.);
+
+ vec2 c = p - in_center;
+ half box = sdRoundedBox(c, in_size, in_cornerRad);
+
+ // Base glow (drawn at the bottom)
+ pieGlow(
+ box,
+ c,
+ in_angles[0],
+ in_colors0.rgb,
+ /* pieAlpha= */ 1., // We always show the base color.
+ /* glowAlpha= */ in_alphas[0],
+ vec2(in_bottomThresholds[0], in_topThresholds[0]),
+ pie,
+ glow
+ );
+
+ // First pie
+ pieGlow(
+ box,
+ c,
+ in_angles[1],
+ in_colors1.rgb,
+ /* pieAlpha= */ in_alphas[1],
+ /* glowAlpha= */ in_alphas[1],
+ vec2(in_bottomThresholds[1], in_topThresholds[1]),
+ pie,
+ glow
+ );
+
+ // Second pie (drawn on top)
+ pieGlow(
+ box,
+ c,
+ in_angles[2],
+ in_colors2.rgb,
+ /* pieAlpha= */ in_alphas[2],
+ /* glowAlpha= */ in_alphas[2],
+ vec2(in_bottomThresholds[2], in_topThresholds[2]),
+ pie,
+ glow
+ );
+
+ return vec4(pie.rgb + glow.rgb * 0.3, pie.a);
+ }
+ """
+
+ private const val REMAP =
+ """
+ float remap(float in_start, float in_end, float out_start, float out_end, float x) {
+ x = (x - in_start) / (in_end - in_start);
+ x = clamp(x, 0., 1.);
+ return x * (out_end - out_start) + out_start;
+ }
+ """
+
+ /**
+ * This function draws a pie slice, an a glow on top. The glow also has the same pie shape
+ * but with more blur and additive blending.
+ */
+ private const val GLOW_PIE =
+ """
+ void pieGlow(
+ half box,
+ vec2 c,
+ half angle,
+ vec3 color,
+ half pieAlpha,
+ half glowAlpha,
+ vec2 angleThresholds,
+ inout vec4 inout_pie,
+ inout vec4 inout_glow) {
+
+ // Apply angular rotation.
+ half co = cos(angle), si = sin(angle);
+ mat2 rotM = mat2(co, -si, si, co); // 2D rotation matrix
+ c *= rotM;
+
+ // We rotate based on the cosine value, since we want to avoid using inverse
+ // trig function, which in this case is atan.
+
+ // Dot product with vec2(1., 0.) and bring the range to [0,1].
+ // Same as dot(normalize(c), vec2(1.,0) * 0.5 + 0.5
+ half d = normalize(c).x * 0.5 + 0.5;
+
+ // Those thresholds represents each end of the pie.
+ float bottomThreshold = angleThresholds[0];
+ float topThreshold = angleThresholds[1];
+ float angleMask = remap(bottomThreshold, topThreshold, 0., 1., d);
+
+ half boxMask = 1. - smoothstep(-0.02, 0.02, box);
+ vec4 pie = vec4(color, 1.0) * angleMask * boxMask * pieAlpha;
+
+ // We are drawing the same pie but with more blur.
+ half glowMask = 1. - smoothstep(0., 0.6, box);
+ // Glow outside only.
+ glowMask = min(glowMask, smoothstep(-0.02, 0.02, box));
+ // Apply some curve for the glow. (Can take out)
+ glowMask *= glowMask * glowMask;
+ // Glow mask should also be sliced with the angle mask.
+ glowMask *= angleMask;
+ vec4 glow = vec4(color, 1.0) * glowMask * glowAlpha;
+
+ inout_pie = pie + inout_pie * (1. - pie.a);
+ // Additive blending.
+ inout_glow += glow;
+ }
+ """
+
+ private const val GLOW_PIE_SHADER_COMP =
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ROUNDED_BOX_SDF +
+ UNIFORMS +
+ REMAP +
+ GLOW_PIE +
+ GLOW_PIE_MAIN
+
+ private val TAG = GlowPieShader::class.java.simpleName
+ }
+
+ fun applyConfig(config: GlowPieEffectConfig) {
+ setCenter(config.centerX, config.centerY)
+ setSize(config.width, config.height)
+ setCornerRadius(config.cornerRadius)
+ setColor(config.colors)
+ }
+
+ fun setCenter(centerX: Float, centerY: Float) {
+ setFloatUniform("in_center", centerX, centerY)
+ }
+
+ fun setSize(width: Float, height: Float) {
+ setFloatUniform("in_size", width, height)
+ }
+
+ fun setCornerRadius(cornerRadius: Float) {
+ setFloatUniform("in_cornerRad", cornerRadius)
+ }
+
+ /** Ignores alpha value, as fade in/out is handled within shader. */
+ fun setColor(colors: IntArray) {
+ if (colors.size != NUM_PIE) {
+ Log.wtf(TAG, "The number of colors must be $NUM_PIE")
+ return
+ }
+ setColorUniform("in_colors0", colors[0])
+ setColorUniform("in_colors1", colors[1])
+ setColorUniform("in_colors2", colors[2])
+ }
+
+ fun setAngles(vararg angles: Float) {
+ if (angles.size != NUM_PIE) {
+ Log.wtf(TAG, "The number of angles must be $NUM_PIE")
+ return
+ }
+ setFloatUniform("in_angles", angles)
+ }
+
+ fun setAlphas(vararg alphas: Float) {
+ if (alphas.size != NUM_PIE) {
+ Log.wtf(TAG, "The number of angles must be $NUM_PIE")
+ return
+ }
+ setFloatUniform("in_alphas", alphas)
+ }
+
+ fun setBottomAngleThresholds(vararg bottomThresholds: Float) {
+ if (bottomThresholds.size != NUM_PIE) {
+ Log.wtf(TAG, "The number of bottomThresholds must be $NUM_PIE")
+ return
+ }
+ setFloatUniform("in_bottomThresholds", bottomThresholds)
+ }
+
+ fun setTopAngleThresholds(vararg topThresholds: Float) {
+ if (topThresholds.size != NUM_PIE) {
+ Log.wtf(TAG, "The number of topThresholds must be $NUM_PIE")
+ return
+ }
+ setFloatUniform("in_topThresholds", topThresholds)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectTest.kt
new file mode 100644
index 000000000000..8105cc58b58e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/gloweffect/GlowPieEffectTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.gloweffect
+
+import android.graphics.Color
+import android.graphics.RenderEffect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.animation.AnimatorTestRule
+import com.android.systemui.model.SysUiStateTest
+import com.android.systemui.surfaceeffects.RenderEffectDrawCallback
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class GlowPieEffectTest : SysUiStateTest() {
+
+ @get:Rule val animatorTestRule = AnimatorTestRule(this)
+
+ @Test
+ fun play_triggersDrawCallback() {
+ var effectFromCallback: RenderEffect? = null
+ val glowPieEffectConfig =
+ GlowPieEffectConfig(
+ centerX = 0f,
+ centerY = 0f,
+ width = 1f,
+ height = 1f,
+ cornerRadius = 0.5f,
+ colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
+ )
+ val drawCallback =
+ object : RenderEffectDrawCallback {
+ override fun onDraw(renderEffect: RenderEffect) {
+ effectFromCallback = renderEffect
+ }
+ }
+ val glowPieEffect = GlowPieEffect(glowPieEffectConfig, drawCallback)
+
+ assertThat(effectFromCallback).isNull()
+
+ glowPieEffect.play()
+
+ animatorTestRule.advanceTimeBy(100L)
+
+ assertThat(effectFromCallback).isNotNull()
+ }
+
+ @Test
+ fun finish_cancelsAnimator() {
+ val glowPieEffectConfig =
+ GlowPieEffectConfig(
+ centerX = 0f,
+ centerY = 0f,
+ width = 1f,
+ height = 1f,
+ cornerRadius = 0.5f,
+ colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
+ )
+ val drawCallback =
+ object : RenderEffectDrawCallback {
+ override fun onDraw(renderEffect: RenderEffect) {}
+ }
+ val glowPieEffect = GlowPieEffect(glowPieEffectConfig, drawCallback)
+
+ glowPieEffect.play()
+ animatorTestRule.advanceTimeBy(100L)
+
+ assertThat(glowPieEffect.mainAnimator.isRunning).isTrue()
+
+ glowPieEffect.finish()
+
+ assertThat(glowPieEffect.mainAnimator.isRunning).isFalse()
+ }
+
+ @Test
+ fun glowPie_progress_computesProgressCorrectly() {
+ val myGlowPieConfig =
+ object : GlowPieEffect.GlowPie {
+ override val startMs: Float = 0f
+ override val endMs: Float = GlowPieEffect.DURATION_MS.toFloat()
+ override val startAngle: Float = 0f
+ override val endAngle: Float = 6f * GlowPieEffect.PI
+ override val alphaFadeStartMs: Float = 0f
+ override val alphaFadeEndMs: Float = GlowPieEffect.DURATION_MS.toFloat()
+ override var progress: Float = 0f
+ override var time: Float = 0f
+ }
+
+ val playTime = GlowPieEffect.DURATION_MS.toFloat() * 0.5f
+ val tolerance = 1e-4f
+ myGlowPieConfig.updateProgress(playTime)
+
+ assertThat(myGlowPieConfig.time).isWithin(tolerance).of(playTime)
+ assertThat(myGlowPieConfig.progress).isWithin(tolerance).of(0.5f)
+ assertThat(myGlowPieConfig.angle()).isWithin(tolerance).of(-3.5f * GlowPieEffect.PI)
+ assertThat(myGlowPieConfig.bottomThreshold())
+ .isWithin(tolerance)
+ .of((1f - GlowPieEffect.FEATHER) * 0.5f)
+ assertThat(myGlowPieConfig.topThreshold())
+ .isWithin(tolerance)
+ .of((1f + GlowPieEffect.FEATHER) * 0.5f)
+ assertThat(myGlowPieConfig.alpha()).isWithin(tolerance).of(0.5f)
+
+ myGlowPieConfig.resetProgress()
+
+ assertThat(myGlowPieConfig.time).isEqualTo(0f)
+ assertThat(myGlowPieConfig.progress).isEqualTo(0f)
+ }
+}