mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Fix several issues with right-to-left locales (#1404)
## Description Fix several issues when the current locale is right-to-left - Fixes the backdrop dynamic colors - Fixes mirroring of playback controls, including seeking ### Related issues Fixes #1397 Fixes #1398 ### Testing Emulator w/ forced RTL in developer options ## Screenshots <img width="1280" height="720" alt="rtl_backdrop Large" src="https://github.com/user-attachments/assets/3e3b14ec-32cc-42c0-a224-d1d859a55acd" /> <img width="1280" height="720" alt="rtl_controls Large" src="https://github.com/user-attachments/assets/79983bb9-e1d0-4712-a5c6-da3d0721db96" /> ## AI or LLM usage None
This commit is contained in:
parent
656393fa1d
commit
9eff22e939
9 changed files with 214 additions and 117 deletions
|
|
@ -45,7 +45,9 @@ import androidx.compose.ui.input.key.KeyEventType
|
|||
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
|
|
@ -106,9 +108,11 @@ fun NowPlayingPage(
|
|||
).value.appPreferences
|
||||
val musicPrefs = preferences.musicPreferences
|
||||
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
val keyHandler =
|
||||
remember(preferences) {
|
||||
remember(isLtr, preferences) {
|
||||
PlaybackKeyHandler(
|
||||
isLtr = isLtr,
|
||||
player = player,
|
||||
controlsEnabled = true,
|
||||
skipWithLeftRight = false,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import androidx.compose.ui.graphics.SolidColor
|
|||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
|
|
@ -75,6 +77,7 @@ fun Backdrop(
|
|||
useExistingImageAsPlaceholder: Boolean = false,
|
||||
crossfadeDuration: Duration = 800.milliseconds,
|
||||
) {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val baseBackgroundColor = MaterialTheme.colorScheme.background
|
||||
if (backdrop.hasColors &&
|
||||
(backdropStyle == BackdropStyle.BACKDROP_DYNAMIC_COLOR || backdropStyle == BackdropStyle.UNRECOGNIZED)
|
||||
|
|
@ -100,12 +103,14 @@ fun Backdrop(
|
|||
.fillMaxSize()
|
||||
.drawBehind {
|
||||
drawRect(color = baseBackgroundColor)
|
||||
val start = if (isRtl) size.width else 0f
|
||||
val end = if (isRtl) 0f else size.width
|
||||
// Top Left (Vibrant/Muted)
|
||||
drawRect(
|
||||
brush =
|
||||
Brush.radialGradient(
|
||||
colors = listOf(animSecondary, Color.Transparent),
|
||||
center = Offset(0f, 0f),
|
||||
center = Offset(start, 0f),
|
||||
radius = size.width * 0.8f,
|
||||
),
|
||||
)
|
||||
|
|
@ -114,7 +119,7 @@ fun Backdrop(
|
|||
brush =
|
||||
Brush.radialGradient(
|
||||
colors = listOf(animPrimary, Color.Transparent),
|
||||
center = Offset(size.width, size.height),
|
||||
center = Offset(end, size.height),
|
||||
radius = size.width * 0.8f,
|
||||
),
|
||||
)
|
||||
|
|
@ -127,7 +132,7 @@ fun Backdrop(
|
|||
baseBackgroundColor,
|
||||
Color.Transparent,
|
||||
),
|
||||
center = Offset(0f, size.height),
|
||||
center = Offset(start, size.height),
|
||||
radius = size.width * 0.8f,
|
||||
),
|
||||
)
|
||||
|
|
@ -136,7 +141,7 @@ fun Backdrop(
|
|||
brush =
|
||||
Brush.radialGradient(
|
||||
colors = listOf(animTertiary, Color.Transparent),
|
||||
center = Offset(size.width, 0f),
|
||||
center = Offset(end, 0f),
|
||||
radius = size.width * 0.8f,
|
||||
),
|
||||
)
|
||||
|
|
@ -165,6 +170,7 @@ fun Backdrop(
|
|||
.fillMaxWidth(.7f)
|
||||
.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
|
||||
.drawWithContent {
|
||||
val start = if (isRtl) size.width else 0f
|
||||
drawContent()
|
||||
if (drawerIsOpen) {
|
||||
drawRect(
|
||||
|
|
@ -190,7 +196,7 @@ fun Backdrop(
|
|||
brush =
|
||||
Brush.horizontalGradient(
|
||||
colors = listOf(Color.Transparent, Color.Black),
|
||||
startX = 0f,
|
||||
startX = start,
|
||||
endX = size.width * 0.6f,
|
||||
),
|
||||
blendMode = BlendMode.DstIn,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ fun isControllerMedia(event: KeyEvent) =
|
|||
event.key == Key.ButtonL1 ||
|
||||
event.key == Key.ButtonL2
|
||||
|
||||
fun isDpadLeft(event: KeyEvent) =
|
||||
event.key == Key.DirectionLeft ||
|
||||
event.key == Key.SystemNavigationLeft
|
||||
|
||||
fun isDpadRight(event: KeyEvent) =
|
||||
event.key == Key.DirectionRight ||
|
||||
event.key == Key.SystemNavigationRight
|
||||
|
||||
fun isSkipBack(event: KeyEvent) =
|
||||
event.key == Key.DirectionLeft ||
|
||||
event.key == Key.ButtonL1 ||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
|
|
@ -85,6 +87,10 @@ fun PlaybackDialog(
|
|||
onPlaybackActionClick: (PlaybackAction) -> Unit,
|
||||
onChangeSubtitleDelay: (Duration) -> Unit,
|
||||
) {
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
// TODO, shouldn't this work out of the box?
|
||||
val leftGravity = remember(isLtr) { if (isLtr) Gravity.START else Gravity.END }
|
||||
val rightGravity = remember(isLtr) { if (isLtr) Gravity.END else Gravity.START }
|
||||
when (type) {
|
||||
PlaybackDialogType.DEBUG -> {
|
||||
throw IllegalStateException("Should not open a dialog with " + PlaybackDialogType.DEBUG)
|
||||
|
|
@ -113,7 +119,7 @@ fun PlaybackDialog(
|
|||
onDismissRequest.invoke()
|
||||
onPlaybackActionClick.invoke(PlaybackAction.SearchCaptions)
|
||||
},
|
||||
gravity = Gravity.END,
|
||||
gravity = rightGravity,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +173,7 @@ fun PlaybackDialog(
|
|||
onClickPlaybackDialogType(choice.data)
|
||||
}
|
||||
},
|
||||
gravity = Gravity.START,
|
||||
gravity = leftGravity,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +188,7 @@ fun PlaybackDialog(
|
|||
onSelectChoice = { _, choice ->
|
||||
onPlaybackActionClick.invoke(PlaybackAction.ToggleAudio(choice.index))
|
||||
},
|
||||
gravity = Gravity.END,
|
||||
gravity = rightGravity,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +211,7 @@ fun PlaybackDialog(
|
|||
onSelectChoice = { _, value ->
|
||||
onPlaybackActionClick.invoke(PlaybackAction.PlaybackSpeed(value.data))
|
||||
},
|
||||
gravity = Gravity.START,
|
||||
gravity = leftGravity,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +234,7 @@ fun PlaybackDialog(
|
|||
onSelectChoice = { _, choice ->
|
||||
onPlaybackActionClick.invoke(PlaybackAction.Scale(choice.data))
|
||||
},
|
||||
gravity = Gravity.START,
|
||||
gravity = leftGravity,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import kotlin.time.Duration
|
|||
* Handles [KeyEvent]s during playback on [PlaybackPage]
|
||||
*/
|
||||
class PlaybackKeyHandler(
|
||||
private val isLtr: Boolean,
|
||||
private val player: Player,
|
||||
private val controlsEnabled: Boolean,
|
||||
private val skipWithLeftRight: Boolean,
|
||||
|
|
@ -47,11 +48,21 @@ class PlaybackKeyHandler(
|
|||
if (isDirectionalDpad(it) || isEnterKey(it) || isControllerMedia(it)) {
|
||||
if (!controllerViewState.controlsVisible) {
|
||||
if (skipWithLeftRight && isSkipBack(it)) {
|
||||
updateSkipIndicator(-seekBack.inWholeMilliseconds)
|
||||
player.seekBack(seekBack)
|
||||
if (isLtr) {
|
||||
updateSkipIndicator(-seekBack.inWholeMilliseconds)
|
||||
player.seekBack(seekBack)
|
||||
} else {
|
||||
updateSkipIndicator(seekForward.inWholeMilliseconds)
|
||||
player.seekForward(seekForward)
|
||||
}
|
||||
} else if (skipWithLeftRight && isSkipForward(it)) {
|
||||
player.seekForward(seekForward)
|
||||
updateSkipIndicator(seekForward.inWholeMilliseconds)
|
||||
if (isLtr) {
|
||||
player.seekForward(seekForward)
|
||||
updateSkipIndicator(seekForward.inWholeMilliseconds)
|
||||
} else {
|
||||
player.seekBack(seekBack)
|
||||
updateSkipIndicator(seekBack.inWholeMilliseconds)
|
||||
}
|
||||
} else if (oneClickPause && isEnterKey(it)) {
|
||||
val wasPlaying = player.isPlaying
|
||||
Util.handlePlayPauseButtonAction(player)
|
||||
|
|
@ -127,7 +138,7 @@ class PlaybackKeyHandler(
|
|||
return false
|
||||
}
|
||||
|
||||
val isBack = isSkipBack(event)
|
||||
val isBack = if (isLtr) isSkipBack(event) else isSkipForward(event)
|
||||
return when (event.type) {
|
||||
KeyEventType.KeyDown -> {
|
||||
val repeatCount = event.nativeKeyEvent.repeatCount
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ import androidx.compose.ui.layout.ContentScale
|
|||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
|
|
@ -234,25 +236,29 @@ fun PlaybackPageContent(
|
|||
skipIndicatorDuration += delta
|
||||
skipPosition = player.currentPosition
|
||||
}
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
val keyHandler =
|
||||
PlaybackKeyHandler(
|
||||
player = player,
|
||||
controlsEnabled = state.nextUp == null,
|
||||
skipWithLeftRight = true,
|
||||
seekForward = preferences.appPreferences.playbackPreferences.skipForwardMs.milliseconds,
|
||||
seekBack = preferences.appPreferences.playbackPreferences.skipBackMs.milliseconds,
|
||||
getDurationMs = { player.duration.coerceAtLeast(0L) },
|
||||
controllerViewState = controllerViewState,
|
||||
updateSkipIndicator = updateSkipIndicator,
|
||||
skipBackOnResume = preferences.appPreferences.playbackPreferences.skipBackOnResume,
|
||||
onInteraction = viewModel::reportInteraction,
|
||||
oneClickPause = preferences.appPreferences.playbackPreferences.oneClickPause,
|
||||
onStop = {
|
||||
player.stop()
|
||||
viewModel.navigationManager.goBack()
|
||||
},
|
||||
onPlaybackDialogTypeClick = { playbackDialog = it },
|
||||
)
|
||||
remember(isLtr, preferences) {
|
||||
PlaybackKeyHandler(
|
||||
isLtr = isLtr,
|
||||
player = player,
|
||||
controlsEnabled = state.nextUp == null,
|
||||
skipWithLeftRight = true,
|
||||
seekForward = preferences.appPreferences.playbackPreferences.skipForwardMs.milliseconds,
|
||||
seekBack = preferences.appPreferences.playbackPreferences.skipBackMs.milliseconds,
|
||||
getDurationMs = { player.duration.coerceAtLeast(0L) },
|
||||
controllerViewState = controllerViewState,
|
||||
updateSkipIndicator = updateSkipIndicator,
|
||||
skipBackOnResume = preferences.appPreferences.playbackPreferences.skipBackOnResume,
|
||||
onInteraction = viewModel::reportInteraction,
|
||||
oneClickPause = preferences.appPreferences.playbackPreferences.oneClickPause,
|
||||
onStop = {
|
||||
player.stop()
|
||||
viewModel.navigationManager.goBack()
|
||||
},
|
||||
onPlaybackDialogTypeClick = { playbackDialog = it },
|
||||
)
|
||||
}
|
||||
|
||||
val onPlaybackActionClick: (PlaybackAction) -> Unit = {
|
||||
when (it) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ import androidx.compose.ui.focus.focusProperties
|
|||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.media3.common.Player
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
|
|
@ -47,6 +49,7 @@ fun ChapterRowOverlay(
|
|||
onChangeState: (OverlayViewState) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
val chapterInteractionSources =
|
||||
remember(chapters.size) { List(chapters.size) { MutableInteractionSource() } }
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
|
|
@ -135,7 +138,10 @@ fun ChapterRowOverlay(
|
|||
index == 0,
|
||||
Modifier.focusProperties {
|
||||
// Prevent scrolling left on first card to prevent moving down
|
||||
left = FocusRequester.Cancel
|
||||
left =
|
||||
if (isLtr) FocusRequester.Cancel else FocusRequester.Default
|
||||
right =
|
||||
if (isLtr) FocusRequester.Default else FocusRequester.Cancel
|
||||
},
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,10 +42,12 @@ import androidx.compose.ui.focus.onFocusChanged
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.isSpecified
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
|
|
@ -368,12 +370,13 @@ fun PlaybackButtons(
|
|||
seekForward: Duration,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
Row(
|
||||
modifier = modifier.focusGroup(),
|
||||
horizontalArrangement = Arrangement.spacedBy(buttonSpacing),
|
||||
) {
|
||||
PlaybackButton(
|
||||
iconRes = R.drawable.baseline_skip_previous_24,
|
||||
iconRes = if (isLtr) R.drawable.baseline_skip_previous_24 else R.drawable.baseline_skip_next_24,
|
||||
onClick = {
|
||||
onControllerInteraction.invoke()
|
||||
onPlaybackActionClick.invoke(PlaybackAction.Previous)
|
||||
|
|
@ -382,7 +385,7 @@ fun PlaybackButtons(
|
|||
onControllerInteraction = onControllerInteraction,
|
||||
)
|
||||
PlaybackButton(
|
||||
iconRes = R.drawable.baseline_fast_rewind_24,
|
||||
iconRes = if (isLtr) R.drawable.baseline_fast_rewind_24 else R.drawable.baseline_fast_forward_24,
|
||||
onClick = {
|
||||
onControllerInteraction.invoke()
|
||||
player.seekBack(seekBack)
|
||||
|
|
@ -406,7 +409,7 @@ fun PlaybackButtons(
|
|||
onControllerInteraction = onControllerInteraction,
|
||||
)
|
||||
PlaybackButton(
|
||||
iconRes = R.drawable.baseline_fast_forward_24,
|
||||
iconRes = if (isLtr) R.drawable.baseline_fast_forward_24 else R.drawable.baseline_fast_rewind_24,
|
||||
onClick = {
|
||||
onControllerInteraction.invoke()
|
||||
player.seekForward(seekForward)
|
||||
|
|
@ -414,7 +417,7 @@ fun PlaybackButtons(
|
|||
onControllerInteraction = onControllerInteraction,
|
||||
)
|
||||
PlaybackButton(
|
||||
iconRes = R.drawable.baseline_skip_next_24,
|
||||
iconRes = if (isLtr) R.drawable.baseline_skip_next_24 else R.drawable.baseline_skip_previous_24,
|
||||
onClick = {
|
||||
onControllerInteraction.invoke()
|
||||
onPlaybackActionClick.invoke(PlaybackAction.Next)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package com.github.damontecres.wholphin.ui.playback.overlay
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import android.view.KeyEvent
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.focusable
|
||||
|
|
@ -44,11 +43,16 @@ import androidx.compose.ui.graphics.StrokeCap
|
|||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import com.github.damontecres.wholphin.ui.playback.ControllerViewState
|
||||
import com.github.damontecres.wholphin.ui.playback.calculateSeekAccelerationMultiplier
|
||||
import com.github.damontecres.wholphin.ui.playback.isDpadLeft
|
||||
import com.github.damontecres.wholphin.ui.playback.isDpadRight
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
|
|
@ -174,6 +178,7 @@ private fun SeekBarDisplay(
|
|||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
val color = MaterialTheme.colorScheme.border
|
||||
val onSurface = MaterialTheme.colorScheme.onSurface
|
||||
|
||||
|
|
@ -192,70 +197,75 @@ private fun SeekBarDisplay(
|
|||
.height(animatedIndicatorHeight)
|
||||
.padding(horizontal = 4.dp)
|
||||
.onPreviewKeyEvent { event ->
|
||||
when (event.nativeKeyEvent.keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT -> {
|
||||
when (event.type) {
|
||||
KeyEventType.KeyDown -> {
|
||||
val repeatCount = event.nativeKeyEvent.repeatCount
|
||||
if (repeatCount > 0) {
|
||||
leftHandledByRepeat = true
|
||||
onLeft.invoke(
|
||||
calculateSeekAccelerationMultiplier(
|
||||
repeatCount = repeatCount,
|
||||
durationMs = durationMs,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
leftHandledByRepeat = false
|
||||
}
|
||||
}
|
||||
|
||||
KeyEventType.KeyUp -> {
|
||||
if (!leftHandledByRepeat) {
|
||||
onLeft.invoke(1)
|
||||
}
|
||||
if (!isDpadLeft(event) && !isDpadRight(event)) {
|
||||
Timber.v("Ignoring %s", event)
|
||||
return@onPreviewKeyEvent false
|
||||
}
|
||||
val seekBack =
|
||||
if (isLtr) {
|
||||
isDpadLeft(event)
|
||||
} else {
|
||||
isDpadRight(event)
|
||||
}
|
||||
if (seekBack) {
|
||||
when (event.type) {
|
||||
KeyEventType.KeyDown -> {
|
||||
val repeatCount = event.nativeKeyEvent.repeatCount
|
||||
if (repeatCount > 0) {
|
||||
leftHandledByRepeat = true
|
||||
onLeft.invoke(
|
||||
calculateSeekAccelerationMultiplier(
|
||||
repeatCount = repeatCount,
|
||||
durationMs = durationMs,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
leftHandledByRepeat = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
return@onPreviewKeyEvent false
|
||||
}
|
||||
}
|
||||
return@onPreviewKeyEvent true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT -> {
|
||||
when (event.type) {
|
||||
KeyEventType.KeyDown -> {
|
||||
val repeatCount = event.nativeKeyEvent.repeatCount
|
||||
if (repeatCount > 0) {
|
||||
rightHandledByRepeat = true
|
||||
onRight.invoke(
|
||||
calculateSeekAccelerationMultiplier(
|
||||
repeatCount = repeatCount,
|
||||
durationMs = durationMs,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
rightHandledByRepeat = false
|
||||
}
|
||||
KeyEventType.KeyUp -> {
|
||||
if (!leftHandledByRepeat) {
|
||||
onLeft.invoke(1)
|
||||
}
|
||||
leftHandledByRepeat = false
|
||||
}
|
||||
|
||||
KeyEventType.KeyUp -> {
|
||||
if (!rightHandledByRepeat) {
|
||||
onRight.invoke(1)
|
||||
}
|
||||
else -> {
|
||||
return@onPreviewKeyEvent false
|
||||
}
|
||||
}
|
||||
return@onPreviewKeyEvent true
|
||||
} else {
|
||||
when (event.type) {
|
||||
KeyEventType.KeyDown -> {
|
||||
val repeatCount = event.nativeKeyEvent.repeatCount
|
||||
if (repeatCount > 0) {
|
||||
rightHandledByRepeat = true
|
||||
onRight.invoke(
|
||||
calculateSeekAccelerationMultiplier(
|
||||
repeatCount = repeatCount,
|
||||
durationMs = durationMs,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
rightHandledByRepeat = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
return@onPreviewKeyEvent false
|
||||
}
|
||||
}
|
||||
return@onPreviewKeyEvent true
|
||||
|
||||
KeyEventType.KeyUp -> {
|
||||
if (!rightHandledByRepeat) {
|
||||
onRight.invoke(1)
|
||||
}
|
||||
rightHandledByRepeat = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
return@onPreviewKeyEvent false
|
||||
}
|
||||
}
|
||||
return@onPreviewKeyEvent true
|
||||
}
|
||||
false
|
||||
}.focusable(enabled = enabled, interactionSource = interactionSource),
|
||||
onDraw = {
|
||||
val yOffset = size.height.div(2)
|
||||
|
|
@ -266,33 +276,70 @@ private fun SeekBarDisplay(
|
|||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
drawLine(
|
||||
color = onSurface.copy(alpha = .65f),
|
||||
start = Offset(x = 0f, y = yOffset),
|
||||
end =
|
||||
Offset(
|
||||
x = size.width.times(bufferedProgress),
|
||||
y = yOffset,
|
||||
),
|
||||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
drawLine(
|
||||
color = color,
|
||||
start = Offset(x = 0f, y = yOffset),
|
||||
end =
|
||||
Offset(
|
||||
// x = size.width.times(if (isSelected) seekProgress else progress),
|
||||
x = size.width.times(progress),
|
||||
y = yOffset,
|
||||
),
|
||||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
if (isLtr) {
|
||||
drawLine(
|
||||
color = onSurface.copy(alpha = .65f),
|
||||
start = Offset(x = 0f, y = yOffset),
|
||||
end =
|
||||
Offset(
|
||||
x = size.width.times(bufferedProgress),
|
||||
y = yOffset,
|
||||
),
|
||||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
drawLine(
|
||||
color = color,
|
||||
start = Offset(x = 0f, y = yOffset),
|
||||
end =
|
||||
Offset(
|
||||
x = size.width.times(progress),
|
||||
y = yOffset,
|
||||
),
|
||||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
} else {
|
||||
drawLine(
|
||||
color = onSurface.copy(alpha = .65f),
|
||||
start =
|
||||
Offset(
|
||||
x = size.width - size.width.times(bufferedProgress),
|
||||
y = yOffset,
|
||||
),
|
||||
end =
|
||||
Offset(
|
||||
x = size.width,
|
||||
y = yOffset,
|
||||
),
|
||||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
drawLine(
|
||||
color = color,
|
||||
start = Offset(x = size.width - size.width.times(progress), y = yOffset),
|
||||
end =
|
||||
Offset(
|
||||
x = size.width,
|
||||
y = yOffset,
|
||||
),
|
||||
strokeWidth = size.height,
|
||||
cap = StrokeCap.Round,
|
||||
)
|
||||
}
|
||||
drawCircle(
|
||||
color = Color.White,
|
||||
radius = size.height + 2,
|
||||
center = Offset(x = size.width.times(progress), y = yOffset),
|
||||
center =
|
||||
Offset(
|
||||
x =
|
||||
if (isLtr) {
|
||||
size.width.times(progress)
|
||||
} else {
|
||||
size.width - size.width.times(progress)
|
||||
},
|
||||
y = yOffset,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue