Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing animated gradient #5004

Merged
merged 9 commits into from
Dec 21, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import it.fast4x.rimusic.appContext
import kotlinx.coroutines.CancellationException
import kotlin.coroutines.cancellation.CancellationException

@UnstableApi
class SongsShuffle private constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ import it.fast4x.rimusic.utils.thumbnailSpacingKey
import kotlinx.coroutines.delay
import it.fast4x.rimusic.colorPalette
import it.fast4x.rimusic.typography
import it.fast4x.rimusic.utils.thumbnailSpacingLKey

@Composable
fun TextFieldDialog(
Expand Down Expand Up @@ -1279,14 +1280,15 @@ fun BlurParamsDialog(
fun ThumbnailOffsetDialog(
onDismiss: () -> Unit,
spacingValue: (Float) -> Unit,
spacingValueL: (Float) -> Unit,
fadeValue: (Float) -> Unit,
imageCoverSizeValue: (Float) -> Unit
) {
val defaultFade = 5f
val defaultOffset = 0f
val defaultSpacing = 0f
val defaultImageCoverSize = 50f
var thumbnailSpacing by rememberPreference(thumbnailSpacingKey, defaultOffset)
var thumbnailSpacing by rememberPreference(thumbnailSpacingKey, defaultSpacing)
var thumbnailSpacingL by rememberPreference(thumbnailSpacingLKey, defaultSpacing)
var thumbnailFade by rememberPreference(thumbnailFadeKey, defaultFade)
var fadingedge by rememberPreference(fadingedgeKey, false)
var imageCoverSize by rememberPreference(VinylSizeKey, defaultImageCoverSize)
Expand All @@ -1295,6 +1297,7 @@ fun BlurParamsDialog(
DefaultDialog(
onDismiss = {
spacingValue(thumbnailSpacing)
spacingValueL(thumbnailSpacingL)
fadeValue(thumbnailFade)
imageCoverSizeValue(imageCoverSize)
onDismiss()
Expand Down Expand Up @@ -1415,7 +1418,7 @@ fun BlurParamsDialog(
*/
}
}
if (expandedplayer || isLandscape) {
if (expandedplayer && !isLandscape) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
Expand Down Expand Up @@ -1499,6 +1502,32 @@ fun BlurParamsDialog(
*/
}
}
if (isLandscape) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
) {
IconButton(
onClick = {
thumbnailSpacingL = defaultSpacing
},
icon = R.drawable.burger,
color = colorPalette().favoritesIcon,
modifier = Modifier
.size(24.dp)
)

SliderControl(
state = thumbnailSpacingL,
onSlide = { thumbnailSpacingL = it },
onSlideComplete = {},
toDisplay = { "%.0f".format(it) },
range = -50f..50f
)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package it.fast4x.rimusic.ui.screens.player

import androidx.annotation.OptIn
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
Expand All @@ -14,17 +17,38 @@ import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.media3.common.util.UnstableApi
import it.fast4x.rimusic.enums.ColorPaletteMode
import it.fast4x.rimusic.utils.colorPaletteModeKey
import it.fast4x.rimusic.utils.rememberPreference
import kotlin.math.sqrt

@OptIn(UnstableApi::class)
fun Modifier.animatedGradient(
animating: Boolean,
C1: Color,
C2: Color,
C3: Color,
C4: Color,
D: Color,
V: Color,
LV: Color,
DV: Color,
M: Color,
LM: Color,
DM: Color
): Modifier = composed {
val rotation = remember { Animatable(0f) }
val colorPaletteMode by rememberPreference(colorPaletteModeKey, ColorPaletteMode.Dark)
var lightTheme = colorPaletteMode == ColorPaletteMode.Light || (colorPaletteMode == ColorPaletteMode.System && (!isSystemInDarkTheme()))
var ratio = if (lightTheme) 1f else 0.3f

fun Color.darkenBy(): Color {
return copy(
red = red * ratio,
green = green * ratio,
blue = blue * ratio,
alpha = alpha
)
}

LaunchedEffect(rotation, animating) {
if (!animating) return@LaunchedEffect
Expand All @@ -48,21 +72,25 @@ fun Modifier.animatedGradient(
)

val brush1 = Brush.linearGradient(
0f to C1,
1f to C2,
0f to D.darkenBy(),
0.33f to LV.compositeOver(D).darkenBy(),
0.66f to V.compositeOver(D).darkenBy(),
1f to DV.compositeOver(D).darkenBy(),
start = topLeft,
end = Offset(rectSize * 0.7f, rectSize * 0.7f),
)

val brush2 = Brush.linearGradient(
0f to C3,
1f to C4,
0f to D.darkenBy(),
0.33f to LM.compositeOver(D).darkenBy(),
0.66f to M.compositeOver(D).darkenBy(),
1f to DM.compositeOver(D).darkenBy(),
start = Offset(rectSize, 0f),
end = Offset(0f, rectSize),
)

val maskBrush = Brush.linearGradient(
0f to Color.White,
0f to D.darkenBy(),
1f to Color.Transparent,
start = Offset(rectSize / 2f, 0f),
end = Offset(rectSize / 2f, rectSize),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,10 @@ import kotlin.math.absoluteValue
import androidx.compose.ui.unit.times
import androidx.compose.ui.util.lerp
import androidx.compose.ui.zIndex
import androidx.core.graphics.ColorUtils.colorToHSL
import androidx.media3.common.PlaybackException
import androidx.media3.common.Timeline
import androidx.palette.graphics.Palette
import dev.chrisbanes.haze.HazeDefaults
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.haze
Expand Down Expand Up @@ -267,8 +269,8 @@ import it.fast4x.rimusic.utils.thumbnailFadeKey
import it.fast4x.rimusic.colorPalette
import it.fast4x.rimusic.thumbnailShape
import it.fast4x.rimusic.typography
import it.fast4x.rimusic.ui.styling.AlbumPalette
import it.fast4x.rimusic.ui.styling.dynamicPaletteOf
import it.fast4x.rimusic.utils.seamlessPlay
import it.fast4x.rimusic.utils.thumbnailSpacingLKey
import it.fast4x.rimusic.utils.topPaddingKey


Expand Down Expand Up @@ -326,6 +328,7 @@ fun Player(
val defaultImageCoverSize = 50f
var blurStrength by rememberPreference(blurStrengthKey, defaultStrength)
var thumbnailSpacing by rememberPreference(thumbnailSpacingKey, defaultSpacing)
var thumbnailSpacingL by rememberPreference(thumbnailSpacingLKey, defaultSpacing)
var thumbnailFade by rememberPreference(thumbnailFadeKey, defaultFade)
var imageCoverSize by rememberPreference(VinylSizeKey, defaultImageCoverSize)
var blurDarkenFactor by rememberPreference(blurDarkenFactorKey, defaultDarkenFactor)
Expand Down Expand Up @@ -359,6 +362,7 @@ fun Player(
ThumbnailOffsetDialog(
onDismiss = { showThumbnailOffsetDialog = false},
spacingValue = { thumbnailSpacing = it },
spacingValueL = { thumbnailSpacingL = it },
fadeValue = { thumbnailFade = it },
imageCoverSizeValue = { imageCoverSize = it }
)
Expand Down Expand Up @@ -712,20 +716,30 @@ fun Player(
}
}
}
val defaultPalette = AlbumPalette(
dominant = Color.Magenta,
vibrant = Color.Cyan,
lightVibrant = Color(0xFF9E03FF),
darkVibrant = Color(0xFF11D36E),
muted = Color.White,
darkMuted = Color.Red,
lightMuted = Color.Transparent,
)

val color = colorPalette()
var dynamicColorPalette by remember { mutableStateOf( color ) }
var dynamicPalette by remember { mutableStateOf( defaultPalette ) }
var dominant by remember{ mutableStateOf(0) }
var vibrant by remember{ mutableStateOf(0) }
var lightVibrant by remember{ mutableStateOf(0) }
var darkVibrant by remember{ mutableStateOf(0) }
var muted by remember{ mutableStateOf(0) }
var lightMuted by remember{ mutableStateOf(0) }
var darkMuted by remember{ mutableStateOf(0) }



val colorPaletteMode by rememberPreference(colorPaletteModeKey, ColorPaletteMode.Dark)

@Composable
fun saturate(color : Int): Color {
val colorHSL by remember { mutableStateOf(floatArrayOf(0f, 0f, 0f)) }
var lightTheme = colorPaletteMode == ColorPaletteMode.Light || (colorPaletteMode == ColorPaletteMode.System && (!isSystemInDarkTheme()))
colorToHSL(color,colorHSL)
colorHSL[1] = (colorHSL[1] + if (lightTheme) 0f else 0.35f).coerceIn(0f,1f)
return Color.hsl(colorHSL[0],colorHSL[1],colorHSL[2])
}

val playerBackgroundColors by rememberPreference(
playerBackgroundColorsKey,
PlayerBackgroundColors.BlurredCoverColor
Expand Down Expand Up @@ -762,22 +776,25 @@ fun Player(
) ?: color
println("Player INSIDE getting dynamic color ${dynamicColorPalette}")

dynamicPalette = dynamicPaletteOf(
bitmap,
isSystemDarkMode
) ?: defaultPalette
val palette = Palette.from(bitmap).generate()

dominant = palette.getDominantColor(0)
vibrant = palette.getVibrantColor(0)
lightVibrant = palette.getLightVibrantColor(0)
darkVibrant = palette.getDarkVibrantColor(0)
muted = palette.getMutedColor(0)
lightMuted = palette.getLightMutedColor(0)
darkMuted = palette.getDarkMutedColor(0)

} catch (e: Exception) {
dynamicColorPalette = color
dynamicPalette = defaultPalette
println("Player Error getting dynamic color ${e.printStackTrace()}")
}

}
println("Player after getting dynamic color ${dynamicColorPalette}")
}

/* */
var sizeShader by remember { mutableStateOf(Size.Zero) }

val shaderA = LinearGradientShader(
Expand Down Expand Up @@ -987,11 +1004,15 @@ fun Player(
.onSizeChanged {
sizeShader = Size(it.width.toFloat(), it.height.toFloat())
}
.animatedGradient(binder.player.isPlaying,
dynamicPalette.darkVibrant,
dynamicPalette.lightVibrant,
dynamicPalette.darkMuted,
dynamicPalette.lightMuted
.animatedGradient(
binder.player.isPlaying,
saturate(dominant),
saturate(vibrant),
saturate(lightVibrant),
saturate(darkVibrant),
saturate(muted),
saturate(lightMuted),
saturate(darkMuted)
)
}

Expand Down Expand Up @@ -1641,6 +1662,8 @@ fun Player(
color = colorPalette().accent,
enabled = true,
onClick = {
binder.stopRadio()
binder.player.seamlessPlay(mediaItem)
binder.setupRadio(
NavigationEndpoint.Endpoint.Watch(videoId = mediaItem.mediaId)
)
Expand Down Expand Up @@ -1983,7 +2006,7 @@ fun Player(
HorizontalPager(
state = pagerState,
pageSize = PageSize.Fixed(thumbnailSizeDp),
pageSpacing = thumbnailSpacing.toInt()*0.01*(screenWidth) - (2.5*playerThumbnailSize.size.dp),
pageSpacing = thumbnailSpacingL.toInt()*0.01*(screenWidth) - (2.5*playerThumbnailSize.size.dp),
contentPadding = PaddingValues(start = (maxWidth - maxHeight)/2),
beyondViewportPageCount = 3,
flingBehavior = fling,
Expand Down Expand Up @@ -2477,6 +2500,11 @@ fun Player(
if (topPadding && !showTopActionsBar) {
Spacer(
modifier = Modifier
.padding(
windowInsets
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
.asPaddingValues()
)
.height(35.dp)
)
}
Expand Down Expand Up @@ -2859,6 +2887,8 @@ fun Player(

)
} else {
val index = if (pagerState.currentPage > binder.player.currentTimeline.windowCount) 0 else
pagerState.currentPage
Controls(
navController = navController,
onCollapse = onDismiss,
Expand All @@ -2869,8 +2899,8 @@ fun Player(
isShowingLyrics = isShowingLyrics,
media = mediaItem.toUiMedia(positionAndDuration.second),
mediaId = mediaItem.mediaId,
title = player.getMediaItemAt(pagerState.currentPage).mediaMetadata.title?.toString(),
artist = player.getMediaItemAt(pagerState.currentPage).mediaMetadata.artist?.toString(),
title = player.getMediaItemAt(index).mediaMetadata.title?.toString(),
artist = player.getMediaItemAt(index).mediaMetadata.artist?.toString(),
artistIds = artistsInfo,
albumId = albumId,
shouldBePlaying = shouldBePlaying,
Expand Down
Loading