Merge pull request #1153 from silentbil/fix-hebrew
Fix Hebrew translations and RTL player seek direction
This commit is contained in:
commit
9951620f1b
5 changed files with 77 additions and 28 deletions
|
|
@ -573,6 +573,7 @@ private fun LegacySidebarScaffold(
|
|||
val openDrawerWidth = 196.dp
|
||||
|
||||
val focusManager = LocalFocusManager.current
|
||||
val isRtl = androidx.compose.ui.platform.LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Rtl
|
||||
val contentFocusRequester = remember { FocusRequester() }
|
||||
var pendingContentFocusTransfer by remember { mutableStateOf(false) }
|
||||
var pendingSidebarFocusRequest by remember { mutableStateOf(false) }
|
||||
|
|
@ -625,7 +626,8 @@ private fun LegacySidebarScaffold(
|
|||
.padding(12.dp)
|
||||
.selectableGroup()
|
||||
.onPreviewKeyEvent { keyEvent ->
|
||||
if (keyEvent.key == Key.DirectionRight && keyEvent.type == KeyEventType.KeyDown) {
|
||||
val closeKey = if (isRtl) Key.DirectionLeft else Key.DirectionRight
|
||||
if (keyEvent.key == closeKey && keyEvent.type == KeyEventType.KeyDown) {
|
||||
drawerState.setValue(DrawerValue.Closed)
|
||||
pendingContentFocusTransfer = false
|
||||
true
|
||||
|
|
@ -741,13 +743,14 @@ private fun LegacySidebarScaffold(
|
|||
.fillMaxSize()
|
||||
.padding(start = contentStartPadding)
|
||||
.onKeyEvent { keyEvent ->
|
||||
val openKey = if (isRtl) Key.DirectionRight else Key.DirectionLeft
|
||||
if (
|
||||
showSidebar &&
|
||||
drawerState.currentValue == DrawerValue.Closed &&
|
||||
keyEvent.type == KeyEventType.KeyDown &&
|
||||
keyEvent.key == Key.DirectionLeft
|
||||
keyEvent.key == openKey
|
||||
) {
|
||||
if (focusManager.moveFocus(FocusDirection.Left)) {
|
||||
if (focusManager.moveFocus(if (isRtl) FocusDirection.Right else FocusDirection.Left)) {
|
||||
true
|
||||
} else {
|
||||
pendingSidebarFocusRequest = true
|
||||
|
|
@ -875,6 +878,7 @@ private fun ModernSidebarScaffold(
|
|||
val openSidebarWidth = 262.dp
|
||||
|
||||
val focusManager = LocalFocusManager.current
|
||||
val isRtl = androidx.compose.ui.platform.LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Rtl
|
||||
val contentFocusRequester = remember { FocusRequester() }
|
||||
val drawerItemFocusRequesters = remember(drawerItems) {
|
||||
drawerItems.associate { item -> item.route to FocusRequester() }
|
||||
|
|
@ -1092,8 +1096,9 @@ private fun ModernSidebarScaffold(
|
|||
else -> Unit
|
||||
}
|
||||
}
|
||||
if (keyEvent.key == Key.DirectionLeft) {
|
||||
if (focusManager.moveFocus(FocusDirection.Left)) {
|
||||
val openKey = if (isRtl) Key.DirectionRight else Key.DirectionLeft
|
||||
if (keyEvent.key == openKey) {
|
||||
if (focusManager.moveFocus(if (isRtl) FocusDirection.Right else FocusDirection.Left)) {
|
||||
true
|
||||
} else {
|
||||
isSidebarExpanded = true
|
||||
|
|
@ -1156,10 +1161,15 @@ private fun ModernSidebarScaffold(
|
|||
focusedDrawerIndex == drawerItems.lastIndex
|
||||
}
|
||||
|
||||
Key.DirectionRight -> {
|
||||
pendingContentFocusTransfer = false
|
||||
sidebarCollapsePending = true
|
||||
true
|
||||
Key.DirectionRight, Key.DirectionLeft -> {
|
||||
val collapseKey = if (isRtl) Key.DirectionLeft else Key.DirectionRight
|
||||
if (keyEvent.key == collapseKey) {
|
||||
pendingContentFocusTransfer = false
|
||||
sidebarCollapsePending = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
else -> false
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ import android.text.format.DateFormat
|
|||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
|
|
@ -121,6 +124,8 @@ fun PlayerScreen(
|
|||
onPlaybackEnded: ((nextVideoId: String?, nextSeason: Int?, nextEpisode: Int?) -> Unit)? = null
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val isRtl = layoutDirection == LayoutDirection.Rtl
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val containerFocusRequester = remember { FocusRequester() }
|
||||
val playPauseFocusRequester = remember { FocusRequester() }
|
||||
|
|
@ -417,11 +422,8 @@ fun PlayerScreen(
|
|||
repeatCount >= 3 -> 20_000L
|
||||
else -> 10_000L
|
||||
}
|
||||
val deltaMs = if (keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
|
||||
-stepMs
|
||||
} else {
|
||||
stepMs
|
||||
}
|
||||
val isLeft = keyEvent.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_DPAD_LEFT
|
||||
val deltaMs = if (isLeft xor isRtl) -stepMs else stepMs
|
||||
viewModel.onEvent(PlayerEvent.OnPreviewSeekBy(deltaMs))
|
||||
true
|
||||
} else {
|
||||
|
|
@ -1029,6 +1031,7 @@ private fun PlayerControlsOverlay(
|
|||
onBack: () -> Unit,
|
||||
skipButtonVisible: Boolean = false
|
||||
) {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val customPlayPainter = rememberRawSvgPainter(R.raw.ic_player_play)
|
||||
val customPausePainter = rememberRawSvgPainter(R.raw.ic_player_pause)
|
||||
val customSubtitlePainter = rememberRawSvgPainter(R.raw.ic_player_subtitles)
|
||||
|
|
@ -1152,12 +1155,13 @@ private fun PlayerControlsOverlay(
|
|||
ProgressBar(
|
||||
currentPosition = uiState.pendingPreviewSeekPosition ?: uiState.currentPosition,
|
||||
duration = uiState.duration,
|
||||
onSeekPreview = { delta ->
|
||||
onSeekPreview = { delta ->
|
||||
viewModel.onEvent(PlayerEvent.OnPreviewSeekBy(delta))
|
||||
},
|
||||
onSeekCommit = {
|
||||
onSeekCommit = {
|
||||
viewModel.onEvent(PlayerEvent.OnCommitPreviewSeek)
|
||||
},
|
||||
isRtl = isRtl,
|
||||
focusRequester = progressBarFocusRequester,
|
||||
upFocusRequester = progressBarUpFocusRequester,
|
||||
downFocusRequester = playPauseFocusRequester,
|
||||
|
|
@ -1414,8 +1418,9 @@ private fun ControlButton(
|
|||
private fun ProgressBar(
|
||||
currentPosition: Long,
|
||||
duration: Long,
|
||||
onSeekPreview: (Long) -> Unit,
|
||||
onSeekCommit: () -> Unit,
|
||||
onSeekPreview: (Long) -> Unit,
|
||||
onSeekCommit: () -> Unit,
|
||||
isRtl: Boolean = false,
|
||||
focusRequester: FocusRequester? = null,
|
||||
upFocusRequester: FocusRequester? = null,
|
||||
downFocusRequester: FocusRequester? = null,
|
||||
|
|
@ -1493,11 +1498,11 @@ private fun ProgressBar(
|
|||
}
|
||||
}
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
onSeekPreview(-10_000L)
|
||||
onSeekPreview(if (isRtl) 10_000L else -10_000L)
|
||||
true
|
||||
}
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
onSeekPreview(10_000L)
|
||||
onSeekPreview(if (isRtl) -10_000L else 10_000L)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ fun SettingsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
val isRtl = androidx.compose.ui.platform.LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Rtl
|
||||
var selectedCategory by remember(visibleSections) {
|
||||
mutableStateOf(
|
||||
visibleSections.firstOrNull()?.category ?: SettingsCategory.APPEARANCE
|
||||
|
|
@ -298,7 +299,8 @@ fun SettingsScreen(
|
|||
}
|
||||
}
|
||||
.onPreviewKeyEvent { event ->
|
||||
if (event.type == KeyEventType.KeyDown && event.key == Key.DirectionRight) {
|
||||
val toDetailKey = if (isRtl) Key.DirectionLeft else Key.DirectionRight
|
||||
if (event.type == KeyEventType.KeyDown && event.key == toDetailKey) {
|
||||
allowDetailAutofocus = true
|
||||
false
|
||||
} else {
|
||||
|
|
@ -343,8 +345,9 @@ fun SettingsScreen(
|
|||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
.onKeyEvent { event ->
|
||||
if (event.type == KeyEventType.KeyDown && event.key == Key.DirectionLeft) {
|
||||
val movedLeft = focusManager.moveFocus(FocusDirection.Left)
|
||||
val toRailKey = if (isRtl) Key.DirectionRight else Key.DirectionLeft
|
||||
if (event.type == KeyEventType.KeyDown && event.key == toRailKey) {
|
||||
val movedLeft = focusManager.moveFocus(if (isRtl) FocusDirection.Right else FocusDirection.Left)
|
||||
if (!movedLeft) {
|
||||
allowDetailAutofocus = false
|
||||
val requested = railFocusRequesters[selectedCategory]?.let { requester ->
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ private fun RightStreamSection(
|
|||
onRetry: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val isRtl = androidx.compose.ui.platform.LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Rtl
|
||||
var enter by remember { mutableStateOf(false) }
|
||||
var shouldFocusFirstStream by remember { mutableStateOf(false) }
|
||||
var wasLoading by remember { mutableStateOf(true) }
|
||||
|
|
@ -620,14 +621,27 @@ private fun AddonFilterChips(
|
|||
focusRequesters: List<FocusRequester>,
|
||||
orderedNames: List<String>
|
||||
) {
|
||||
val isRtl = androidx.compose.ui.platform.LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Rtl
|
||||
val chipMap = sourceChips.associateBy { it.name }
|
||||
var chipRowHasFocus by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val lastKeyRepeatDispatchRef = remember { java.util.concurrent.atomic.AtomicLong(0L) }
|
||||
LazyRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
|
||||
modifier = Modifier
|
||||
.onFocusChanged { chipRowHasFocus = it.hasFocus }
|
||||
.onFocusChanged { focusState ->
|
||||
val hasFocus = focusState.hasFocus
|
||||
if (hasFocus && !chipRowHasFocus && isRtl) {
|
||||
val selectedIdx = if (selectedAddon == null) 0
|
||||
else (orderedNames.indexOf(selectedAddon) + 1).coerceAtLeast(0)
|
||||
scope.coroutineLaunch {
|
||||
withFrameNanos {}
|
||||
focusRequesters.getOrNull(selectedIdx)?.requestFocus()
|
||||
}
|
||||
}
|
||||
chipRowHasFocus = hasFocus
|
||||
}
|
||||
.onKeyEvent { event ->
|
||||
if (event.nativeKeyEvent.action != android.view.KeyEvent.ACTION_DOWN) return@onKeyEvent false
|
||||
|
||||
|
|
@ -642,10 +656,18 @@ private fun AddonFilterChips(
|
|||
val currentIdx = allOptions.indexOf(selectedAddon)
|
||||
when (event.key) {
|
||||
androidx.compose.ui.input.key.Key.DirectionLeft -> {
|
||||
if (currentIdx > 0) { onAddonSelected(allOptions[currentIdx - 1]); true } else false
|
||||
if (isRtl) {
|
||||
if (currentIdx < allOptions.lastIndex) { onAddonSelected(allOptions[currentIdx + 1]); true } else false
|
||||
} else {
|
||||
if (currentIdx > 0) { onAddonSelected(allOptions[currentIdx - 1]); true } else false
|
||||
}
|
||||
}
|
||||
androidx.compose.ui.input.key.Key.DirectionRight -> {
|
||||
if (currentIdx < allOptions.lastIndex) { onAddonSelected(allOptions[currentIdx + 1]); true } else false
|
||||
if (isRtl) {
|
||||
if (currentIdx > 0) { onAddonSelected(allOptions[currentIdx - 1]); true } else false
|
||||
} else {
|
||||
if (currentIdx < allOptions.lastIndex) { onAddonSelected(allOptions[currentIdx + 1]); true } else false
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
|
@ -783,6 +805,7 @@ private fun StreamsList(
|
|||
orderedAddonNames: List<String> = emptyList(),
|
||||
onFocusChanged: (Boolean) -> Unit = {}
|
||||
) {
|
||||
val isRtl = androidx.compose.ui.platform.LocalLayoutDirection.current == androidx.compose.ui.unit.LayoutDirection.Rtl
|
||||
val firstCardFocusRequester = remember { FocusRequester() }
|
||||
val lastKeyRepeatDispatchRef = remember { java.util.concurrent.atomic.AtomicLong(0L) }
|
||||
val restoreFocusRequester = remember { FocusRequester() }
|
||||
|
|
@ -833,10 +856,18 @@ private fun StreamsList(
|
|||
val currentIdx = allOptions.indexOf(selectedAddonFilter)
|
||||
when (event.key) {
|
||||
Key.DirectionLeft -> {
|
||||
if (currentIdx > 0) { onAddonFilterSelected(allOptions[currentIdx - 1]); true } else false
|
||||
if (isRtl) {
|
||||
if (currentIdx < allOptions.lastIndex) { onAddonFilterSelected(allOptions[currentIdx + 1]); true } else false
|
||||
} else {
|
||||
if (currentIdx > 0) { onAddonFilterSelected(allOptions[currentIdx - 1]); true } else false
|
||||
}
|
||||
}
|
||||
Key.DirectionRight -> {
|
||||
if (currentIdx < allOptions.lastIndex) { onAddonFilterSelected(allOptions[currentIdx + 1]); true } else false
|
||||
if (isRtl) {
|
||||
if (currentIdx > 0) { onAddonFilterSelected(allOptions[currentIdx - 1]); true } else false
|
||||
} else {
|
||||
if (currentIdx < allOptions.lastIndex) { onAddonFilterSelected(allOptions[currentIdx + 1]); true } else false
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue