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

Commit 9392f149 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Make PropertyTransformation public

This CL makes the PropertyTransformation interface public. This is the
first step to making it easier to apply custom transformations to
elements during transitions.

In follow-up CLs, I will:
 - add transformSize(), transformOffset() and transformAlpha() to the
   DSL.
 - change the semantics of this interface a bit so that the returned
   value is the value for the current frame instead of the value being
   interpolated with the target value and the transition progress.

Bug: 376438969
Test: atest PlatformComposeSceneTransitionLayoutTests
Flag: com.android.systemui.scene_container
Change-Id: Iedf20e47320686bf2886171b5154402b30aca4c9
parent 9bd93474
Loading
Loading
Loading
Loading
+35 −27
Original line number Diff line number Diff line
@@ -48,7 +48,6 @@ import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.fastForEachReversed
import androidx.compose.ui.util.lerp
import com.android.compose.animation.scene.Element.State
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.PropertyTransformation
@@ -163,7 +162,7 @@ private fun Modifier.maybeElevateInContent(
    transitionStates: List<TransitionState>,
): Modifier {
    fun isSharedElement(
        stateByContent: Map<ContentKey, State>,
        stateByContent: Map<ContentKey, Element.State>,
        transition: TransitionState.Transition,
    ): Boolean {
        fun inFromContent() = transition.fromContent in stateByContent
@@ -1281,14 +1280,14 @@ private inline fun <T> computeValue(
                checkNotNull(if (currentContent == toContent) toState else fromState)
            val idleValue = contentValue(overscrollState)
            val targetValue =
                propertySpec.transform(
                    layoutImpl,
                with(propertySpec) {
                    layoutImpl.propertyTransformationScope.transform(
                        currentContent,
                    element,
                    overscrollState,
                        element.key,
                        transition,
                        idleValue,
                    )
                }

            // Make sure we don't read progress if values are the same and we don't need to
            // interpolate, so we don't invalidate the phase where this is read.
@@ -1376,24 +1375,26 @@ private inline fun <T> computeValue(
        val idleValue = contentValue(contentState)
        val isEntering = content == toContent
        val previewTargetValue =
            previewTransformation.transform(
                layoutImpl,
            with(previewTransformation) {
                layoutImpl.propertyTransformationScope.transform(
                    content,
                element,
                contentState,
                    element.key,
                    transition,
                    idleValue,
                )
            }

        val targetValueOrNull =
            transformation?.transform(
                layoutImpl,
            transformation?.let { transformation ->
                with(transformation) {
                    layoutImpl.propertyTransformationScope.transform(
                        content,
                element,
                contentState,
                        element.key,
                        transition,
                        idleValue,
                    )
                }
            }

        // Make sure we don't read progress if values are the same and we don't need to interpolate,
        // so we don't invalidate the phase where this is read.
@@ -1460,7 +1461,14 @@ private inline fun <T> computeValue(

    val idleValue = contentValue(contentState)
    val targetValue =
        transformation.transform(layoutImpl, content, element, contentState, transition, idleValue)
        with(transformation) {
            layoutImpl.propertyTransformationScope.transform(
                content,
                element.key,
                transition,
                idleValue,
            )
        }

    // Make sure we don't read progress if values are the same and we don't need to interpolate, so
    // we don't invalidate the phase where this is read.
+1 −0
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ internal class SceneTransitionLayoutImpl(
    private val verticalDraggableHandler: DraggableHandlerImpl

    internal val elementStateScope = ElementStateScopeImpl(this)
    internal val propertyTransformationScope = PropertyTransformationScopeImpl(this)
    private var _userActionDistanceScope: UserActionDistanceScope? = null
    internal val userActionDistanceScope: UserActionDistanceScope
        get() =
+14 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.compose.animation.scene

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import com.android.compose.animation.scene.transformation.PropertyTransformationScope

internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
    ElementStateScope {
@@ -46,3 +48,15 @@ internal class UserActionDistanceScopeImpl(private val layoutImpl: SceneTransiti
    override val fontScale: Float
        get() = layoutImpl.density.fontScale
}

internal class PropertyTransformationScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
    PropertyTransformationScope, ElementStateScope by layoutImpl.elementStateScope {
    override val density: Float
        get() = layoutImpl.density.density

    override val fontScale: Float
        get() = layoutImpl.density.fontScale

    override val layoutDirection: LayoutDirection
        get() = layoutImpl.layoutDirection
}
+3 −9
Original line number Diff line number Diff line
@@ -18,10 +18,8 @@ package com.android.compose.animation.scene.transformation

import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState

/** Anchor the size of an element to the size of another element. */
@@ -31,19 +29,15 @@ internal class AnchoredSize(
    private val anchorWidth: Boolean,
    private val anchorHeight: Boolean,
) : PropertyTransformation<IntSize> {
    override fun transform(
        layoutImpl: SceneTransitionLayoutImpl,
    override fun PropertyTransformationScope.transform(
        content: ContentKey,
        element: Element,
        stateInContent: Element.State,
        element: ElementKey,
        transition: TransitionState.Transition,
        value: IntSize,
    ): IntSize {
        fun anchorSizeIn(content: ContentKey): IntSize {
            val size =
                layoutImpl.elements[anchor]?.stateByContent?.get(content)?.targetSize?.takeIf {
                    it != Element.SizeUnspecified
                }
                anchor.targetSize(content)
                    ?: throwMissingAnchorException(
                        transformation = "AnchoredSize",
                        anchor = anchor,
+4 −14
Original line number Diff line number Diff line
@@ -17,12 +17,9 @@
package com.android.compose.animation.scene.transformation

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState

/** Anchor the translation of an element to another element. */
@@ -30,11 +27,9 @@ internal class AnchoredTranslate(
    override val matcher: ElementMatcher,
    private val anchor: ElementKey,
) : PropertyTransformation<Offset> {
    override fun transform(
        layoutImpl: SceneTransitionLayoutImpl,
    override fun PropertyTransformationScope.transform(
        content: ContentKey,
        element: Element,
        stateInContent: Element.State,
        element: ElementKey,
        transition: TransitionState.Transition,
        value: Offset,
    ): Offset {
@@ -46,18 +41,13 @@ internal class AnchoredTranslate(
            )
        }

        val anchor = layoutImpl.elements[anchor] ?: throwException(content = null)
        fun anchorOffsetIn(content: ContentKey): Offset? {
            return anchor.stateByContent[content]?.targetOffset?.takeIf { it.isSpecified }
        }

        // [element] will move the same amount as [anchor] does.
        // TODO(b/290184746): Also support anchors that are not shared but translated because of
        // other transformations, like an edge translation.
        val anchorFromOffset =
            anchorOffsetIn(transition.fromContent) ?: throwException(transition.fromContent)
            anchor.targetOffset(transition.fromContent) ?: throwException(transition.fromContent)
        val anchorToOffset =
            anchorOffsetIn(transition.toContent) ?: throwException(transition.toContent)
            anchor.targetOffset(transition.toContent) ?: throwException(transition.toContent)
        val offset = anchorToOffset - anchorFromOffset

        return if (content == transition.toContent) {
Loading