chore: Implement translation callbacks to flutter

This commit is contained in:
PartyDonut 2025-10-17 18:43:00 +02:00
parent e902e2034a
commit 29917bb59f
16 changed files with 681 additions and 43 deletions

View file

@ -3,6 +3,7 @@ package nl.jknaapen.fladder
import NativeVideoActivity
import PlayerSettingsPigeon
import StartResult
import TranslationsPigeon
import VideoPlayerApi
import VideoPlayerControlsCallback
import VideoPlayerListenerCallback
@ -12,6 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import com.ryanheise.audioservice.AudioServiceFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import nl.jknaapen.fladder.objects.PlayerSettingsObject
import nl.jknaapen.fladder.objects.TranslationsMessenger
import nl.jknaapen.fladder.objects.VideoPlayerObject
import nl.jknaapen.fladder.utility.leanBackEnabled
@ -37,6 +39,9 @@ class MainActivity : AudioServiceFragmentActivity(), NativeVideoActivity {
videoPlayerHost.videoPlayerControls =
VideoPlayerControlsCallback(flutterEngine.dartExecutor.binaryMessenger)
TranslationsMessenger.translation =
TranslationsPigeon(flutterEngine.dartExecutor.binaryMessenger)
PlayerSettingsPigeon.setUp(
flutterEngine.dartExecutor.binaryMessenger,
api = PlayerSettingsObject
@ -54,9 +59,9 @@ class MainActivity : AudioServiceFragmentActivity(), NativeVideoActivity {
StartResult(resultValue = "Cancelled")
}
callback?.invoke(Result.success(startResult))
VideoPlayerObject.implementation.player?.stop()
VideoPlayerObject.implementation.player?.release()
callback?.invoke(Result.success(startResult))
}
}

View file

