Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ import com.theveloper.pixelplay.presentation.components.snapping.rememberLazyLis
import com.theveloper.pixelplay.presentation.components.snapping.rememberSnapperFlingBehavior
import com.theveloper.pixelplay.utils.LyricsUtils
import com.theveloper.pixelplay.presentation.components.subcomps.LyricsMoreBottomSheet
import android.content.BroadcastReceiver
import android.content.Intent
import android.content.IntentFilter
import androidx.compose.ui.platform.LocalView
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import androidx.datastore.preferences.core.booleanPreferencesKey
Expand Down Expand Up @@ -348,6 +352,67 @@ fun LyricsSheet(
}
val animatedLyricsBlurStrength by animatedLyricsBlurStrengthFlow.collectAsStateWithLifecycle(initialValue = 2.5f)

// Read keep-screen-on preference from DataStore
val keepScreenOnFlow = remember(context) {
context.dataStore.data.map { it[booleanPreferencesKey("keep_screen_on_lyrics")] ?: false }
}
var keepScreenOn by remember { mutableStateOf(false) }
// Sync DataStore → local state
LaunchedEffect(Unit) {
keepScreenOnFlow.collect { keepScreenOn = it }
}
val coroutineScope = rememberCoroutineScope()

// Apply FLAG_KEEP_SCREEN_ON via the window when enabled
val view = LocalView.current
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current

DisposableEffect(keepScreenOn, lifecycleOwner) {
val observer = androidx.lifecycle.LifecycleEventObserver { _, event ->
if (event == androidx.lifecycle.Lifecycle.Event.ON_STOP && keepScreenOn) {
keepScreenOn = false
coroutineScope.launch {
context.dataStore.edit { prefs ->
prefs[booleanPreferencesKey("keep_screen_on_lyrics")] = false
}
}
}
}

if (keepScreenOn) {
view.keepScreenOn = true
}

lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
view.keepScreenOn = false
lifecycleOwner.lifecycle.removeObserver(observer)
}
}

DisposableEffect(keepScreenOn, lifecycleOwner) {
val observer = androidx.lifecycle.LifecycleEventObserver { _, event ->
if (event == androidx.lifecycle.Lifecycle.Event.ON_STOP && keepScreenOn) {
keepScreenOn = false
coroutineScope.launch {
context.dataStore.edit { prefs ->
prefs[booleanPreferencesKey("keep_screen_on_lyrics")] = false
}
}
}
}

if (keepScreenOn) {
view.keepScreenOn = true
}

lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
view.keepScreenOn = false
lifecycleOwner.lifecycle.removeObserver(observer)
}
}

