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

Commit bc85ca1d authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 14104676 from 56468ec9 to 25Q4-release

Change-Id: Ice6bcc8f3ea3bd328d8aa749cc9337902669b101
parents 83545f01 56468ec9
Loading
Loading
Loading
Loading
+15 −39
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import androidx.compose.ui.layout.ApproachMeasureScope
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
@@ -39,43 +40,22 @@ import com.android.mechanics.debug.DebugMotionValueNode
import com.android.mechanics.effects.FixedValue
import com.android.mechanics.spec.Mapping
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.builder.MotionBuilderContext
import com.android.mechanics.spec.builder.ComposeMotionBuilderContext
import com.android.mechanics.spec.builder.effectsMotionSpec
import com.android.mechanics.spec.builder.fixedEffectsValueSpec
import com.android.mechanics.spec.builder.motionBuilderContext

/**
 * This component remains hidden until it reach its target height.
 *
 * TODO: Once b/413283893 is done, [motionBuilderContext] can be read internally via
 *   CompositionLocalConsumerModifierNode, instead of passing it.
 */
fun Modifier.verticalFadeContentReveal(
    motionBuilderContext: MotionBuilderContext,
    deltaY: Float = 0f,
    label: String? = null,
): Modifier =
    this then
        FadeContentRevealElement(
            motionBuilderContext = motionBuilderContext,
            deltaY = deltaY,
            label = label,
        )
/** This component remains hidden until it reach its target height. */
fun Modifier.verticalFadeContentReveal(deltaY: Float = 0f, label: String? = null): Modifier =
    this then FadeContentRevealElement(deltaY = deltaY, label = label)

