mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
[UI] Add playback overlay dark gradient background (#505)
## Description This PR adds a background dimming effect behind the playback controls to fix readability issues against bright video content. It implements a full-screen vertical gradient that ensures white text and buttons remain legible. I added a vertical gradient fading from transparent at the top to ~95% black at the bottom which adds contrast for UI elements without obscuring the center of the video. ### Related issues Solves [#485](https://github.com/damontecres/Wholphin/issues/485) ### Screenshots <img width="1948" height="1112" alt="playback" src="https://github.com/user-attachments/assets/f671072c-e37a-491d-b82f-fb6f888a42c7" /> <img width="1918" height="1081" alt="no_gradient" src="https://github.com/user-attachments/assets/30bc2fee-ef5a-4d63-87a6-953e401cd84b" /> ### AI/LLM usage This change was developed with the assistance of AI, but was not wholly generated by AI. --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Damontecres <damontecres@gmail.com> Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
This commit is contained in:
parent
0c823b91cd
commit
e60748a0d0
4 changed files with 63 additions and 31 deletions
|
|
@ -17,6 +17,7 @@ fun BoxScope.TimeDisplay(modifier: Modifier = Modifier) {
|
||||||
text = LocalClock.current.timeString,
|
text = LocalClock.current.timeString,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
|
@ -34,6 +35,8 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.focusRestorer
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.key.KeyEventType
|
import androidx.compose.ui.input.key.KeyEventType
|
||||||
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||||
import androidx.compose.ui.input.key.type
|
import androidx.compose.ui.input.key.type
|
||||||
|
|
@ -127,10 +130,37 @@ fun PlaybackOverlay(
|
||||||
var controllerHeight by remember { mutableStateOf(0.dp) }
|
var controllerHeight by remember { mutableStateOf(0.dp) }
|
||||||
var state by remember { mutableStateOf(OverlayViewState.CONTROLLER) }
|
var state by remember { mutableStateOf(OverlayViewState.CONTROLLER) }
|
||||||
|
|
||||||
|
// Background scrim for OSD readability
|
||||||
|
val scrimBrush =
|
||||||
|
remember {
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors =
|
||||||
|
listOf(
|
||||||
|
Color.Transparent,
|
||||||
|
Color.Black.copy(alpha = 0.5f),
|
||||||
|
Color.Black.copy(alpha = 0.80f),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
contentAlignment = Alignment.BottomCenter,
|
contentAlignment = Alignment.BottomCenter,
|
||||||
) {
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = controllerViewState.controlsVisible,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
modifier = Modifier.matchParentSize(),
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(scrimBrush),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
state == OverlayViewState.CONTROLLER,
|
state == OverlayViewState.CONTROLLER,
|
||||||
enter = slideInVertically() + fadeIn(),
|
enter = slideInVertically() + fadeIn(),
|
||||||
|
|
|
||||||
|
|
@ -332,37 +332,6 @@ fun PlaybackPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtitles
|
|
||||||
if (skipIndicatorDuration == 0L && currentItemPlayback.subtitleIndexEnabled) {
|
|
||||||
val maxSize by animateFloatAsState(if (controllerViewState.controlsVisible) .7f else 1f)
|
|
||||||
AndroidView(
|
|
||||||
factory = { context ->
|
|
||||||
SubtitleView(context).apply {
|
|
||||||
preferences.appPreferences.interfacePreferences.subtitlesPreferences.let {
|
|
||||||
setStyle(it.toSubtitleStyle())
|
|
||||||
setFixedTextSize(Dimension.SP, it.fontSize.toFloat())
|
|
||||||
setBottomPaddingFraction(it.margin.toFloat() / 100f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
update = {
|
|
||||||
it.setCues(cues)
|
|
||||||
Media3SubtitleOverride(
|
|
||||||
preferences.appPreferences.interfacePreferences.subtitlesPreferences
|
|
||||||
.calculateEdgeSize(density),
|
|
||||||
).apply(it)
|
|
||||||
},
|
|
||||||
onReset = {
|
|
||||||
it.setCues(null)
|
|
||||||
},
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.fillMaxSize(maxSize)
|
|
||||||
.align(Alignment.TopCenter)
|
|
||||||
.background(Color.Transparent),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The playback controls
|
// The playback controls
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
controllerViewState.controlsVisible,
|
controllerViewState.controlsVisible,
|
||||||
|
|
@ -402,6 +371,37 @@ fun PlaybackPage(
|
||||||
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subtitles
|
||||||
|
if (skipIndicatorDuration == 0L && currentItemPlayback.subtitleIndexEnabled) {
|
||||||
|
val maxSize by animateFloatAsState(if (controllerViewState.controlsVisible) .7f else 1f)
|
||||||
|
AndroidView(
|
||||||
|
factory = { context ->
|
||||||
|
SubtitleView(context).apply {
|
||||||
|
preferences.appPreferences.interfacePreferences.subtitlesPreferences.let {
|
||||||
|
setStyle(it.toSubtitleStyle())
|
||||||
|
setFixedTextSize(Dimension.SP, it.fontSize.toFloat())
|
||||||
|
setBottomPaddingFraction(it.margin.toFloat() / 100f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update = {
|
||||||
|
it.setCues(cues)
|
||||||
|
Media3SubtitleOverride(
|
||||||
|
preferences.appPreferences.interfacePreferences.subtitlesPreferences
|
||||||
|
.calculateEdgeSize(density),
|
||||||
|
).apply(it)
|
||||||
|
},
|
||||||
|
onReset = {
|
||||||
|
it.setCues(null)
|
||||||
|
},
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize(maxSize)
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
.background(Color.Transparent),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask to skip intros, etc button
|
// Ask to skip intros, etc button
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ fun SkipIndicator(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
text = abs(durationMs / 1000).toString(),
|
text = abs(durationMs / 1000).toString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue