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

Commit f412c23b authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add `MotionSpec.Undefined` to delay initialization of MotionValue." into main

parents 27bc9726 b64e01d4
Loading
Loading
Loading
Loading
+86 −36
Original line number Diff line number Diff line
@@ -39,17 +39,27 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
    )

    // currentComputedValues input
    private var memoizedSpec: MotionSpec? = null
    private var memoizedSpec: MotionSpec = MotionSpec.InitiallyUndefined
    private var memoizedInput: Float = Float.MIN_VALUE
    private var memoizedAnimationTimeNanos: Long = Long.MIN_VALUE
    private var memoizedDirection: InputDirection = InputDirection.Min

    // currentComputedValues output
    private lateinit var memoizedComputedValues: ComputedValues
    private var memoizedComputedValues: ComputedValues =
        ComputedValues(
            MotionSpec.InitiallyUndefined.segmentAtInput(memoizedInput, memoizedDirection),
            GuaranteeState.Inactive,
            DiscontinuityAnimation.None,
        )

    internal val currentComputedValues: ComputedValues
        get() {
            val currentSpec: MotionSpec = spec
            if (currentSpec == MotionSpec.InitiallyUndefined) {
                requireNoMotionSpecSet()
                return memoizedComputedValues
            }

            val currentInput: Float = currentInput
            val currentAnimationTimeNanos: Long = currentAnimationTimeNanos
            val currentDirection: InputDirection = currentDirection
@@ -63,11 +73,21 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
                return memoizedComputedValues
            }

            val isInitialComputation = memoizedSpec == MotionSpec.InitiallyUndefined

            memoizedSpec = currentSpec
            memoizedInput = currentInput
            memoizedAnimationTimeNanos = currentAnimationTimeNanos
            memoizedDirection = currentDirection

            memoizedComputedValues =
                if (isInitialComputation) {
                    ComputedValues(
                        currentSpec.segmentAtInput(currentInput, currentDirection),
                        GuaranteeState.Inactive,
                        DiscontinuityAnimation.None,
                    )
                } else {
                    val segment: SegmentData =
                        computeSegmentData(
                            spec = currentSpec,
@@ -99,9 +119,9 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
                            animationTimeNanos = currentAnimationTimeNanos,
                        )

            return ComputedValues(segment, guarantee, animation).also {
                memoizedComputedValues = it
                    ComputedValues(segment, guarantee, animation)
                }
            return memoizedComputedValues
        }

    // currentSpringState input
@@ -613,4 +633,34 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
            }
        }
    }

    /**
     * Precondition to ensure that this [Computations] has not yet been initialized with a
     * MotionSpec other than [MotionSpec.InitiallyUndefined].
     *
     * This precondition is added since the desired behavior of the MotionValue when toggling back
     * to a [MotionSpec.InitiallyUndefined] spec is unclear. If there is a compelling usecase, this
     * restriction could be lifted.
     */
    private fun requireNoMotionSpecSet() {
        // A MotionValue's spec can be MotionValue.Undefined initially. However, once a real spec
        // has been set, it cannot be changed back to MotionValue.Undefined.

        require(memoizedSpec == MotionSpec.InitiallyUndefined) {
            // memoizedSpec is only ever Undefined initially, before a motionSpec was set.
            //  This is used as a signal to detect if a user switches back to Undefined.
            "MotionSpec must not be changed back to undefined!\n" +
                " MotionValue: $label\n" +
                " last MotionSpec: $memoizedSpec"
        }

        // memoizedComputedValues must not have been reassigned either.
        require(
            with(memoizedComputedValues) {
                segment.spec == MotionSpec.InitiallyUndefined &&
                    guarantee == GuaranteeState.Inactive &&
                    animation == DiscontinuityAnimation.None
            }
        )
    }
}
+30 −0
Original line number Diff line number Diff line
@@ -150,6 +150,19 @@ data class MotionSpec(

        /* Empty motion spec, the output is the same as the input. */
        val Empty = MotionSpec(DirectionalMotionSpec.Empty)

        /**
         * Placeholder to indicate that a [MotionSpec] cannot be supplied yet.
         *
         * As long as this spec is set, the MotionValue output is NaN. When the MotionValue is first
         * supplied with an actual spec, the output value will be set immediately, without an
         * animation.
         *
         * This must only ever be supplied as a spec for new `MotionValue`s, which never were
         * supplied any other spec. Supplying this [InitiallyUndefined] spec to a MotionValue that
         * has already been supplied a spec will throw an exception.
         */
        val InitiallyUndefined = MotionSpec(DirectionalMotionSpec.InitiallyUndefined)
    }
}

@@ -247,5 +260,22 @@ data class DirectionalMotionSpec(
                listOf(Breakpoint.minLimit, Breakpoint.maxLimit),
                listOf(Mapping.Identity),
            )

        /** Internal marker for [MotionSpec.InitiallyUndefined]. */
        internal val InitiallyUndefined =
            DirectionalMotionSpec(
                listOf(Breakpoint.minLimit, Breakpoint.maxLimit),
                listOf(
                    object : Mapping {
                        override fun map(input: Float): Float {
                            return Float.NaN
                        }

                        override fun toString(): String {
                            return "InitiallyUndefined"
                        }
                    }
                ),
            )
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -222,9 +222,9 @@ private class ImperativeComputations(
            repeatMode = ValueAnimator.RESTART
            repeatCount = ValueAnimator.INFINITE
            start()
            pause()
            addUpdateListener {
                val isAnimationFinished = updateOutputValue(currentPlayTime)
                debugInspector?.isAnimating = !isAnimationFinished
                if (isAnimationFinished) {
                    pause()
                }
@@ -234,14 +234,12 @@ private class ImperativeComputations(
    fun ensureFrameRequested() {
        if (animationFrameDriver.isPaused) {
            animationFrameDriver.resume()
            debugInspector?.isAnimating = true
        }
    }

    fun pauseFrameRequests() {
        if (animationFrameDriver.isRunning) {
            animationFrameDriver.pause()
            debugInspector?.isAnimating = false
        }
    }

@@ -290,6 +288,8 @@ private class ImperativeComputations(
                )
        }

        if (currentValues.segment.spec == MotionSpec.InitiallyUndefined) return true

        listeners.fastForEach { it.onMotionValueUpdated(motionValue) }

        // Prepare last* state
+92 −0
Original line number Diff line number Diff line
{
  "frame_ids": [
    0,
    16,
    32,
    48,
    64
  ],
  "features": [
    {
      "name": "input",
      "type": "float",
      "data_points": [
        0,
        5,
        10,
        15,
        20
      ]
    },
    {
      "name": "gestureDirection",
      "type": "string",
      "data_points": [
        "Max",
        "Max",
        "Max",
        "Max",
        "Max"
      ]
    },
    {
      "name": "output",
      "type": "float",
      "data_points": [
        "NaN",
        "NaN",
        "NaN",
        15,
        20
      ]
    },
    {
      "name": "outputTarget",
      "type": "float",
      "data_points": [
        "NaN",
        "NaN",
        "NaN",
        15,
        20
      ]
    },
    {
      "name": "outputSpring",
      "type": "springParameters",
      "data_points": [
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        }
      ]
    },
    {
      "name": "isStable",
      "type": "boolean",
      "data_points": [
        true,
        true,
        true,
        true,
        true
      ]
    }
  ]
}
 No newline at end of file
+102 −0
Original line number Diff line number Diff line
{
  "frame_ids": [
    0,
    16,
    32,
    48,
    64,
    80
  ],
  "features": [
    {
      "name": "input",
      "type": "float",
      "data_points": [
        0,
        20,
        40,
        60,
        80,
        100
      ]
    },
    {
      "name": "gestureDirection",
      "type": "string",
      "data_points": [
        "Max",
        "Max",
        "Max",
        "Max",
        "Max",
        "Max"
      ]
    },
    {
      "name": "output",
      "type": "float",
      "data_points": [
        "NaN",
        "NaN",
        "NaN",
        "NaN",
        "NaN",
        "NaN"
      ]
    },
    {
      "name": "outputTarget",
      "type": "float",
      "data_points": [
        "NaN",
        "NaN",
        "NaN",
        "NaN",
        "NaN",
        "NaN"
      ]
    },
    {
      "name": "outputSpring",
      "type": "springParameters",
      "data_points": [
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        },
        {
          "stiffness": 100000,
          "dampingRatio": 1
        }
      ]
    },
    {
      "name": "isStable",
      "type": "boolean",
      "data_points": [
        true,
        true,
        true,
        true,
        true,
        true
      ]
    }
  ]
}
 No newline at end of file
Loading