mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
chore: Implement translation callbacks to flutter
This commit is contained in:
parent
e902e2034a
commit
29917bb59f
16 changed files with 681 additions and 43 deletions
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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,15 +137,22 @@ 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MediaSegmentType.toSegment: SegmentType
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
262
lib/src/translations_pigeon.g.dart
Normal file
262
lib/src/translations_pigeon.g.dart
Normal 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()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
30
pigeons/translations_pigeon.dart
Normal file
30
pigeons/translations_pigeon.dart
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue