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

Commit fbf29554 authored by Brad Hinegardner's avatar Brad Hinegardner Committed by Android (Google) Code Review
Browse files

Merge "Port stepping animation to FlexClockView" into main

parents dd5840f5 3f319c02
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