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

Commit 3f319c02 authored by Brad Hinegardner's avatar Brad Hinegardner
Browse files

Port stepping animation to FlexClockView

Bug: 364673982
Test: manual
Flag: com.android.systemui.clock_reactive_variants
Change-Id: I2e2fbf28da29b7f073df036db53f43626efc1c40
parent 7bb5abb4
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -55,10 +55,7 @@ class FlexClockFaceController(
    override val view: View
        get() = layerController.view

    override val config =
        ClockFaceConfig(
            hasCustomPositionUpdatedAnimation = false // TODO(b/364673982)
        )
    override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)

    override var theme = ThemeConfig(true, assets.seedColor)

@@ -96,6 +93,19 @@ class FlexClockFaceController(
        layerController.view.layoutParams = lp
    }

    /** See documentation at [FlexClockView.offsetGlyphsForStepClockAnimation]. */
    private fun offsetGlyphsForStepClockAnimation(
        clockStartLeft: Int,
        direction: Int,
        fraction: Float
    ) {
        (view as? FlexClockView)?.offsetGlyphsForStepClockAnimation(
            clockStartLeft,
            direction,
            fraction,
        )
    }

    override val layout: ClockFaceLayout =
        DefaultClockFaceLayout(view).apply {
            views[0].id =
@@ -248,10 +258,12 @@ class FlexClockFaceController(

            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
                layerController.animations.onPositionUpdated(fromLeft, direction, fraction)
                if (isLargeClock) offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction)
            }

            override fun onPositionUpdated(distance: Float, fraction: Float) {
                layerController.animations.onPositionUpdated(distance, fraction)
                // TODO(b/378128811) port stepping animation
            }
        }
}
+112 −8
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.shared.clocks.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
import android.util.MathUtils.constrainedMap
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
@@ -50,6 +51,8 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
            )
    }

    private val digitOffsets = mutableMapOf<Int, Float>()

    override fun addView(child: View?) {
        super.addView(child)
        (child as SimpleDigitalClockTextView).digitTranslateAnimator =
@@ -76,7 +79,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
        digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitSize.x, 0)
        digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitSize.y)
        digitLeftTopMap[R.id.MINUTE_SECOND_DIGIT] = Point(maxSingleDigitSize)
        digitLeftTopMap.forEach { _, point ->
        digitLeftTopMap.forEach { (_, point) ->
            point.x += abs(aodTranslate.x)
            point.y += abs(aodTranslate.y)
        }
@@ -89,11 +92,17 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        digitalClockTextViewMap.forEach { (id, _) ->
            val textView = digitalClockTextViewMap[id]!!
            canvas.translate(digitLeftTopMap[id]!!.x.toFloat(), digitLeftTopMap[id]!!.y.toFloat())
        digitalClockTextViewMap.forEach { (id, textView) ->
            // save canvas location in anticipation of restoration later
            canvas.save()
            val xTranslateAmount =
                digitOffsets.getOrDefault(id, 0f) + digitLeftTopMap[id]!!.x.toFloat()
            // move canvas to location that the textView would like
            canvas.translate(xTranslateAmount, digitLeftTopMap[id]!!.y.toFloat())
            // draw the textView at the location of the canvas above
            textView.draw(canvas)
            canvas.translate(-digitLeftTopMap[id]!!.x.toFloat(), -digitLeftTopMap[id]!!.y.toFloat())
            // reset the canvas location back to 0 without drawing
            canvas.restore()
        }
    }

@@ -157,10 +166,108 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
        }
    }

    /**
     * Offsets the textViews of the clock for the step clock animation.
     *
     * The animation makes the textViews of the clock move at different speeds, when the clock is
     * moving horizontally.
     *
     * @param clockStartLeft the [getLeft] position of the clock, before it started moving.
     * @param clockMoveDirection the direction in which it is moving. A positive number means right,
     *   and negative means left.
     * @param moveFraction fraction of the clock movement. 0 means it is at the beginning, and 1
     *   means it finished moving.
     */
    fun offsetGlyphsForStepClockAnimation(
        clockStartLeft: Int,
        clockMoveDirection: Int,
        moveFraction: Float,
    ) {
        val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0
        // The sign of moveAmountDeltaForDigit is already set here
        // we can interpret (left - clockStartLeft) as (destinationPosition - originPosition)
        // so we no longer need to multiply direct sign to moveAmountDeltaForDigit
        val currentMoveAmount = left - clockStartLeft
        for (i in 0 until NUM_DIGITS) {
            val mapIndexToId =
                when (i) {
                    0 -> R.id.HOUR_FIRST_DIGIT
                    1 -> R.id.HOUR_SECOND_DIGIT
                    2 -> R.id.MINUTE_FIRST_DIGIT
                    3 -> R.id.MINUTE_SECOND_DIGIT
                    else -> -1
                }
            val digitFraction =
                getDigitFraction(
                    digit = i,
                    isMovingToCenter = isMovingToCenter,
                    fraction = moveFraction,
                )
            // left here is the final left position after the animation is done
            val moveAmountForDigit = currentMoveAmount * digitFraction
            var moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
            if (isMovingToCenter && moveAmountForDigit < 0) moveAmountDeltaForDigit *= -1
            digitOffsets[mapIndexToId] = moveAmountDeltaForDigit
            invalidate()
        }
    }

    private val moveToCenterDelays: List<Int>
        get() = if (isLayoutRtl) MOVE_LEFT_DELAYS else MOVE_RIGHT_DELAYS

    private val moveToSideDelays: List<Int>
        get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS

    private fun getDigitFraction(digit: Int, isMovingToCenter: Boolean, fraction: Float): Float {
        // The delay for the digit, in terms of fraction.
        // (i.e. the digit should not move during 0.0 - 0.1).
        val delays = if (isMovingToCenter) moveToCenterDelays else moveToSideDelays
        val digitInitialDelay = delays[digit] * MOVE_DIGIT_STEP
        return MOVE_INTERPOLATOR.getInterpolation(
            constrainedMap(
                /* rangeMin= */ 0.0f,
                /* rangeMax= */ 1.0f,
                /* valueMin= */ digitInitialDelay,
                /* valueMax= */ digitInitialDelay + AVAILABLE_ANIMATION_TIME,
                /* value= */ fraction,
            )
        )
    }

    companion object {
        val AOD_TRANSITION_DURATION = 750L
        val CHARGING_TRANSITION_DURATION = 300L

        // Calculate the positions of all of the digits...
        // Offset each digit by, say, 0.1
        // This means that each digit needs to move over a slice of "fractions", i.e. digit 0 should
        // move from 0.0 - 0.7, digit 1 from 0.1 - 0.8, digit 2 from 0.2 - 0.9, and digit 3
        // from 0.3 - 1.0.
        private const val NUM_DIGITS = 4

        // Delays. Each digit's animation should have a slight delay, so we get a nice
        // "stepping" effect. When moving right, the second digit of the hour should move first.
        // When moving left, the first digit of the hour should move first. The lists encode
        // the delay for each digit (hour[0], hour[1], minute[0], minute[1]), to be multiplied
        // by delayMultiplier.
        private val MOVE_LEFT_DELAYS = listOf(0, 1, 2, 3)
        private val MOVE_RIGHT_DELAYS = listOf(1, 0, 3, 2)

        // How much delay to apply to each subsequent digit. This is measured in terms of "fraction"
        // (i.e. a value of 0.1 would cause a digit to wait until fraction had hit 0.1, or 0.2 etc
        // before moving).
        //
        // The current specs dictate that each digit should have a 33ms gap between them. The
        // overall time is 1s right now.
        private const val MOVE_DIGIT_STEP = 0.033f

        // Constants for the animation
        private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED

        // Total available transition time for each digit, taking into account the step. If step is
        // 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
        private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1)

        // Use the sign of targetTranslation to control the direction of digit translation
        fun updateDirectionalTargetTranslate(id: Int, targetTranslation: Point): Point {
            val outPoint = Point(targetTranslation)
@@ -169,17 +276,14 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
                    outPoint.x *= -1
                    outPoint.y *= -1
                }

                R.id.HOUR_SECOND_DIGIT -> {
                    outPoint.x *= 1
                    outPoint.y *= -1
                }

                R.id.MINUTE_FIRST_DIGIT -> {
                    outPoint.x *= -1
                    outPoint.y *= 1
                }

                R.id.MINUTE_SECOND_DIGIT -> {
                    outPoint.x *= 1
                    outPoint.y *= 1