@ -0,0 +1,217 @@
// Autogenerated from Pigeon (v26.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private object TranslationsPigeonPigeonUtils {
fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") }
}
private open class TranslationsPigeonPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return super.readValueOfType(type, buffer)
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
super.writeValue(stream, value)
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
class TranslationsPigeon(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
companion object {
/** The codec used by TranslationsPigeon. */
val codec: MessageCodec<Any?> by lazy {
TranslationsPigeonPigeonCodec()
}
}
fun next(callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.next$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
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 nextVideo(callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.nextVideo$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
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 close(callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.close$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
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 skip(nameArg: String, callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.skip$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(nameArg)) {
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 subtitles(callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.subtitles$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
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 off(callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.off$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
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 chapters(countArg: Long, callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.chapters$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(countArg)) {
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 nextUpInSeconds(secondsArg: Long, callback: (Result<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.nextUpInSeconds$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(secondsArg)) {
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<String>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.endsAt$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(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)))
}
}
}
}

View file

@ -66,6 +66,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceIn
import androidx.media3.exoplayer.ExoPlayer
import kotlinx.coroutines.delay
import nl.jknaapen.fladder.objects.Localized
import nl.jknaapen.fladder.objects.Translate
import nl.jknaapen.fladder.objects.VideoPlayerObject
import nl.jknaapen.fladder.utility.capitalize
import nl.jknaapen.fladder.utility.formatTime
@ -122,12 +124,14 @@ internal fun ProgressBar(
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Translate({ Localized.endsAt(endTimeString ?: "", it) }, endTimeString) { translation ->
val progressBarTopLabel = listOf(
playableData?.currentItem?.subTitle,
endTimeString,
translation,
)
val label = progressBarTopLabel.joinToString(separator = " - ")
val label = progressBarTopLabel.filterNot { it.isNullOrBlank() }
.joinToString(separator = " - ")
if (label.isNotBlank()) {
Text(
text = label,
@ -137,6 +141,7 @@ internal fun ProgressBar(
),
)
}
}
Spacer(modifier = Modifier.weight(1f))

View file

@ -34,7 +34,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
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.defaultSelected
import nl.jknaapen.fladder.utility.leanBackEnabled
@ -135,9 +137,15 @@ internal fun BoxScope.SegmentSkipOverlay(
}
}
}
activeSegment?.let {
activeSegment?.let { segment ->
Translate({ cb ->
Localized.skip(
segment.name.lowercase(),
cb
)
}) { translation ->
Text(
"Skip ${it.name.lowercase()}",
translation,
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
)
}
@ -145,6 +153,7 @@ internal fun BoxScope.SegmentSkipOverlay(
}
}
}
}
private val MediaSegmentType.toSegment: SegmentType
get() = when (this) {

View file

@ -17,6 +17,8 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import nl.jknaapen.fladder.objects.Localized
import nl.jknaapen.fladder.objects.Translate
import nl.jknaapen.fladder.objects.VideoPlayerObject
import nl.jknaapen.fladder.utility.clearAudioTrack
import nl.jknaapen.fladder.utility.setInternalAudioTrack
@ -82,7 +84,9 @@ fun AudioPicker(
},
selected = selectedOff
) {
Text("Off")
Translate(Localized::off) {
Text(it)
}
}
}

View file

@ -34,6 +34,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import nl.jknaapen.fladder.objects.Localized
import nl.jknaapen.fladder.objects.Translate
import nl.jknaapen.fladder.objects.VideoPlayerObject
import nl.jknaapen.fladder.utility.highlightOnFocus
@ -78,11 +80,14 @@ internal fun ChapterSelectionSheet(
.wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Translate({ Localized.chapters(chapters.size.toLong(), it) }) {
Text(
"Chapters",
it,
style = MaterialTheme.typography.titleLarge,
color = Color.White
)
}
LazyRow(
state = lazyListState,
horizontalArrangement = Arrangement.spacedBy(8.dp)

View file

@ -18,6 +18,8 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import nl.jknaapen.fladder.objects.Localized
import nl.jknaapen.fladder.objects.Translate
import nl.jknaapen.fladder.objects.VideoPlayerObject
import nl.jknaapen.fladder.utility.clearSubtitleTrack
import nl.jknaapen.fladder.utility.setInternalSubtitleTrack
@ -79,9 +81,9 @@ fun SubtitlePicker(
},
selected = selectedOff
) {
Text(
text = "Off",
)
Translate(Localized::off) {
Text(it)
}
}
}
internalSubTracks.forEachIndexed { index, subtitle ->

View file

@ -45,7 +45,9 @@ import io.github.rabehx.iconsax.filled.Next
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import nl.jknaapen.fladder.composables.controls.CustomButton
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.conditional
import nl.jknaapen.fladder.utility.highlightOnFocus
@ -168,14 +170,25 @@ internal fun NextUpOverlay(
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Translate(
{
Localized.nextUpInSeconds(
timeUntilNextVideo.toLong(),
callback = it
)
},
key = timeUntilNextVideo,
) {
Text(
"Next-up in $timeUntilNextVideo seconds",
it,
style = MaterialTheme.typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.onSurface
)
}
Box(
modifier = Modifier
.align(alignment = Alignment.CenterHorizontally)
@ -199,7 +212,9 @@ internal fun NextUpOverlay(
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("Next")
Translate(Localized::next) { value ->
Text(value)
}
Icon(Iconsax.Filled.Next, contentDescription = "Play next video")
}
}
@ -213,7 +228,9 @@ internal fun NextUpOverlay(
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("Close")
Translate(Localized::close) {
Text(it)
}
Icon(Iconsax.Filled.CloseCircle, contentDescription = "Close Icon")
}
}

View file

@ -0,0 +1,33 @@
package nl.jknaapen.fladder.objects
import TranslationsPigeon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
val Localized
get() = TranslationsMessenger.translation
internal object TranslationsMessenger {
lateinit var translation: TranslationsPigeon
}
@Composable
internal fun Translate(
callback: ((Result<String>) -> Unit) -> Unit,
key: Any? = Unit,
content: @Composable (String) -> Unit
) {
var value by remember { mutableStateOf<String?>(null) }
LaunchedEffect(key) {
callback { result ->
value = result.getOrNull()
}
}
content(value.orEmpty())
}

View file

@ -14,7 +14,6 @@ import nl.jknaapen.fladder.VideoPlayerActivity
import nl.jknaapen.fladder.messengers.VideoPlayerImplementation
import nl.jknaapen.fladder.utility.InternalTrack
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import kotlin.time.Clock
import kotlin.time.ExperimentalTime
import kotlin.time.toJavaInstant
@ -43,9 +42,8 @@ object VideoPlayerObject {
val remainingMs = (dur - pos).coerceAtLeast(0L)
val endInstant = startInstant.toJavaInstant().plusMillis(remainingMs)
val endZoned = endInstant.atZone(zone)
val formatter = DateTimeFormatter.ofPattern("hh:mm a")
"ends at ${endZoned.format(formatter)}"
endZoned.toOffsetDateTime().toString()
}
val currentSubtitleTrackIndex =

View file

@ -1348,5 +1348,13 @@
"mediaTunnelingTitle": "Media tunneling",
"mediaTunnelingDesc": "Enable media tunneling for native player",
"clientSettingsUseSystemIMETitle": "Use system keyboard",
"clientSettingsUseSystemIMEDesc": "Use the built-in keyboard provided by your system"
"clientSettingsUseSystemIMEDesc": "Use the built-in keyboard provided by your system",
"nextUpInCount": "Next-up in {seconds}",
"@nextUpInCount": {
"placeholders": {
"seconds": {
"type": "int"
}
}
}
}

View file

@ -0,0 +1,262 @@
// Autogenerated from Pigeon (v26.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';
List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
if (empty) {
return <Object?>[];
}
if (error == null) {
return <Object?>[result];
}
return <Object?>[error.code, error.message, error.details];
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is int) {
buffer.putUint8(4);
buffer.putInt64(value);
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
default:
return super.readValueOfType(type, buffer);
}
}
}
abstract class TranslationsPigeon {
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
String next();
String nextVideo();
String close();
String skip(String name);
String subtitles();
String off();
String chapters(int count);
String nextUpInSeconds(int seconds);
String endsAt(String time);
static void setUp(TranslationsPigeon? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.next$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
final String output = api.next();
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.nextVideo$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
final String output = api.nextVideo();
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.close$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
final String output = api.close();
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.skip$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.skip was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_name = (args[0] as String?);
assert(arg_name != null,
'Argument for dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.skip was null, expected non-null String.');
try {
final String output = api.skip(arg_name!);
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.subtitles$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
final String output = api.subtitles();
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.off$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
try {
final String output = api.off();
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.chapters$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.chapters was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_count = (args[0] as int?);
assert(arg_count != null,
'Argument for dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.chapters was null, expected non-null int.');
try {
final String output = api.chapters(arg_count!);
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.nextUpInSeconds$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.nextUpInSeconds was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_seconds = (args[0] as int?);
assert(arg_seconds != null,
'Argument for dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.nextUpInSeconds was null, expected non-null int.');
try {
final String output = api.nextUpInSeconds(arg_seconds!);
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<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.endsAt$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.endsAt was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_time = (args[0] as String?);
assert(arg_time != null,
'Argument for dev.flutter.pigeon.nl_jknaapen_fladder.settings.TranslationsPigeon.endsAt was null, expected non-null String.');
try {
final String output = api.endsAt(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()));
}
});
}
}
}
}

