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

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

Snap for 13495399 from 4fb8e634 to 25Q3-release

Change-Id: I8d24ff2fd3cdcd18ebccda1b1d3eb1d3cdea09fe
parents 86210ef4 4fb8e634
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -13,6 +13,12 @@
        {"exclude-annotation": "org.junit.Ignore"},
        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
      ]
    },
    {
      "name": "PlatformComposeSceneTransitionLayoutTests"
    },
    {
      "name": "PlatformComposeCoreTests"
    }
  ],
  "presubmit-large": [
+141 −34
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spec.builder.Effect
import com.android.mechanics.spec.builder.EffectApplyScope
import com.android.mechanics.spec.builder.EffectPlacemenType
import com.android.mechanics.spec.builder.EffectPlacement
import com.android.mechanics.spec.builder.MotionBuilderContext
import com.android.mechanics.spec.with
@@ -48,6 +49,7 @@ import com.android.mechanics.spring.SpringParameters
 */
class MagneticDetach(
    private val semanticState: SemanticKey<State> = Defaults.AttachDetachState,
    private val semanticAttachedValue: SemanticKey<Float?> = Defaults.AttachedValue,
    private val detachPosition: Dp = Defaults.DetachPosition,
    private val attachPosition: Dp = Defaults.AttachPosition,
    private val detachScale: Float = Defaults.AttachDetachScale,
@@ -76,40 +78,135 @@ class MagneticDetach(
        maxLimitKey: BreakpointKey,
        placement: EffectPlacement,
    ) {
        if (placement.type == EffectPlacemenType.Before) {
            createPlacedBeforeSpec(minLimit, minLimitKey, maxLimit, maxLimitKey)
        } else {
            assert(placement.type == EffectPlacemenType.After)
            createPlacedAfterSpec(minLimit, minLimitKey, maxLimit, maxLimitKey)
        }
    }

    object Defaults {
        val AttachDetachState = SemanticKey<State>()
        val AttachedValue = SemanticKey<Float?>()
        val AttachDetachScale = .3f
        val DetachPosition = 80.dp
        val AttachPosition = 40.dp
        val Spring = SpringParameters(stiffness = 800f, dampingRatio = 0.95f)
    }

    /* Effect is attached at minLimit, and detaches at maxLimit. */
    private fun EffectApplyScope.createPlacedAfterSpec(
        minLimit: Float,
        minLimitKey: BreakpointKey,
        maxLimit: Float,
        maxLimitKey: BreakpointKey,
    ) {
        val attachedValue = baseValue(minLimit)
        val detachedValue = baseValue(maxLimit)
        val reattachPos = minLimit + attachPosition.toPx()
        val startValue = baseValue(minLimit)
        val detachValue = baseValue(maxLimit)
        val reattachValue = baseValue(reattachPos)

        val scaledDetachValue = startValue + (detachValue - startValue) * detachScale
        val scaledReattachValue = startValue + (reattachValue - startValue) * attachScale
        val attachedSemantics =
            listOf(semanticState with State.Attached, semanticAttachedValue with attachedValue)
        val detachedSemantics =
            listOf(semanticState with State.Detached, semanticAttachedValue with null)

        val attachKey = BreakpointKey("attach")
        val scaledDetachValue = attachedValue + (detachedValue - attachedValue) * detachScale
        val scaledReattachValue = attachedValue + (reattachValue - attachedValue) * attachScale

        val attachKey = BreakpointKey("attach")
        forward(
            initialMapping = Mapping.Linear(minLimit, startValue, maxLimit, scaledDetachValue),
            semantics = listOf(semanticState with State.Attached),
            initialMapping = Mapping.Linear(minLimit, attachedValue, maxLimit, scaledDetachValue),
            semantics = attachedSemantics,
        ) {
            after(spring = detachSpring, semantics = listOf(semanticState with State.Detached))
            after(spring = detachSpring, semantics = detachedSemantics)
            before(semantics = listOf(semanticAttachedValue with null))
        }

        backward(
            initialMapping = Mapping.Linear(minLimit, startValue, reattachPos, scaledReattachValue),
            semantics = listOf(semanticState with State.Attached),
            initialMapping =
                Mapping.Linear(minLimit, attachedValue, reattachPos, scaledReattachValue),
            semantics = attachedSemantics,
        ) {
            mapping(
                breakpoint = reattachPos,
                key = attachKey,
                spring = attachSpring,
                semantics = listOf(semanticState with State.Detached),
                semantics = detachedSemantics,
                mapping = baseMapping,
            )
            before(semantics = listOf(semanticAttachedValue with null))
            after(semantics = listOf(semanticAttachedValue with null))
        }

        val beforeDetachSegment = SegmentKey(minLimitKey, maxLimitKey, InputDirection.Max)
        val beforeAttachSegment = SegmentKey(attachKey, maxLimitKey, InputDirection.Min)
        val afterAttachSegment = SegmentKey(minLimitKey, attachKey, InputDirection.Min)
        addSegmentHandlers(
            beforeDetachSegment = SegmentKey(minLimitKey, maxLimitKey, InputDirection.Max),
            beforeAttachSegment = SegmentKey(attachKey, maxLimitKey, InputDirection.Min),
            afterAttachSegment = SegmentKey(minLimitKey, attachKey, InputDirection.Min),
            minLimit = minLimit,
            maxLimit = maxLimit,
        )
    }

    /* Effect is attached at maxLimit, and detaches at minLimit. */
    private fun EffectApplyScope.createPlacedBeforeSpec(
        minLimit: Float,
        minLimitKey: BreakpointKey,
        maxLimit: Float,
        maxLimitKey: BreakpointKey,
    ) {
        val attachedValue = baseValue(maxLimit)
        val detachedValue = baseValue(minLimit)
        val reattachPos = maxLimit - attachPosition.toPx()
        val reattachValue = baseValue(reattachPos)

        val attachedSemantics =
            listOf(semanticState with State.Attached, semanticAttachedValue with attachedValue)
        val detachedSemantics =
            listOf(semanticState with State.Detached, semanticAttachedValue with null)

        val scaledDetachValue = attachedValue + (detachedValue - attachedValue) * detachScale
        val scaledReattachValue = attachedValue + (reattachValue - attachedValue) * attachScale

        val attachKey = BreakpointKey("attach")

        backward(
            initialMapping = Mapping.Linear(minLimit, scaledDetachValue, maxLimit, attachedValue),
            semantics = attachedSemantics,
        ) {
            before(spring = detachSpring, semantics = detachedSemantics)
            after(semantics = listOf(semanticAttachedValue with null))
        }

        forward(initialMapping = baseMapping, semantics = detachedSemantics) {
            target(
                breakpoint = reattachPos,
                key = attachKey,
                from = scaledReattachValue,
                to = attachedValue,
                spring = attachSpring,
                semantics = attachedSemantics,
            )
            after(semantics = listOf(semanticAttachedValue with null))
        }

        addSegmentHandlers(
            beforeDetachSegment = SegmentKey(minLimitKey, maxLimitKey, InputDirection.Min),
            beforeAttachSegment = SegmentKey(minLimitKey, attachKey, InputDirection.Max),
            afterAttachSegment = SegmentKey(attachKey, maxLimitKey, InputDirection.Max),
            minLimit = minLimit,
            maxLimit = maxLimit,
        )
    }

    private fun EffectApplyScope.addSegmentHandlers(
        beforeDetachSegment: SegmentKey,
        beforeAttachSegment: SegmentKey,
        afterAttachSegment: SegmentKey,
        minLimit: Float,
        maxLimit: Float,
    ) {
        // Suppress direction change during detach. This prevents snapping to the origin when
        // changing the direction while detaching.
        addSegmentHandler(beforeDetachSegment, PreventDirectionChangeWithinCurrentSegment)
@@ -122,31 +219,41 @@ class MagneticDetach(
        addSegmentHandler(afterAttachSegment) { currentSegment, newInput, newDirection ->
            val nextSegment = segmentAtInput(newInput, newDirection)
            if (nextSegment.key == beforeDetachSegment) {

                val pivotPos = newInput
                val pivotValue = currentSegment.mapping.map(pivotPos)

                val tweakedMapping = Mapping { input ->
                    if (input <= pivotPos) {
                        val t = (input - minLimit) / (pivotPos - minLimit)
                        lerp(startValue, pivotValue, t)
                    } else {
                        val t = (input - pivotPos) / (maxLimit - pivotPos)
                        lerp(pivotValue, scaledDetachValue, t)
                    }
                }
                nextSegment.copy(mapping = tweakedMapping)
                nextSegment.copy(
                    mapping =
                        switchMappingWithSamePivotValue(
                            currentSegment.mapping,
                            nextSegment.mapping,
                            minLimit,
                            newInput,
                            maxLimit,
                        )
                )
            } else {
                nextSegment
            }
        }
    }

    object Defaults {
        val AttachDetachState = SemanticKey<State>()
        val AttachDetachScale = .3f
        val DetachPosition = 80.dp
        val AttachPosition = 40.dp
        val Spring = SpringParameters(stiffness = 800f, dampingRatio = 0.95f)
    private fun switchMappingWithSamePivotValue(
        source: Mapping,
        target: Mapping,
        minLimit: Float,
        pivot: Float,
        maxLimit: Float,
    ): Mapping {
        val minValue = target.map(minLimit)
        val pivotValue = source.map(pivot)
        val maxValue = target.map(maxLimit)

        return Mapping { input ->
            if (input <= pivot) {
                val t = (input - minLimit) / (pivot - minLimit)
                lerp(minValue, pivotValue, t)
            } else {
                val t = (input - pivot) / (maxLimit - pivot)
                lerp(pivotValue, maxValue, t)
            }
        }
    }
}
+12 −12
Original line number Diff line number Diff line
@@ -481,13 +481,6 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
                                guaranteeState.updatedSpringParameters(lastBreakpoint)
                        }

                        springState =
                            springState.calculateUpdatedState(
                                nextBreakpointCrossTime - lastAnimationTime,
                                springParameters,
                            )
                        lastAnimationTime = nextBreakpointCrossTime

                        val mappingBefore = mappings[segmentIndex]
                        val beforeBreakpoint = mappingBefore.map(nextBreakpoint.position)
                        val mappingAfter = mappings[segmentIndex + directionOffset]
@@ -505,7 +498,18 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
                                    "  after: $afterBreakpoint (mapping: $mappingAfter)",
                            )
                        }
                        hasJumped = hasJumped || delta != 0f

                        if (!hasJumped && delta != 0f) {
                            hasJumped = true
                            springState = springState.nudge(velocityDelta = directMappedVelocity)
                        }

                        springState =
                            springState.calculateUpdatedState(
                                nextBreakpointCrossTime - lastAnimationTime,
                                springParameters,
                            )
                        lastAnimationTime = nextBreakpointCrossTime

                        if (deltaIsFinite) {
                            springState = springState.nudge(displacementDelta = -delta)
@@ -530,10 +534,6 @@ internal abstract class Computations : CurrentFrameInput, LastFrameState, Static
                            }
                    }

                    if (hasJumped) {
                        springState = springState.nudge(velocityDelta = directMappedVelocity)
                    }

                    val tightened = guarantee.updatedSpringParameters(segment.entryBreakpoint)

                    DiscontinuityAnimation(springState, tightened, lastAnimationTime)
+1 −1
Original line number Diff line number Diff line
@@ -177,7 +177,7 @@ data class DirectionalMotionSpec(

        semantics.forEach {
            require(it.values.size == mappings.size) {
                "Semantics ${it.key} does not include correct number of segments"
                "Semantics ${it.key} contains ${it.values.size} values vs ${mappings.size} expected"
            }
        }
    }
+2 −0
Original line number Diff line number Diff line
@@ -69,4 +69,6 @@ class SegmentSemanticValues<T>(val key: SemanticKey<T>, val values: List<T>) {
    operator fun get(segmentIndex: Int): SemanticValue<T> {
        return SemanticValue(key, values[segmentIndex])
    }

    override fun toString() = "Semantics($key): [$values]"
}
Loading