From d2b8a855f3e7e10d027ef76293ea4ce6dacc1923 Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sat, 18 Oct 2025 21:15:35 +0200 Subject: [PATCH] fix: Properly localized dates in native player --- .../fladder/api/TranslationsPigeon.g.kt | 20 +++++++++++ .../controls/VideoPlayerControls.kt | 36 +++++++++++++------ .../fladder/objects/VideoPlayerObject.kt | 6 ++-- lib/l10n/app_en.arb | 10 ++++++ lib/src/translations_pigeon.g.dart | 27 ++++++++++++++ lib/util/localization_helper.dart | 5 ++- pigeons/translations_pigeon.dart | 1 + 7 files changed, 90 insertions(+), 15 deletions(-) diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/api/TranslationsPigeon.g.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/api/TranslationsPigeon.g.kt index 84a8b42..ce673c2 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/api/TranslationsPigeon.g.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/api/TranslationsPigeon.g.kt @@ -194,6 +194,26 @@ class TranslationsPigeon(private val binaryMessenger: BinaryMessenger, private v } } } + fun hoursAndMinutes(timeArg: String, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.hoursAndMinutes$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(timeArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else if (it[0] == null) { + callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", ""))) + } else { + val output = it[0] as String + callback(Result.success(output)) + } + } else { + callback(Result.failure(TranslationsPigeonPigeonUtils.createConnectionError(channelName))) + } + } + } fun endsAt(timeArg: String, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt index bbe9146..f344e48 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt @@ -74,14 +74,15 @@ import kotlinx.coroutines.delay import nl.jknaapen.fladder.composables.dialogs.AudioPicker import nl.jknaapen.fladder.composables.dialogs.ChapterSelectionSheet import nl.jknaapen.fladder.composables.dialogs.SubtitlePicker +import nl.jknaapen.fladder.objects.Localized import nl.jknaapen.fladder.objects.PlayerSettingsObject +import nl.jknaapen.fladder.objects.Translate import nl.jknaapen.fladder.objects.VideoPlayerObject import nl.jknaapen.fladder.utility.ImmersiveSystemBars import nl.jknaapen.fladder.utility.defaultSelected import nl.jknaapen.fladder.utility.leanBackEnabled import nl.jknaapen.fladder.utility.visible import java.time.ZoneId -import java.time.format.DateTimeFormatter import kotlin.time.Clock import kotlin.time.Duration.Companion.seconds import kotlin.time.ExperimentalTime @@ -522,21 +523,34 @@ internal fun RowScope.RightButtons( } } - @RequiresApi(Build.VERSION_CODES.O) @kotlin.OptIn(ExperimentalTime::class) @Composable private fun CurrentTime() { - val startInstant = Clock.System.now() val zone = ZoneId.systemDefault() - val endInstant = startInstant.toJavaInstant() - val endZoned = endInstant.atZone(zone) - val formatter = DateTimeFormatter.ofPattern("hh:mm a") + var currentTime by remember { mutableStateOf(Clock.System.now()) } - Text( - endZoned.format(formatter), - style = MaterialTheme.typography.titleMedium, - color = Color.White - ) + LaunchedEffect(Unit) { + while (true) { + currentTime = Clock.System.now() + val delayMs = 60_000L - (currentTime.toEpochMilliseconds() % 60_000L) + delay(delayMs) + } + } + + val endZoned = currentTime.toJavaInstant().atZone(zone) + + Translate( + { + Localized.hoursAndMinutes(endZoned.toOffsetDateTime().toString(), it) + }, + key = currentTime, + ) { time -> + Text( + text = time, + style = MaterialTheme.typography.titleMedium, + color = Color.White + ) + } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt index fa75063..8a14e39 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt @@ -36,11 +36,11 @@ object VideoPlayerObject { @RequiresApi(Build.VERSION_CODES.O) @OptIn(ExperimentalTime::class) val endTime = combine(position, duration) { pos, dur -> - val startInstant = Clock.System.now() + val now = Clock.System.now().toJavaInstant() val zone = ZoneId.systemDefault() - val remainingMs = (dur - pos).coerceAtLeast(0L) - val endInstant = startInstant.toJavaInstant().plusMillis(remainingMs) + val endInstant = now.plusMillis(remainingMs) + val endZoned = endInstant.atZone(zone) endZoned.toOffsetDateTime().toString() diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 97e6285..2363c77 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1356,5 +1356,15 @@ "type": "int" } } + }, + "formattedTime": "{time}", + "@formattedTime": { + "description": "Formatted time", + "placeholders": { + "time": { + "type": "DateTime", + "format": "jm" + } + } } } \ No newline at end of file diff --git a/lib/src/translations_pigeon.g.dart b/lib/src/translations_pigeon.g.dart index f75bd4e..67077d9 100644 --- a/lib/src/translations_pigeon.g.dart +++ b/lib/src/translations_pigeon.g.dart @@ -59,6 +59,8 @@ abstract class TranslationsPigeon { String nextUpInSeconds(int seconds); + String hoursAndMinutes(String time); + String endsAt(String time); static void setUp(TranslationsPigeon? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { @@ -233,6 +235,31 @@ abstract class TranslationsPigeon { }); } } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.hoursAndMinutes$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.hoursAndMinutes was null.'); + final List args = (message as List?)!; + final String? arg_time = (args[0] as String?); + assert(arg_time != null, + 'Argument for dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.hoursAndMinutes was null, expected non-null String.'); + try { + final String output = api.hoursAndMinutes(arg_time!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.endsAt$messageChannelSuffix', pigeonChannelCodec, diff --git a/lib/util/localization_helper.dart b/lib/util/localization_helper.dart index e3b876b..83ef458 100644 --- a/lib/util/localization_helper.dart +++ b/lib/util/localization_helper.dart @@ -78,7 +78,7 @@ class _TranslationsMessgener extends messenger.TranslationsPigeon { String close() => context.localized.close; @override - String endsAt(String time) => context.localized.endsAt(DateTime.parse(time)); + String endsAt(String time) => context.localized.endsAt(DateTime.parse(time).toLocal()); @override String next() => context.localized.nextVideo; @@ -97,4 +97,7 @@ class _TranslationsMessgener extends messenger.TranslationsPigeon { @override String subtitles() => context.localized.subtitles; + + @override + String hoursAndMinutes(String time) => context.localized.formattedTime(DateTime.parse(time).toLocal()); } diff --git a/pigeons/translations_pigeon.dart b/pigeons/translations_pigeon.dart index 2e74c79..e33f867 100644 --- a/pigeons/translations_pigeon.dart +++ b/pigeons/translations_pigeon.dart @@ -25,6 +25,7 @@ abstract class TranslationsPigeon { String chapters(int count); String nextUpInSeconds(int seconds); + String hoursAndMinutes(String time); String endsAt(String time); }