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

Commit 118924ac authored by Mike Schneider's avatar Mike Schneider Committed by Android (Google) Code Review
Browse files

Merge changes I1be67e52,I4a9d18cb into main

* changes:
  Expose current segment key on motion value
  Render shape of non-linear functions in the debug output
parents 341d1b1d 9db74a26
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.mechanics.spec.InputDirection
import com.android.mechanics.spec.Mapping
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.SegmentData
import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spring.SpringState
import java.util.concurrent.atomic.AtomicInteger
@@ -150,6 +151,10 @@ class MotionValue(
        return impl.semanticState(key)
    }

    /** The current segment used to compute the output. */
    val segmentKey: SegmentKey
        get() = impl.currentSegment.key

    /**
     * Keeps the [MotionValue]'s animated output running.
     *
+25 −4
Original line number Diff line number Diff line
@@ -50,8 +50,10 @@ import com.android.mechanics.MotionValue
import com.android.mechanics.spec.DirectionalMotionSpec
import com.android.mechanics.spec.Guarantee
import com.android.mechanics.spec.InputDirection
import com.android.mechanics.spec.Mapping
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.SegmentKey
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.flow.first
@@ -359,17 +361,36 @@ private fun DrawScope.drawDirectionalSpec(
        val segmentEnd = endBreakpoint.position
        val toInput = segmentEnd.fastCoerceAtMost(inputRange.endInclusive)

        // TODO add support for functions that are not linear
        val strokeWidth = if (isActiveSegment) 2.dp.toPx() else Stroke.HairlineWidth
        val dotSize = if (isActiveSegment) 4.dp.toPx() else 2.dp.toPx()
        val fromY = mapPointInOutputToY(mapping.map(fromInput), outputRange)
        val toY = mapPointInOutputToY(mapping.map(toInput), outputRange)

        val start = Offset(mapPointInInputToX(fromInput, inputRange), fromY)
        val end = Offset(mapPointInInputToX(toInput, inputRange), toY)
        if (mapping is Mapping.Fixed || mapping is Mapping.Identity || mapping is Mapping.Linear) {
            drawLine(color, start, end, strokeWidth = strokeWidth)
        } else {
            val xStart = mapPointInInputToX(fromInput, inputRange)
            val xEnd = mapPointInInputToX(toInput, inputRange)

        val strokeWidth = if (isActiveSegment) 2.dp.toPx() else Stroke.HairlineWidth
        val dotSize = if (isActiveSegment) 4.dp.toPx() else 2.dp.toPx()
            val oneDpInPx = 1.dp.toPx()
            val numberOfLines = ceil((xEnd - xStart) / oneDpInPx).toInt()
            val inputLength = (toInput - fromInput) / numberOfLines

        drawLine(color, start, end, strokeWidth = strokeWidth)
            repeat(numberOfLines) {
                val lineStart = fromInput + inputLength * it
                val lineEnd = lineStart + inputLength

                val partialFromY = mapPointInOutputToY(mapping.map(lineStart), outputRange)
                val partialToY = mapPointInOutputToY(mapping.map(lineEnd), outputRange)

                val partialStart = Offset(mapPointInInputToX(lineStart, inputRange), partialFromY)
                val partialEnd = Offset(mapPointInInputToX(lineEnd, inputRange), partialToY)

                drawLine(color, partialStart, partialEnd, strokeWidth = strokeWidth)
            }
        }

        if (segmentStart == fromInput) {
            drawCircle(color, dotSize, start)
+7 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.mechanics.impl.GuaranteeState
import com.android.mechanics.spec.InputDirection
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.SegmentData
import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spring.SpringState
import java.util.concurrent.atomic.AtomicInteger
@@ -77,6 +78,9 @@ class ViewMotionValue(
     */
    val outputTarget: Float by impl::outputTarget

    /** Whether an animation is currently running. */
    val isStable: Boolean by impl::isStable

    /**
     * The current value for the [SemanticKey].
     *
@@ -86,8 +90,9 @@ class ViewMotionValue(
        return impl.semanticState(key)
    }

    /** Whether an animation is currently running. */
    val isStable: Boolean by impl::isStable
    /** The current segment used to compute the output. */
    val segmentKey: SegmentKey
        get() = impl.currentSegment.key

    val label: String? by impl::label

+19 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.TestMonotonicFrameClock
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.mechanics.spec.Breakpoint
import com.android.mechanics.spec.BreakpointKey
import com.android.mechanics.spec.CanBeLastSegment
import com.android.mechanics.spec.DirectionalMotionSpec
@@ -36,6 +37,7 @@ import com.android.mechanics.spec.Guarantee
import com.android.mechanics.spec.InputDirection
import com.android.mechanics.spec.Mapping
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spec.SemanticValue
import com.android.mechanics.spec.buildDirectionalMotionSpec
@@ -416,6 +418,23 @@ class MotionValueTest {
        assertThat(underTest[s1]).isEqualTo("two")
    }

    @Test
    fun segment_returnsCurrentSegmentKey() {
        val spec =
            specBuilder(Mapping.Zero) {
                constantValue(1f, 1f, key = B1)
                constantValue(2f, 2f, key = B2)
            }

        val input = mutableFloatStateOf(1f)
        val underTest = MotionValue(input::value, FakeGestureContext, spec)

        assertThat(underTest.segmentKey).isEqualTo(SegmentKey(B1, B2, InputDirection.Max))
        input.floatValue = 2f
        assertThat(underTest.segmentKey)
            .isEqualTo(SegmentKey(B2, Breakpoint.maxLimit.key, InputDirection.Max))
    }

    @Test
    fun derivedValue_reflectsInputChangeInSameFrame() {
        motion.goldenTest(
+22 −0
Original line number Diff line number Diff line
@@ -27,10 +27,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.mechanics.MotionValueTest.Companion.B1
import com.android.mechanics.MotionValueTest.Companion.B2
import com.android.mechanics.MotionValueTest.Companion.specBuilder
import com.android.mechanics.spec.Breakpoint
import com.android.mechanics.spec.Guarantee
import com.android.mechanics.spec.InputDirection
import com.android.mechanics.spec.Mapping
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spec.with
import com.android.mechanics.testing.EmptyTestActivity
@@ -130,6 +132,26 @@ class ViewMotionValueTest {
        }
    }

    @Test
    fun segment_returnsCurrentSegmentKey() {
        activityRule.scenario.onActivity {
            val spec =
                specBuilder(Mapping.Zero) {
                    constantValue(1f, 1f, key = B1)
                    constantValue(2f, 2f, key = B2)
                }

            val gestureContext = DistanceGestureContext(0f, InputDirection.Max, 5f)
            val underTest = ViewMotionValue(1f, gestureContext, spec)

            assertThat(underTest.segmentKey).isEqualTo(SegmentKey(B1, B2, InputDirection.Max))
            underTest.input = 2f
            animatorTestRule.advanceTimeBy(16L)
            assertThat(underTest.segmentKey)
                .isEqualTo(SegmentKey(B2, Breakpoint.maxLimit.key, InputDirection.Max))
        }
    }

    @Test
    fun gestureContext_listensToGestureContextUpdates() =
        goldenTest(