Loading packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +195 −154 Original line number Diff line number Diff line Loading @@ -1307,8 +1307,7 @@ private inline fun <T> computeValue( val currentContent = currentContentState.contents.last() // The element is shared: interpolate between the value in fromContent and the value in // toContent. // The element is shared: interpolate between the value in fromContent and toContent. // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared // elements follow the finger direction. val isSharedElement = fromState != null && toState != null Loading Loading @@ -1343,28 +1342,133 @@ private inline fun <T> computeValue( // The content for which we compute the transformation. Note that this is not necessarily // [currentContent] because [currentContent] could be a different content than the transition // fromContent or toContent during interruptions or when a ancestor transition is running. val content: ContentKey val transformationContentKey: ContentKey = getTransformationContentKey( isDisabledSharedElement = isSharedElement, currentContent = currentContent, layoutImpl = layoutImpl, transition = transition, element = element, currentSceneState = currentSceneState, ) // Get the transformed value, i.e. the target value at the beginning (for entering elements) or // end (for leaving elements) of the transition. val contentState: Element.State val targetState: Element.State = element.stateByContent.getValue(transformationContentKey) val idleValue = contentValue(targetState) val transformationWithRange = transformation( transition.transformationSpec.transformations(element.key, transformationContentKey) ) val isElementEntering = when { isSharedElement -> { content = currentContent contentState = currentContentState transformationContentKey == toContent -> true transformationContentKey == fromContent -> false isAncestorTransition(layoutImpl, transition) -> isEnteringAncestorTransition(layoutImpl, transition) transformationContentKey == transition.currentScene -> toState == null else -> transformationContentKey == toContent } val previewTransformation = transition.previewTransformationSpec?.let { transformation(it.transformations(element.key, transformationContentKey)) } if (previewTransformation != null) { return computePreviewTransformationValue( transition, idleValue, transformationContentKey, isElementEntering, previewTransformation, element, layoutImpl, transformationWithRange, lerp, ) } if (transformationWithRange == null) { // If there is no transformation explicitly associated to this element value, let's use // the value given by the system (like the current position and size given by the layout // pass). return currentValue() } val transformation = transformationWithRange.transformation when (transformation) { is CustomPropertyTransformation -> return with(transformation) { layoutImpl.propertyTransformationScope.transform( transformationContentKey, element.key, transition, transition.coroutineScope, ) } is InterpolatedPropertyTransformation -> { /* continue */ } } val targetValue = with(transformation) { layoutImpl.propertyTransformationScope.transform( transformationContentKey, 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. if (targetValue == idleValue) { return targetValue } val progress = transition.progress // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range. val rangeProgress = transformationWithRange.range?.progress(progress) ?: progress return if (isElementEntering) { lerp(targetValue, idleValue, rangeProgress) } else { lerp(idleValue, targetValue, rangeProgress) } } private fun getTransformationContentKey( isDisabledSharedElement: Boolean, currentContent: ContentKey, layoutImpl: SceneTransitionLayoutImpl, transition: TransitionState.Transition, element: Element, currentSceneState: Element.State?, ): ContentKey { return when { isDisabledSharedElement -> { currentContent } isAncestorTransition(layoutImpl, transition) -> { if ( fromState != null && transition.transformationSpec.hasTransformation(element.key, fromContent) element.stateByContent[transition.fromContent] != null && transition.transformationSpec.hasTransformation( element.key, transition.fromContent, ) ) { content = fromContent contentState = fromState transition.fromContent } else if ( toState != null && transition.transformationSpec.hasTransformation(element.key, toContent) element.stateByContent[transition.toContent] != null && transition.transformationSpec.hasTransformation( element.key, transition.toContent, ) ) { content = toContent contentState = toState transition.toContent } else { throw IllegalStateException( "Ancestor transition is active but no transformation " + Loading @@ -1374,31 +1478,30 @@ private inline fun <T> computeValue( } } currentSceneState != null && currentContent == transition.currentScene -> { content = currentContent contentState = currentSceneState currentContent } fromState != null -> { content = fromContent contentState = fromState element.stateByContent[transition.fromContent] != null -> { transition.fromContent } else -> { content = toContent contentState = toState!! transition.toContent } } val transformationWithRange = transformation(transition.transformationSpec.transformations(element.key, content)) val previewTransformation = transition.previewTransformationSpec?.let { transformation(it.transformations(element.key, content)) } if (previewTransformation != null) { private inline fun <T> computePreviewTransformationValue( transition: TransitionState.Transition, idleValue: T, transformationContentKey: ContentKey, isEntering: Boolean, previewTransformation: TransformationWithRange<PropertyTransformation<T>>, element: Element, layoutImpl: SceneTransitionLayoutImpl, transformationWithRange: TransformationWithRange<PropertyTransformation<T>>?, lerp: (T, T, Float) -> T, ): T { val isInPreviewStage = transition.isInPreviewStage val idleValue = contentValue(contentState) val isEntering = content == toContent val previewTargetValue = with( previewTransformation.transformation.requireInterpolatedTransformation( Loading @@ -1409,7 +1512,7 @@ private inline fun <T> computeValue( } ) { layoutImpl.propertyTransformationScope.transform( content, transformationContentKey, element.key, transition, idleValue, Loading @@ -1427,7 +1530,7 @@ private inline fun <T> computeValue( } ) { layoutImpl.propertyTransformationScope.transform( content, transformationContentKey, element.key, transition, idleValue, Loading Loading @@ -1485,74 +1588,12 @@ private inline fun <T> computeValue( lerp( lerp(idleValue, previewTargetValue, previewRangeProgress), targetValueOrNull, transformationWithRange.range?.progress(transition.progress) ?: transition.progress, transformationWithRange.range?.progress(transition.progress) ?: transition.progress, ) } } } if (transformationWithRange == null) { // If there is no transformation explicitly associated to this element value, let's use // the value given by the system (like the current position and size given by the layout // pass). return currentValue() } val transformation = transformationWithRange.transformation when (transformation) { is CustomPropertyTransformation -> return with(transformation) { layoutImpl.propertyTransformationScope.transform( content, element.key, transition, transition.coroutineScope, ) } is InterpolatedPropertyTransformation -> { /* continue */ } } val idleValue = contentValue(contentState) val targetValue = 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. if (targetValue == idleValue) { return targetValue } val progress = transition.progress // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range. val rangeProgress = transformationWithRange.range?.progress(progress) ?: progress // Interpolate between the value at rest and the value before entering/after leaving. val isEntering = when { content == toContent -> true content == fromContent -> false isAncestorTransition(layoutImpl, transition) -> isEnteringAncestorTransition(layoutImpl, transition) content == transition.currentScene -> toState == null else -> content == toContent } return if (isEntering) { lerp(targetValue, idleValue, rangeProgress) } else { lerp(idleValue, targetValue, rangeProgress) } } private fun isAncestorTransition( layoutImpl: SceneTransitionLayoutImpl, transition: TransitionState.Transition, Loading @@ -1564,7 +1605,7 @@ private fun isAncestorTransition( private fun isEnteringAncestorTransition( layoutImpl: SceneTransitionLayoutImpl, transition: TransitionState.Transition transition: TransitionState.Transition, ): Boolean { return layoutImpl.ancestors.fastAny { it.inContent == transition.toContent } } Loading Loading
packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +195 −154 Original line number Diff line number Diff line Loading @@ -1307,8 +1307,7 @@ private inline fun <T> computeValue( val currentContent = currentContentState.contents.last() // The element is shared: interpolate between the value in fromContent and the value in // toContent. // The element is shared: interpolate between the value in fromContent and toContent. // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared // elements follow the finger direction. val isSharedElement = fromState != null && toState != null Loading Loading @@ -1343,28 +1342,133 @@ private inline fun <T> computeValue( // The content for which we compute the transformation. Note that this is not necessarily // [currentContent] because [currentContent] could be a different content than the transition // fromContent or toContent during interruptions or when a ancestor transition is running. val content: ContentKey val transformationContentKey: ContentKey = getTransformationContentKey( isDisabledSharedElement = isSharedElement, currentContent = currentContent, layoutImpl = layoutImpl, transition = transition, element = element, currentSceneState = currentSceneState, ) // Get the transformed value, i.e. the target value at the beginning (for entering elements) or // end (for leaving elements) of the transition. val contentState: Element.State val targetState: Element.State = element.stateByContent.getValue(transformationContentKey) val idleValue = contentValue(targetState) val transformationWithRange = transformation( transition.transformationSpec.transformations(element.key, transformationContentKey) ) val isElementEntering = when { isSharedElement -> { content = currentContent contentState = currentContentState transformationContentKey == toContent -> true transformationContentKey == fromContent -> false isAncestorTransition(layoutImpl, transition) -> isEnteringAncestorTransition(layoutImpl, transition) transformationContentKey == transition.currentScene -> toState == null else -> transformationContentKey == toContent } val previewTransformation = transition.previewTransformationSpec?.let { transformation(it.transformations(element.key, transformationContentKey)) } if (previewTransformation != null) { return computePreviewTransformationValue( transition, idleValue, transformationContentKey, isElementEntering, previewTransformation, element, layoutImpl, transformationWithRange, lerp, ) } if (transformationWithRange == null) { // If there is no transformation explicitly associated to this element value, let's use // the value given by the system (like the current position and size given by the layout // pass). return currentValue() } val transformation = transformationWithRange.transformation when (transformation) { is CustomPropertyTransformation -> return with(transformation) { layoutImpl.propertyTransformationScope.transform( transformationContentKey, element.key, transition, transition.coroutineScope, ) } is InterpolatedPropertyTransformation -> { /* continue */ } } val targetValue = with(transformation) { layoutImpl.propertyTransformationScope.transform( transformationContentKey, 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. if (targetValue == idleValue) { return targetValue } val progress = transition.progress // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range. val rangeProgress = transformationWithRange.range?.progress(progress) ?: progress return if (isElementEntering) { lerp(targetValue, idleValue, rangeProgress) } else { lerp(idleValue, targetValue, rangeProgress) } } private fun getTransformationContentKey( isDisabledSharedElement: Boolean, currentContent: ContentKey, layoutImpl: SceneTransitionLayoutImpl, transition: TransitionState.Transition, element: Element, currentSceneState: Element.State?, ): ContentKey { return when { isDisabledSharedElement -> { currentContent } isAncestorTransition(layoutImpl, transition) -> { if ( fromState != null && transition.transformationSpec.hasTransformation(element.key, fromContent) element.stateByContent[transition.fromContent] != null && transition.transformationSpec.hasTransformation( element.key, transition.fromContent, ) ) { content = fromContent contentState = fromState transition.fromContent } else if ( toState != null && transition.transformationSpec.hasTransformation(element.key, toContent) element.stateByContent[transition.toContent] != null && transition.transformationSpec.hasTransformation( element.key, transition.toContent, ) ) { content = toContent contentState = toState transition.toContent } else { throw IllegalStateException( "Ancestor transition is active but no transformation " + Loading @@ -1374,31 +1478,30 @@ private inline fun <T> computeValue( } } currentSceneState != null && currentContent == transition.currentScene -> { content = currentContent contentState = currentSceneState currentContent } fromState != null -> { content = fromContent contentState = fromState element.stateByContent[transition.fromContent] != null -> { transition.fromContent } else -> { content = toContent contentState = toState!! transition.toContent } } val transformationWithRange = transformation(transition.transformationSpec.transformations(element.key, content)) val previewTransformation = transition.previewTransformationSpec?.let { transformation(it.transformations(element.key, content)) } if (previewTransformation != null) { private inline fun <T> computePreviewTransformationValue( transition: TransitionState.Transition, idleValue: T, transformationContentKey: ContentKey, isEntering: Boolean, previewTransformation: TransformationWithRange<PropertyTransformation<T>>, element: Element, layoutImpl: SceneTransitionLayoutImpl, transformationWithRange: TransformationWithRange<PropertyTransformation<T>>?, lerp: (T, T, Float) -> T, ): T { val isInPreviewStage = transition.isInPreviewStage val idleValue = contentValue(contentState) val isEntering = content == toContent val previewTargetValue = with( previewTransformation.transformation.requireInterpolatedTransformation( Loading @@ -1409,7 +1512,7 @@ private inline fun <T> computeValue( } ) { layoutImpl.propertyTransformationScope.transform( content, transformationContentKey, element.key, transition, idleValue, Loading @@ -1427,7 +1530,7 @@ private inline fun <T> computeValue( } ) { layoutImpl.propertyTransformationScope.transform( content, transformationContentKey, element.key, transition, idleValue, Loading Loading @@ -1485,74 +1588,12 @@ private inline fun <T> computeValue( lerp( lerp(idleValue, previewTargetValue, previewRangeProgress), targetValueOrNull, transformationWithRange.range?.progress(transition.progress) ?: transition.progress, transformationWithRange.range?.progress(transition.progress) ?: transition.progress, ) } } } if (transformationWithRange == null) { // If there is no transformation explicitly associated to this element value, let's use // the value given by the system (like the current position and size given by the layout // pass). return currentValue() } val transformation = transformationWithRange.transformation when (transformation) { is CustomPropertyTransformation -> return with(transformation) { layoutImpl.propertyTransformationScope.transform( content, element.key, transition, transition.coroutineScope, ) } is InterpolatedPropertyTransformation -> { /* continue */ } } val idleValue = contentValue(contentState) val targetValue = 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. if (targetValue == idleValue) { return targetValue } val progress = transition.progress // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range. val rangeProgress = transformationWithRange.range?.progress(progress) ?: progress // Interpolate between the value at rest and the value before entering/after leaving. val isEntering = when { content == toContent -> true content == fromContent -> false isAncestorTransition(layoutImpl, transition) -> isEnteringAncestorTransition(layoutImpl, transition) content == transition.currentScene -> toState == null else -> content == toContent } return if (isEntering) { lerp(targetValue, idleValue, rangeProgress) } else { lerp(idleValue, targetValue, rangeProgress) } } private fun isAncestorTransition( layoutImpl: SceneTransitionLayoutImpl, transition: TransitionState.Transition, Loading @@ -1564,7 +1605,7 @@ private fun isAncestorTransition( private fun isEnteringAncestorTransition( layoutImpl: SceneTransitionLayoutImpl, transition: TransitionState.Transition transition: TransitionState.Transition, ): Boolean { return layoutImpl.ancestors.fastAny { it.inContent == transition.toContent } } Loading