From 67c8f9ae841c0846bdfce929549c1b19499157af Mon Sep 17 00:00:00 2001 From: XDdevv Date: Mon, 18 Aug 2025 20:19:15 +0500 Subject: [PATCH 1/4] Animate follow icon rotation in Jetcaster --- .../java/com/example/jetcaster/util/Buttons.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt index 3979a05aa..ccc896195 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt @@ -16,6 +16,8 @@ package com.example.jetcaster.util +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @@ -25,7 +27,9 @@ import androidx.compose.material3.IconToggleButtonColors import androidx.compose.material3.IconToggleButtonShapes import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -52,8 +56,16 @@ fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modi checkedShape = CircleShape, ), ) { + val iconRotation by animateFloatAsState( + targetValue = if(isFollowed) 0f else 360f, + animationSpec = spring( + dampingRatio = .6f, + stiffness = 200f + ) + ) + Icon( - // TODO: think about animating these icons + // Animated rotation when follow state changes painter = when { isFollowed -> painterResource(id = R.drawable.ic_check) else -> painterResource(id = R.drawable.ic_add) @@ -62,6 +74,7 @@ fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modi isFollowed -> stringResource(R.string.cd_following) else -> stringResource(R.string.cd_not_following) }, + modifier = Modifier.rotate(iconRotation) ) } } From 66bf1f4dd1e7afeb4511a1f2ccfb1fb6b95879cb Mon Sep 17 00:00:00 2001 From: XDdevv Date: Mon, 18 Aug 2025 20:38:57 +0500 Subject: [PATCH 2/4] Use updateTransition to avoid initial animation --- .../com/example/jetcaster/util/Buttons.kt | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt index ccc896195..b62ed16cd 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt @@ -16,8 +16,11 @@ package com.example.jetcaster.util +import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @@ -56,13 +59,23 @@ fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modi checkedShape = CircleShape, ), ) { - val iconRotation by animateFloatAsState( - targetValue = if(isFollowed) 0f else 360f, - animationSpec = spring( - dampingRatio = .6f, - stiffness = 200f - ) - ) + val transition = updateTransition(targetState = isFollowed, label = "FollowToggle") + val iconRotation by transition.animateFloat( + label = "IconRotation", + transitionSpec = { + if (initialState == targetState) { + tween(durationMillis = 0) + } else { + spring( + dampingRatio = 0.6f, + stiffness = 200f + ) + } + } + ) { followed -> + if (followed) 360f else 0f + } + Icon( // Animated rotation when follow state changes From 6252f1829436099b9d84d4771af64bf6b6c000d0 Mon Sep 17 00:00:00 2001 From: XDdevv Date: Tue, 19 Aug 2025 15:21:16 +0500 Subject: [PATCH 3/4] Use snap() instead of tween to disable initial animation --- .../mobile/src/main/java/com/example/jetcaster/util/Buttons.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt index b62ed16cd..9c62f44f8 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt @@ -18,6 +18,7 @@ package com.example.jetcaster.util import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.snap import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.animation.core.updateTransition @@ -64,7 +65,7 @@ fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modi label = "IconRotation", transitionSpec = { if (initialState == targetState) { - tween(durationMillis = 0) + snap() } else { spring( dampingRatio = 0.6f, From 954420155b6ee94dd0406e6930533c222423ee17 Mon Sep 17 00:00:00 2001 From: XDdevv Date: Tue, 19 Aug 2025 15:37:11 +0500 Subject: [PATCH 4/4] Apply Spotless formatting --- .../src/main/java/com/example/jetcaster/util/Buttons.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt index 9c62f44f8..8bc981000 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt @@ -17,10 +17,8 @@ package com.example.jetcaster.util import androidx.compose.animation.core.animateFloat -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.snap import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -69,15 +67,14 @@ fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modi } else { spring( dampingRatio = 0.6f, - stiffness = 200f + stiffness = 200f, ) } - } + }, ) { followed -> if (followed) 360f else 0f } - Icon( // Animated rotation when follow state changes painter = when { @@ -88,7 +85,7 @@ fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modi isFollowed -> stringResource(R.string.cd_following) else -> stringResource(R.string.cd_not_following) }, - modifier = Modifier.rotate(iconRotation) + modifier = Modifier.rotate(iconRotation), ) } }