refactor(player): replace side sliders with centered adjustment indicators

- Replace the horizontal `PlayerSideSliders` with a new vertical `PlayerAdjustmentIndicator` for brightness and volume, displaying it in the center of the screen.
- Create `PlayerAdjustmentIndicator.kt`, a new composable that shows an icon, a vertical progress bar, and a percentage value within a semi-transparent black background.
- Update `PlayerScreen` to use the new centered indicator instead of the old side-aligned sliders when brightness or volume is adjusted via gestures.
- Pass a `modifier` to `ValueChangeTimedVisibility` to correctly position the new indicators.
- Change the background color of the "Seek to" toast message from a theme surface color to semi-transparent black for better consistency.
This commit is contained in:
2026-01-27 20:23:18 +01:00
parent d24eff19cc
commit aeec3784be
4 changed files with 108 additions and 102 deletions

View File

@@ -9,6 +9,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import kotlinx.coroutines.delay
/**
@@ -25,7 +26,8 @@ import kotlinx.coroutines.delay
fun <T> EmptyValueTimedVisibility(
value: T?,
hideAfterMillis: Long = 1_000,
content: @Composable (T) -> Unit,
modifier: Modifier = Modifier,
content: @Composable (T) -> Unit
) {
val shownValue = remember { mutableStateOf<T?>(null) }
@@ -55,7 +57,8 @@ fun <T> EmptyValueTimedVisibility(
fun <T> ValueChangeTimedVisibility(
value: T,
hideAfterMillis: Long = 1_000,
content: @Composable (T) -> Unit,
modifier: Modifier = Modifier,
content: @Composable (T) -> Unit
) {
var displayedValue by remember { mutableStateOf(value) }
var isVisible by remember { mutableStateOf(false) }
@@ -75,6 +78,7 @@ fun <T> ValueChangeTimedVisibility(
AnimatedVisibility(
visible = isVisible,
modifier = modifier,
enter = EnterTransition.None,
exit = ExitTransition.None
) {

View File

@@ -15,6 +15,9 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.BrightnessMedium
import androidx.compose.material.icons.outlined.VolumeUp
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -27,6 +30,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
@@ -35,12 +39,12 @@ import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import hu.bbara.purefin.common.ui.components.EmptyValueTimedVisibility
import hu.bbara.purefin.common.ui.components.ValueChangeTimedVisibility
import hu.bbara.purefin.player.ui.components.PlayerAdjustmentIndicator
import hu.bbara.purefin.player.ui.components.PlayerControlsOverlay
import hu.bbara.purefin.player.ui.components.PlayerGesturesLayer
import hu.bbara.purefin.player.ui.components.PlayerLoadingErrorEndCard
import hu.bbara.purefin.player.ui.components.PlayerQueuePanel
import hu.bbara.purefin.player.ui.components.PlayerSettingsSheet
import hu.bbara.purefin.player.ui.components.PlayerSideSliders
import hu.bbara.purefin.player.viewmodel.PlayerViewModel
import kotlin.math.abs
import kotlin.math.roundToInt
@@ -135,27 +139,33 @@ fun PlayerScreen(
ValueChangeTimedVisibility(
value = brightness,
hideAfterMillis = 800
hideAfterMillis = 800,
modifier = Modifier
.align(Alignment.CenterStart)
.padding(start = 20.dp)
) { currentBrightness ->
PlayerSideSliders(
modifier = Modifier.fillMaxSize(),
brightness = currentBrightness,
volume = volume,
showBrightness = true,
showVolume = false,
PlayerAdjustmentIndicator(
modifier = Modifier
.align(Alignment.Center),
icon = Icons.Outlined.BrightnessMedium,
contentDescription = "Brightness",
value = currentBrightness
)
}
ValueChangeTimedVisibility(
value = volume,
hideAfterMillis = 800
hideAfterMillis = 800,
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 20.dp)
) { currentVolume ->
PlayerSideSliders(
modifier = Modifier.fillMaxSize(),
brightness = brightness,
volume = currentVolume,
showBrightness = false,
showVolume = true,
PlayerAdjustmentIndicator(
modifier = Modifier
.align(Alignment.Center),
icon = Icons.Outlined.VolumeUp,
contentDescription = "Volume",
value = currentVolume
)
}
@@ -235,7 +245,7 @@ private fun SeekAmountIndicator(deltaMs: Long, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.clip(RoundedCornerShape(16.dp))
.background(scheme.surface.copy(alpha = 0.9f))
.background(Color.Black.copy(alpha = 0.9f))
.padding(horizontal = 20.dp, vertical = 12.dp)
) {
Text(

View File

@@ -0,0 +1,76 @@
package hu.bbara.purefin.player.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
@Composable
fun PlayerAdjustmentIndicator(
modifier: Modifier = Modifier,
icon: ImageVector,
contentDescription: String?,
value: Float,
sliderHeight: Dp = 140.dp,
) {
val scheme = MaterialTheme.colorScheme
val percent = (value.coerceIn(0f, 1f) * 100).roundToInt()
val clamped = value.coerceIn(0f, 1f)
Box(
modifier = modifier
.clip(RoundedCornerShape(16.dp))
.background(Color.Black.copy(alpha = 0.5f))
.padding(horizontal = 20.dp, vertical = 16.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = icon,
contentDescription = contentDescription,
tint = scheme.onSurface
)
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.width(10.dp)
.height(sliderHeight)
.clip(RoundedCornerShape(5.dp))
.background(scheme.onSurface.copy(alpha = 0.2f))
) {
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.width(10.dp)
.height(sliderHeight * clamped)
.clip(RoundedCornerShape(5.dp))
.background(scheme.primary)
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "$percent%",
color = scheme.onSurface,
style = MaterialTheme.typography.titleMedium
)
}
}
}

View File

@@ -1,84 +0,0 @@
package hu.bbara.purefin.player.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.BrightnessMedium
import androidx.compose.material.icons.outlined.VolumeUp
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@Composable
fun PlayerSideSliders(
modifier: Modifier = Modifier,
brightness: Float,
volume: Float,
showBrightness: Boolean,
showVolume: Boolean,
) {
val scheme = MaterialTheme.colorScheme
Box(modifier = modifier.fillMaxWidth()) {
if (showBrightness) {
SideOverlay(
modifier = Modifier
.align(Alignment.CenterStart)
.padding(start = 16.dp),
icon = { Icon(Icons.Outlined.BrightnessMedium, contentDescription = null, tint = scheme.onBackground) },
progress = brightness,
scheme = scheme
)
}
if (showVolume) {
SideOverlay(
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 16.dp),
icon = { Icon(Icons.Outlined.VolumeUp, contentDescription = null, tint = scheme.onBackground) },
progress = volume,
scheme = scheme
)
}
}
}
@Composable
private fun SideOverlay(
modifier: Modifier = Modifier,
icon: @Composable () -> Unit,
progress: Float,
scheme: androidx.compose.material3.ColorScheme
) {
Column(
modifier = modifier
.fillMaxHeight(0.3f)
.wrapContentHeight(align = Alignment.CenterVertically)
.clip(RoundedCornerShape(18.dp))
.background(scheme.background.copy(alpha = 0.8f))
.padding(horizontal = 14.dp, vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
icon()
LinearProgressIndicator(
progress = { progress.coerceIn(0f, 1f) },
modifier = Modifier
.padding(top = 10.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp)),
color = scheme.primary,
trackColor = scheme.onBackground.copy(alpha = 0.2f)
)
}
}