Skip to content

Commit

Permalink
Reduce frequency of progress animations (#1284)
Browse files Browse the repository at this point in the history
  • Loading branch information
yschimke authored May 17, 2023
1 parent d6b39f1 commit 432d5eb
Showing 1 changed file with 92 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
Expand All @@ -37,8 +41,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
Expand All @@ -51,6 +61,7 @@ import androidx.wear.compose.material.ButtonDefaults
import androidx.wear.compose.material.CircularProgressIndicator
import androidx.wear.compose.material.LocalContentAlpha
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.ProgressIndicatorDefaults
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.rememberLottieAnimatable
Expand All @@ -64,6 +75,8 @@ import com.google.android.horologist.media.ui.state.model.TrackPositionUiModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flowOf
import kotlin.math.floor
import kotlin.math.min

@Composable
public fun AnimatedPlayPauseButton(
Expand Down Expand Up @@ -193,7 +206,8 @@ public fun AnimatedPlayPauseProgressButton(
) {
val animatedProgressColor = animateColorAsState(
targetValue = progressColor,
animationSpec = tween(450, 0, LinearEasing)
animationSpec = tween(450, 0, LinearEasing),
"Progress Colour"
)

AnimatedPlayPauseButton(
Expand All @@ -207,7 +221,6 @@ public fun AnimatedPlayPauseProgressButton(
tapTargetSize = tapTargetSize,
backgroundColor = backgroundColor
) {
val progress by ProgressStateHolder.fromTrackPositionUiModel(trackPositionUiModel)
if (trackPositionUiModel.isLoading) {
CircularProgressIndicator(
modifier = Modifier.fillMaxSize(),
Expand All @@ -216,14 +229,17 @@ public fun AnimatedPlayPauseProgressButton(
strokeWidth = progressStrokeWidth
)
} else if (trackPositionUiModel.showProgress) {
CircularProgressIndicator(
val progress = ProgressStateHolder.fromTrackPositionUiModel(trackPositionUiModel)

CircularProgressIndicatorFast(
modifier = Modifier
.fillMaxSize()
.rotate(animateChangeAsRotation(rotateProgressIndicator)),
progress = progress,
indicatorColor = animatedProgressColor.value,
trackColor = trackColor,
strokeWidth = progressStrokeWidth
strokeWidth = progressStrokeWidth,
tapTargetSize = tapTargetSize
)
}
}
Expand All @@ -241,3 +257,75 @@ private fun animateChangeAsRotation(rotateProgressIndicator: Flow<Unit>): Float
)
return animatedProgressIndicatorRotation
}

// From https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java?q=CircularProgressIndicator
@Composable
private fun CircularProgressIndicatorFast(
progress: State<Float>,
modifier: Modifier = Modifier,
startAngle: Float = 270f,
endAngle: Float = startAngle,
indicatorColor: Color = MaterialTheme.colors.primary,
trackColor: Color = MaterialTheme.colors.onBackground.copy(alpha = 0.1f),
strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth,
tapTargetSize: DpSize
) {
val progressSteps = with(LocalDensity.current) {
tapTargetSize.width.toPx() * Math.PI
}
val truncatedProgress by remember { derivedStateOf { (floor(progress.value * progressSteps) / progressSteps).toFloat() } }
val semanticsProgress by remember { derivedStateOf { (floor(progress.value * 100) / 100) } }

val stroke = with(LocalDensity.current) {
Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
}

Canvas(
modifier
.progressSemantics(semanticsProgress)
.focusable()
) {
val backgroundSweep = 360f - ((startAngle - endAngle) % 360 + 360) % 360
val progressSweep = backgroundSweep * truncatedProgress.coerceIn(0f..1f)
// Draw a background
drawCircularIndicator(
startAngle,
backgroundSweep,
trackColor,
stroke
)

// Draw a progress
drawCircularIndicator(
startAngle,
progressSweep,
indicatorColor,
stroke
)
}
}

private fun DrawScope.drawCircularIndicator(
startAngle: Float,
sweep: Float,
color: Color,
stroke: Stroke
) {
// To draw this circle we need a rect with edges that line up with the midpoint of the stroke.
// To do this we need to remove half the stroke width from the total diameter for both sides.
val diameter = min(size.width, size.height)
val diameterOffset = stroke.width / 2
val arcDimen = diameter - 2 * diameterOffset
drawArc(
color = color,
startAngle = startAngle,
sweepAngle = sweep,
useCenter = false,
topLeft = Offset(
diameterOffset + (size.width - diameter) / 2,
diameterOffset + (size.height - diameter) / 2
),
size = Size(arcDimen, arcDimen),
style = stroke
)
}

0 comments on commit 432d5eb

Please sign in to comment.