diff --git a/android/app/src/main/java/com/thisux/droidclaw/overlay/GradientBorder.kt b/android/app/src/main/java/com/thisux/droidclaw/overlay/GradientBorder.kt new file mode 100644 index 0000000..316e3f9 --- /dev/null +++ b/android/app/src/main/java/com/thisux/droidclaw/overlay/GradientBorder.kt @@ -0,0 +1,85 @@ +package com.thisux.droidclaw.overlay + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp + +private val GradientColors = listOf( + Color(0xFF8B5CF6), // purple + Color(0xFF3B82F6), // blue + Color(0xFF06B6D4), // cyan + Color(0xFF10B981), // green + Color(0xFF8B5CF6), // purple (loop) +) + +@Composable +fun GradientBorder() { + val transition = rememberInfiniteTransition(label = "gradientRotation") + val offset by transition.animateFloat( + initialValue = 0f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis = 3000, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), + label = "gradientOffset" + ) + + val borderWidth = with(LocalDensity.current) { 4.dp.toPx() } + + Canvas(modifier = Modifier.fillMaxSize()) { + val w = size.width + val h = size.height + + val shiftedColors = shiftColors(GradientColors, offset) + + // Top edge + drawRect( + brush = Brush.horizontalGradient(shiftedColors), + topLeft = Offset.Zero, + size = Size(w, borderWidth) + ) + + // Bottom edge + drawRect( + brush = Brush.horizontalGradient(shiftedColors.reversed()), + topLeft = Offset(0f, h - borderWidth), + size = Size(w, borderWidth) + ) + + // Left edge + drawRect( + brush = Brush.verticalGradient(shiftedColors), + topLeft = Offset.Zero, + size = Size(borderWidth, h) + ) + + // Right edge + drawRect( + brush = Brush.verticalGradient(shiftedColors.reversed()), + topLeft = Offset(w - borderWidth, 0f), + size = Size(borderWidth, h) + ) + } +} + +private fun shiftColors(colors: List, offset: Float): List { + if (colors.size < 2) return colors + val n = colors.size + val shift = (offset * n).toInt() % n + return colors.subList(shift, n) + colors.subList(0, shift) +}