private data class FadeContentRevealElement(
    val motionBuilderContext: MotionBuilderContext,
    val deltaY: Float,
    val label: String?,
) : ModifierNodeElement<FadeContentRevealNode>() {
private data class FadeContentRevealElement(val deltaY: Float, val label: String?) :
    ModifierNodeElement<FadeContentRevealNode>() {
    override fun create(): FadeContentRevealNode =
        FadeContentRevealNode(
            motionBuilderContext = motionBuilderContext,
            deltaY = deltaY,
            label = label,
        )
        FadeContentRevealNode(deltaY = deltaY, label = label)

    override fun update(node: FadeContentRevealNode) {
        check(node.deltaY == deltaY) { "Cannot update deltaY from ${node.deltaY} to $deltaY" }
        node.update(motionBuilderContext = motionBuilderContext)
    }

    override fun InspectorInfo.inspectableProperties() {
@@ -85,11 +65,8 @@ private data class FadeContentRevealElement(
    }
}

private class FadeContentRevealNode(
    private var motionBuilderContext: MotionBuilderContext,
    val deltaY: Float,
    private val label: String?,
) : DelegatingNode(), ApproachLayoutModifierNode {
private class FadeContentRevealNode(val deltaY: Float, private val label: String?) :
    DelegatingNode(), ApproachLayoutModifierNode, CompositionLocalConsumerModifierNode {
    // These properties are calculated during the lookahead pass (`lookAheadMeasure`) to
    // orchestrate the reveal animation. They are guaranteed to be updated before `approachMeasure`
    // is called.
@@ -106,12 +83,11 @@ private class FadeContentRevealNode(
     */
    private lateinit var motionDriver: MotionDriver

    private lateinit var motionBuilderContext: ComposeMotionBuilderContext

    override fun onAttach() {
        motionDriver = findMotionDriver()
    }

    fun update(motionBuilderContext: MotionBuilderContext) {
        this.motionBuilderContext = motionBuilderContext
        motionBuilderContext = motionBuilderContext()
    }

    override fun onDetach() {
+9 −16
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import androidx.compose.ui.layout.ApproachMeasureScope
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
@@ -48,27 +49,23 @@ import com.android.mechanics.debug.DebugMotionValueNode
import com.android.mechanics.effects.RevealOnThreshold
import com.android.mechanics.spec.Mapping
import com.android.mechanics.spec.MotionSpec
import com.android.mechanics.spec.builder.MotionBuilderContext
import com.android.mechanics.spec.builder.ComposeMotionBuilderContext
import com.android.mechanics.spec.builder.fixedSpatialValueSpec
import com.android.mechanics.spec.builder.motionBuilderContext
import com.android.mechanics.spec.builder.spatialMotionSpec
import kotlin.math.roundToInt

/**
 * This component remains hidden until its target height meets a minimum threshold. At that point,
 * it reveals itself by animating its height from 0 to the current target height.
 *
 * TODO: Once b/413283893 is done, [motionBuilderContext] can be read internally via
 *   CompositionLocalConsumerModifierNode, instead of passing it.
 */
fun Modifier.verticalTactileSurfaceReveal(
    motionBuilderContext: MotionBuilderContext,
    deltaY: Float = 0f,
    revealOnThreshold: RevealOnThreshold = DefaultRevealOnThreshold,
    label: String? = null,
): Modifier =
    this then
        VerticalTactileSurfaceRevealElement(
            motionBuilderContext = motionBuilderContext,
            deltaY = deltaY,
            revealOnThreshold = revealOnThreshold,
            label = label,
@@ -77,14 +74,12 @@ fun Modifier.verticalTactileSurfaceReveal(
private val DefaultRevealOnThreshold = RevealOnThreshold()

private data class VerticalTactileSurfaceRevealElement(
    val motionBuilderContext: MotionBuilderContext,
    val deltaY: Float,
    val revealOnThreshold: RevealOnThreshold,
    val label: String?,
) : ModifierNodeElement<VerticalTactileSurfaceRevealNode>() {
    override fun create(): VerticalTactileSurfaceRevealNode =
        VerticalTactileSurfaceRevealNode(
            motionBuilderContext = motionBuilderContext,
            deltaY = deltaY,
            revealOnThreshold = revealOnThreshold,
            label = label,
@@ -92,10 +87,7 @@ private data class VerticalTactileSurfaceRevealElement(

    override fun update(node: VerticalTactileSurfaceRevealNode) {
        check(node.deltaY == deltaY) { "Cannot update deltaY from ${node.deltaY} to $deltaY" }
        node.update(
            motionBuilderContext = motionBuilderContext,
            revealOnThreshold = revealOnThreshold,
        )
        node.update(revealOnThreshold = revealOnThreshold)
    }

    override fun InspectorInfo.inspectableProperties() {
@@ -107,11 +99,10 @@ private data class VerticalTactileSurfaceRevealElement(
}

private class VerticalTactileSurfaceRevealNode(
    private var motionBuilderContext: MotionBuilderContext,
    val deltaY: Float,
    private var revealOnThreshold: RevealOnThreshold,
    private val label: String?,
) : DelegatingNode(), ApproachLayoutModifierNode {
) : DelegatingNode(), ApproachLayoutModifierNode, CompositionLocalConsumerModifierNode {
    // These properties are calculated during the lookahead pass (`lookAheadMeasure`) to
    // orchestrate the reveal animation. They are guaranteed to be updated before `approachMeasure`
    // is called.
@@ -128,12 +119,14 @@ private class VerticalTactileSurfaceRevealNode(
     */
    private lateinit var motionDriver: MotionDriver

    private lateinit var motionBuilderContext: ComposeMotionBuilderContext

    override fun onAttach() {
        motionDriver = findMotionDriver()
        motionBuilderContext = motionBuilderContext()
    }

    fun update(motionBuilderContext: MotionBuilderContext, revealOnThreshold: RevealOnThreshold) {
        this.motionBuilderContext = motionBuilderContext
    fun update(revealOnThreshold: RevealOnThreshold) {
        this.revealOnThreshold = revealOnThreshold
    }

+1 −5
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ import com.android.compose.animation.scene.transitions
import com.android.mechanics.debug.LocalMotionValueDebugController
import com.android.mechanics.debug.MotionValueDebugController
import com.android.mechanics.spec.builder.MotionBuilderContext
import com.android.mechanics.spec.builder.rememberMotionBuilderContext
import com.android.mechanics.testing.FakeMotionSpecBuilderContext
import org.junit.Rule
import org.junit.Test
@@ -122,10 +121,7 @@ class VerticalTactileSurfaceRevealModifierTest(val useOverlays: Boolean) :
                                            else -> Color.Blue
                                        },
                                    )
                                    .verticalTactileSurfaceReveal(
                                        motionBuilderContext = rememberMotionBuilderContext(),
                                        label = "box$it",
                                    )
                                    .verticalTactileSurfaceReveal(label = "box$it")
                                    .size(50.dp)
                            )
                        }
+73 −18
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import androidx.compose.runtime.mutableStateSetOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.util.trace
import androidx.compose.ui.util.traceValue
import com.android.mechanics.MotionValue.Companion.StableThresholdSpatial
import com.android.mechanics.debug.DebugInspector
import com.android.mechanics.debug.FrameData
@@ -38,6 +40,8 @@ import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spring.SpringState
import java.util.concurrent.atomic.AtomicInteger
import kotlin.time.Duration
import kotlin.time.measureTime
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.first
@@ -77,6 +81,36 @@ class MotionValueCollection(
        }
    }

    /**
     * Conditionally wraps the execution of a [block] in a performance trace.
     *
     * The primary advantage of this helper is lazy evaluation. The trace message from
     * [onTraceStart] is not computed and no `try-finally` block is entered unless tracing is
     * [enabled]. This helps to avoid performance penalties in production builds where tracing is
     * often turned off.
     *
     * @param enabled A boolean flag to enable or disable tracing.
     * @param onTraceStart A lambda that returns the trace section name. Only invoked if [enabled]
     *   is true.
     * @param onTraceEnd A lambda that executes after the block has finished. Only invoked if
     *   [enabled] is true.
     * @param block The code block to be executed and traced.
     */
    private inline fun trace(
        enabled: Boolean,
        onTraceStart: () -> String,
        onTraceEnd: (Duration) -> Unit = {},
        block: () -> Unit,
    ) {
        if (enabled) {
            val duration = measureTime { trace(sectionName = onTraceStart(), block = block) }

            onTraceEnd(duration)
        } else {
            block()
        }
    }

    /**
     * Keeps the all created [ManagedMotionValue]'s animated output running.
     *
@@ -110,6 +144,22 @@ class MotionValueCollection(
                    withFrameNanos { frameTimeNanos ->
                        frameCount++

                        trace(
                            enabled = isTraceEnabled,
                            onTraceStart = {
                                val prefix = "MotionValueCollection($label)"
                                val unstable = managedComputations.count { !it.isStable }
                                val all = managedComputations.size
                                traceValue("$prefix:unstable", unstable.toLong())
                                traceValue("$prefix:all", all.toLong())

                                "$prefix withFrameNanos f:$frameCount ($unstable/$all)"
                            },
                            onTraceEnd = {
                                val prefix = "MotionValueCollection($label)"
                                traceValue("$prefix:duration", it.inWholeMicroseconds)
                            },
                        ) {
                            lastFrameTimeNanos = currentAnimationTimeNanos
                            lastInput = currentInput
                            lastDirection = currentDirection
@@ -133,6 +183,7 @@ class MotionValueCollection(
                                }
                            }
                        }
                    }

                    isAnimatingUninterrupted = scheduleNextFrame
                    if (scheduleNextFrame) {
@@ -212,6 +263,10 @@ class MotionValueCollection(
        managedComputations.remove(toDispose)
        toDispose.onDeactivate()
    }

    companion object {
        var isTraceEnabled: Boolean = false
    }
}

internal class ManagedMotionComputation(
+18 −2
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import com.android.mechanics.spring.SpringParameters
@@ -32,7 +34,8 @@ import com.android.mechanics.spring.SpringParameters
 *
 * See go/motion-system.
 *
 * @see rememberMotionBuilderContext for Compose
 * @see rememberMotionBuilderContext for Compose (in composition)
 * @see motionBuilderContext for Compose (in Modifier.Node)
 * @see standardViewMotionBuilderContext for Views
 * @see expressiveViewMotionBuilderContext for Views
 */
@@ -84,7 +87,20 @@ fun rememberMotionBuilderContext(): MotionBuilderContext {
    return remember(density, motionScheme) { ComposeMotionBuilderContext(motionScheme, density) }
}

class ComposeMotionBuilderContext(motionScheme: MotionScheme, density: Density) :
/**
 * [MotionBuilderContext] for building motion specs in a [androidx.compose.ui.Modifier.Node].
 *
 * This should be read when the node is attached.
 */
fun CompositionLocalConsumerModifierNode.motionBuilderContext(): ComposeMotionBuilderContext {
    return ComposeMotionBuilderContext(
        motionScheme = currentValueOf(MaterialTheme.LocalMotionScheme),
        density = currentValueOf(LocalDensity),
    )
}

class ComposeMotionBuilderContext
internal constructor(motionScheme: MotionScheme, density: Density) :
    MotionBuilderContext, Density by density {

    override val spatial =