View file

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -100,6 +102,7 @@ Future<void> _playVideo(
}
if (context.mounted) {
log("Finished refreshing");
await context.refreshData();
}

View file

@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/l10n/generated/app_localizations.dart';
import 'package:fladder/providers/sync/background_download_provider.dart';
import 'package:fladder/src/translations_pigeon.g.dart' as messenger;
///Only use for base translations, under normal circumstances ALWAYS use the widgets provided context
final localizationContextProvider = StateProvider<BuildContext?>((ref) => null);
@ -26,6 +27,7 @@ class LocalizationContextWrapper extends ConsumerStatefulWidget {
}
class _LocalizationContextWrapperState extends ConsumerState<LocalizationContextWrapper> {
_TranslationsMessgener? _messenger;
@override
void initState() {
super.initState();
@ -41,6 +43,11 @@ class _LocalizationContextWrapperState extends ConsumerState<LocalizationContext
}
void updateLanguageContext() {
if (_messenger == null) {
_messenger = _TranslationsMessgener(context: context);
messenger.TranslationsPigeon.setUp(_messenger);
}
WidgetsBinding.instance.addPostFrameCallback((value) {
ref.read(localizationContextProvider.notifier).update((cb) => context);
ref.read(backgroundDownloaderProvider.notifier).updateTranslations(context);
@ -58,3 +65,36 @@ extension LocaleDisplayCodeExtension on Locale {
: languageCode.toUpperCase();
}
}
class _TranslationsMessgener extends messenger.TranslationsPigeon {
_TranslationsMessgener({required this.context});
final BuildContext context;
@override
String chapters(int count) => context.localized.chapter(count);
@override
String close() => context.localized.close;
@override
String endsAt(String time) => context.localized.endsAt(DateTime.parse(time));
@override
String next() => context.localized.nextVideo;
@override
String nextUpInSeconds(int seconds) => context.localized.nextUpInCount(seconds);
@override
String nextVideo() => context.localized.nextVideo;
@override
String off() => context.localized.off;
@override
String skip(String name) => context.localized.skipButtonLabel(name);
@override
String subtitles() => context.localized.subtitles;
}

View file

@ -34,9 +34,9 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
Future<void> loadVideo(String url, bool play) async => player.open(url, play);
@override
Future<void> open(BuildContext newContext) async {
Future<StartResult> open(BuildContext newContext) async {
nativeActivityStarted = true;
NativeVideoActivity().launchActivity();
return NativeVideoActivity().launchActivity();
}
@override

View file

@ -0,0 +1,30 @@
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/src/translations_pigeon.g.dart',
dartOptions: DartOptions(),
kotlinOut: 'android/app/src/main/kotlin/nl/jknaapen/fladder/api/TranslationsPigeon.g.kt',
kotlinOptions: KotlinOptions(
includeErrorClass: false,
),
dartPackageName: 'nl_jknaapen_fladder.settings',
),
)
@FlutterApi()
abstract class TranslationsPigeon {
String next();
String nextVideo();
String close();
String skip(String name);
String subtitles();
String off();
String chapters(int count);
String nextUpInSeconds(int seconds);
String endsAt(String time);
}