Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2d9a4ad authored by Yein Jo's avatar Yein Jo
Browse files

Extract GlowPie to avoid code repetition.

Bug: 335315940
Test: Manual, GlowPieEffectTest
Flag: NA. not used anywhere for now.
Change-Id: I08d0d430f96576c3c0195b4761a104899861dfeb
parent d243f966
Loading
Loading
Loading
Loading
+136 −34
Original line number Original line Diff line number Diff line
@@ -44,42 +44,31 @@ class GlowPieEffect(
    fun play() {
    fun play() {
        if (mainAnimator.isRunning) return
        if (mainAnimator.isRunning) return


        baseGlow.resetProgress()
        firstGlowPie.resetProgress()
        secondGlowPie.resetProgress()

        mainAnimator.addUpdateListener { updateListener ->
        mainAnimator.addUpdateListener { updateListener ->
            val time = updateListener.currentPlayTime.toFloat() % mainAnimator.duration
            val time = updateListener.currentPlayTime.toFloat() % mainAnimator.duration


            // TODO(b/335315940): Extract the timestamps to config.
            // Remap each glow pie progress.
            val progress1 = MathUtils.constrainedMap(0f, 1f, 250f, 2500f, time)
            baseGlow.updateProgress(time)
            val progress2 = MathUtils.constrainedMap(0f, 1f, 350f, 2600f, time)
            firstGlowPie.updateProgress(time)
            secondGlowPie.updateProgress(time)


            // TODO(b/335315940): Consider passing in 2D Matrix.
            // TODO(b/335315940): Consider passing in 2D Matrix.
            val angle0 = 0f // No rotation for the base.
            glowPieShader.setAngles(baseGlow.angle(), firstGlowPie.angle(), secondGlowPie.angle())
            // Negate the angle since we want clock-wise rotation.
            val angle1 =
                -(MathUtils.constrainedMap(-PI / 2f, 4f * PI, 0f, 1f, progress1) + progress1 * PI)
            val angle2 =
                -(MathUtils.constrainedMap(-PI / 2f, 3f * PI, 0f, 1f, progress2) + progress2 * PI)
            glowPieShader.setAngle(angle0, angle1, angle2)
            val bottomThreshold0 = 0f
            val topThreshold0 = 0f

            val bottomThreshold1 = MathUtils.lerp(1f, -FEATHER, progress1)
            val topThreshold1 = MathUtils.lerp(1f + FEATHER, 0f, progress1)

            val bottomThreshold2 = MathUtils.lerp(1f, -FEATHER, progress2)
            val topThreshold2 = MathUtils.lerp(1f + FEATHER, 0f, progress2)

            glowPieShader.setBottomAngleThresholds(
            glowPieShader.setBottomAngleThresholds(
                bottomThreshold0,
                baseGlow.bottomThreshold(),
                bottomThreshold1,
                firstGlowPie.bottomThreshold(),
                bottomThreshold2
                secondGlowPie.bottomThreshold()
            )
            )
            glowPieShader.setTopAngleThresholds(topThreshold0, topThreshold1, topThreshold2)
            glowPieShader.setTopAngleThresholds(

                baseGlow.topThreshold(),
            // Remap timestamps (in MS) to alpha [0, 1].
                firstGlowPie.topThreshold(),
            val alpha0 = MathUtils.constrainedMap(0f, 1f, 2250f, 2950f, time)
                secondGlowPie.topThreshold()
            val alpha1 = MathUtils.constrainedMap(1f, 0f, 2500f, 2750f, time)
            )
            val alpha2 = MathUtils.constrainedMap(1f, 0f, 2600f, 2850f, time)
            glowPieShader.setAlphas(baseGlow.alpha(), firstGlowPie.alpha(), secondGlowPie.alpha())
            glowPieShader.setAlphas(alpha0, alpha1, alpha2)


            // Finally trigger the draw callback.
            // Finally trigger the draw callback.
            renderEffectDrawCallback.onDraw(
            renderEffectDrawCallback.onDraw(
@@ -98,10 +87,123 @@ class GlowPieEffect(
        mainAnimator.cancel()
        mainAnimator.cancel()
    }
    }


    private companion object {
    companion object {
        private const val PI = Math.PI.toFloat()
        @VisibleForTesting const val PI = Math.PI.toFloat()
        private const val FEATHER = 0.3f
        @VisibleForTesting const val FEATHER = 0.3f
        // This indicates a single loop of the animation.
        @VisibleForTesting const val DURATION_MS = 3000L
        private 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
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -199,7 +199,7 @@ class GlowPieShader : RuntimeShader(GLOW_PIE_SHADER_COMP) {
        setColorUniform("in_colors2", colors[2])
        setColorUniform("in_colors2", colors[2])
    }
    }


    fun setAngle(vararg angles: Float) {
    fun setAngles(vararg angles: Float) {
        if (angles.size != NUM_PIE) {
        if (angles.size != NUM_PIE) {
            Log.wtf(TAG, "The number of angles must be $NUM_PIE")
            Log.wtf(TAG, "The number of angles must be $NUM_PIE")
            return
            return
+35 −0
Original line number Original line Diff line number Diff line
@@ -91,4 +91,39 @@ class GlowPieEffectTest : SysUiStateTest() {


        assertThat(glowPieEffect.mainAnimator.isRunning).isFalse()
        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)
    }
}
}