val resolvedAutoscrollSpec = autoscrollAnimationSpec ?: if (useAnimatedLyrics) {
spring(
stiffness = Spring.StiffnessMediumLow,
Expand Down Expand Up @@ -395,7 +460,25 @@ fun LyricsSheet(
val swipeThresholdPx = with(LocalDensity.current) { swipeThreshold.toPx() }
val overlayTranslation = remember { Animatable(0f) }
val swipeProgress = remember { Animatable(0f) }
val coroutineScope = rememberCoroutineScope()

// Reset keep-screen-on when the physical screen goes off (power button / OEM sleep gesture).
// ACTION_SCREEN_OFF is a guaranteed platform broadcast; no OEM can suppress it.
DisposableEffect(Unit) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(ctx: android.content.Context, intent: Intent) {
if (intent.action == Intent.ACTION_SCREEN_OFF) {
keepScreenOn = false
coroutineScope.launch {
context.dataStore.edit { prefs ->
prefs[booleanPreferencesKey("keep_screen_on_lyrics")] = false
}
}
}
}
}
context.registerReceiver(receiver, IntentFilter(Intent.ACTION_SCREEN_OFF))
onDispose { context.unregisterReceiver(receiver) }
}

// Auto-hide controls logic
LaunchedEffect(immersiveLyricsEnabled, lastInteractionTime, showSyncedLyrics, isImmersiveTemporarilyDisabled) {
Expand Down Expand Up @@ -523,10 +606,15 @@ fun LyricsSheet(
// Read backProgressProvider inside graphicsLayer (draw-phase) — no layout
// pass is triggered per gesture frame, same pattern as SheetVisualState.
// 0f = fully visible, 1f = fully dismissed.
// Effect: slides down 8 % of height (no fade).
// Effect: scale down to 92 % + slide down 8 % of height + fade to 72 % alpha.
// Matches Android predictive back spec for full-screen destinations and
// mirrors the scale+alpha treatment used across the rest of the app.
.graphicsLayer {
val p = backProgressProvider.value
translationY = androidx.compose.ui.util.lerp(0f, size.height * 0.08f, p)
val scale = lerp(1f, 0.92f, p)
scaleX = scale
scaleY = scale
translationY = lerp(0f, size.height * 0.08f, p)
}
.clip(RoundedCornerShape(32.dp))
.pointerInput(Unit) {
Expand Down Expand Up @@ -938,6 +1026,15 @@ fun LyricsSheet(
resetImmersiveTimer()
onSetImmersiveTemporarilyDisabled(it)
},
keepScreenOn = keepScreenOn,
onKeepScreenOnChange = { enabled ->
keepScreenOn = enabled
coroutineScope.launch {
context.dataStore.edit { prefs ->
prefs[booleanPreferencesKey("keep_screen_on_lyrics")] = enabled
}
}
},
lyricsAlignment = lyricsAlignment,
onLyricsAlignmentChange = { newAlignment ->
coroutineScope.launch {
Expand Down Expand Up @@ -1562,6 +1659,11 @@ fun LyricWordSpan(
unhighlightedColor: Color,
modifier: Modifier = Modifier
) {
val wordAnimSpec = if (useAnimatedLyrics) spring<Float>(
stiffness = Spring.StiffnessVeryLow,
dampingRatio = Spring.DampingRatioMediumBouncy
) else tween(durationMillis = 200)

val color by animateColorAsState(
targetValue = if (isHighlighted) highlightedColor else unhighlightedColor,
animationSpec = if (useAnimatedLyrics) spring(
Expand All @@ -1570,6 +1672,23 @@ fun LyricWordSpan(
) else tween(durationMillis = 200),
label = "wordColor"
)

// Scale: pop up to 1.10 on highlight, settle back to 1f. Only active when
// animated lyrics is on — layout is untouched because it's applied in graphicsLayer.
val scale by animateFloatAsState(
targetValue = if (useAnimatedLyrics && isHighlighted) 1.10f else 1f,
animationSpec = wordAnimSpec,
label = "wordScale"
)

// Alpha: unhighlighted words dim slightly so the active word pops without
// needing a hard color contrast. Only active when animated lyrics is on.
val alpha by animateFloatAsState(
targetValue = if (useAnimatedLyrics && !isHighlighted) 0.55f else 1f,
animationSpec = wordAnimSpec,
label = "wordAlpha"
)

Box(
modifier = modifier,
contentAlignment = Alignment.Center
Expand All @@ -1586,6 +1705,12 @@ fun LyricWordSpan(
style = style,
color = color,
fontWeight = if (isHighlighted) FontWeight.Bold else FontWeight.Normal,
// Scale and alpha applied at draw phase — zero layout impact per frame.
modifier = Modifier.graphicsLayer {
scaleX = scale
scaleY = scale
this.alpha = alpha
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import androidx.compose.material.icons.rounded.Abc
import androidx.compose.material.icons.rounded.FormatAlignCenter
import androidx.compose.material.icons.rounded.Tune
import androidx.compose.material.icons.rounded.Translate
import androidx.compose.material.icons.rounded.BrightnessHigh
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
Expand Down Expand Up @@ -66,6 +69,8 @@ fun LyricsMoreBottomSheet(
onToggleSyncControls: () -> Unit,
isImmersiveTemporarilyDisabled: Boolean,
onSetImmersiveTemporarilyDisabled: (Boolean) -> Unit,
keepScreenOn: Boolean,
onKeepScreenOnChange: (Boolean) -> Unit,
lyricsAlignment: String,
onLyricsAlignmentChange: (String) -> Unit,
hasTranslatedLyrics: Boolean,
Expand Down Expand Up @@ -292,15 +297,17 @@ fun LyricsMoreBottomSheet(
val isRomanizationVisible = hasRomanizedLyrics
val isTranslationVisible = hasTranslatedLyrics
val isImmersiveVisible = showSyncedLyrics && immersiveLyricsEnabled
val isKeepScreenOnVisible = true

if (isSyncVisible || isRomanizationVisible || isTranslationVisible) {
if (isSyncVisible || isRomanizationVisible || isTranslationVisible || isKeepScreenOnVisible) {
// Determine first and last items for rounding
val isRomanizationFirst = isRomanizationVisible && !isSyncVisible
val isTranslationFirst = isTranslationVisible && !isSyncVisible && !isRomanizationVisible

val isSyncLast = isSyncVisible && !isRomanizationVisible && !isTranslationVisible && !isImmersiveVisible
val isRomanizationLast = isRomanizationVisible && !isTranslationVisible && !isImmersiveVisible
val isTranslationLast = isTranslationVisible && !isImmersiveVisible
val isSyncLast = isSyncVisible && !isRomanizationVisible && !isTranslationVisible && !isImmersiveVisible && !isKeepScreenOnVisible
val isRomanizationLast = isRomanizationVisible && !isTranslationVisible && !isImmersiveVisible && !isKeepScreenOnVisible
val isTranslationLast = isTranslationVisible && !isImmersiveVisible && !isKeepScreenOnVisible
val isImmersiveLast = isImmersiveVisible && !isKeepScreenOnVisible

Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
Expand Down Expand Up @@ -461,6 +468,47 @@ fun LyricsMoreBottomSheet(
)
)
},
modifier = Modifier
.fillMaxWidth()
.clip(
RoundedCornerShape(
topStart = 8.dp,
topEnd = 8.dp,
bottomStart = if (isImmersiveLast) 24.dp else 8.dp,
bottomEnd = if (isImmersiveLast) 24.dp else 8.dp
)
)
.background(itemBackgroundColor),
colors = ListItemDefaults.colors(
containerColor = Color.Transparent,
headlineColor = contentColor,
leadingIconColor = contentColor
)
)
}

// Keep Screen On Toggle
if (isKeepScreenOnVisible) {
ListItem(
headlineContent = { Text(stringResource(R.string.lyrics_more_keep_screen_on)) },
leadingContent = {
Icon(
imageVector = Icons.Rounded.BrightnessHigh,
contentDescription = null
)
},
trailingContent = {
Switch(
checked = keepScreenOn,
onCheckedChange = onKeepScreenOnChange,
colors = SwitchDefaults.colors(
checkedThumbColor = onAccentColor,
checkedTrackColor = accentColor,
uncheckedThumbColor = contentColor,
uncheckedTrackColor = contentColor.copy(alpha = 0.3f)
)
)
},
modifier = Modifier
.fillMaxWidth()
.clip(
Expand All @@ -471,7 +519,8 @@ fun LyricsMoreBottomSheet(
bottomEnd = 24.dp
)
)
.background(itemBackgroundColor),
.background(itemBackgroundColor)
.clickable { onKeepScreenOnChange(!keepScreenOn) },
colors = ListItemDefaults.colors(
containerColor = Color.Transparent,
headlineColor = contentColor,
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-de/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Romanisierung anzeigen</string>
<string name="lyrics_more_show_translations">Übersetzungen anzeigen</string>
<string name="lyrics_more_disable_immersive_once">Immersive einmal deaktivieren</string>
<string name="lyrics_more_keep_screen_on">Bildschirm an lassen</string>
<string name="cd_lyrics_align_left">Lyrics linksbündig</string>
<string name="cd_lyrics_align_center">Lyrics zentriert</string>
<string name="cd_lyrics_align_right">Lyrics rechtsbündig</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-es/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Mostrar romanización</string>
<string name="lyrics_more_show_translations">Mostrar traducciones</string>
<string name="lyrics_more_disable_immersive_once">Desactivar inmersivo (una vez)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">Alinear letras a la izquierda</string>
<string name="cd_lyrics_align_center">Alinear letras al centro</string>
<string name="cd_lyrics_align_right">Alinear letras a la derecha</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Afficher la romanisation</string>
<string name="lyrics_more_show_translations">Afficher les traductions</string>
<string name="lyrics_more_disable_immersive_once">Désactiver l\'immersion (une fois)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">Aligner les paroles à gauche</string>
<string name="cd_lyrics_align_center">Aligner les paroles au centre</string>
<string name="cd_lyrics_align_right">Aligner les paroles à droite</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-in/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Tampilkan romanisasi</string>
<string name="lyrics_more_show_translations">Tampilkan terjemahan</string>
<string name="lyrics_more_disable_immersive_once">Nonaktifkan imersif (sekali)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">Rata kiri lirik</string>
<string name="cd_lyrics_align_center">Rata tengah lirik</string>
<string name="cd_lyrics_align_right">Rata kanan lirik</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-it/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Mostra romanizzazione</string>
<string name="lyrics_more_show_translations">Mostra traduzioni</string>
<string name="lyrics_more_disable_immersive_once">Disabilita immersivo (una volta)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">Allinea testo a sinistra</string>
<string name="cd_lyrics_align_center">Allinea testo al centro</string>
<string name="cd_lyrics_align_right">Allinea testo a destra</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ko/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">로마자 표기 표시</string>
<string name="lyrics_more_show_translations">번역 표시</string>
<string name="lyrics_more_disable_immersive_once">몰입 모드 해제 (1회)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">가사 왼쪽 정렬</string>
<string name="cd_lyrics_align_center">가사 가운데 정렬</string>
<string name="cd_lyrics_align_right">가사 오른쪽 정렬</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-nb/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Vis romanisering</string>
<string name="lyrics_more_show_translations">Vis oversettelser</string>
<string name="lyrics_more_disable_immersive_once">Deaktiver immersiv modus (én gang)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">Venstrejuster tekst</string>
<string name="cd_lyrics_align_center">Midtstill tekst</string>
<string name="cd_lyrics_align_right">Høyrejuster tekst</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ru/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Показать романизацию</string>
<string name="lyrics_more_show_translations">Показать перевод</string>
<string name="lyrics_more_disable_immersive_once">Выйти из иммерсивного (раз)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">По левому краю</string>
<string name="cd_lyrics_align_center">По центру</string>
<string name="cd_lyrics_align_right">По правому краю</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-zh-rCN/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">显示罗马音</string>
<string name="lyrics_more_show_translations">显示翻译</string>
<string name="lyrics_more_disable_immersive_once">关闭沉浸模式(仅本次)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">歌词左对齐</string>
<string name="cd_lyrics_align_center">歌词居中对齐</string>
<string name="cd_lyrics_align_right">歌词右对齐</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings_components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<string name="lyrics_more_show_romanization">Show romanization</string>
<string name="lyrics_more_show_translations">Show translations</string>
<string name="lyrics_more_disable_immersive_once">Disable immersive (once)</string>
<string name="lyrics_more_keep_screen_on">Keep screen on</string>
<string name="cd_lyrics_align_left">Align lyrics left</string>
<string name="cd_lyrics_align_center">Align lyrics center</string>
<string name="cd_lyrics_align_right">Align lyrics right</string>
Expand Down
Loading