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 NativeVideoActivity
|
||||||
import PlayerSettingsPigeon
|
import PlayerSettingsPigeon
|
||||||
import StartResult
|
import StartResult
|
||||||
|
import TranslationsPigeon
|
||||||
import VideoPlayerApi
|
import VideoPlayerApi
|
||||||
import VideoPlayerControlsCallback
|
import VideoPlayerControlsCallback
|
||||||
import VideoPlayerListenerCallback
|
import VideoPlayerListenerCallback
|
||||||
|
|
@ -12,6 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import com.ryanheise.audioservice.AudioServiceFragmentActivity
|
import com.ryanheise.audioservice.AudioServiceFragmentActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import nl.jknaapen.fladder.objects.PlayerSettingsObject
|
import nl.jknaapen.fladder.objects.PlayerSettingsObject
|
||||||
|
import nl.jknaapen.fladder.objects.TranslationsMessenger
|
||||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.leanBackEnabled
|
import nl.jknaapen.fladder.utility.leanBackEnabled
|
||||||
|
|
||||||
|
|
@ -37,6 +39,9 @@ class MainActivity : AudioServiceFragmentActivity(), NativeVideoActivity {
|
||||||
videoPlayerHost.videoPlayerControls =
|
videoPlayerHost.videoPlayerControls =
|
||||||
VideoPlayerControlsCallback(flutterEngine.dartExecutor.binaryMessenger)
|
VideoPlayerControlsCallback(flutterEngine.dartExecutor.binaryMessenger)
|
||||||
|
|
||||||
|
TranslationsMessenger.translation =
|
||||||
|
TranslationsPigeon(flutterEngine.dartExecutor.binaryMessenger)
|
||||||
|
|
||||||
PlayerSettingsPigeon.setUp(
|
PlayerSettingsPigeon.setUp(
|
||||||
flutterEngine.dartExecutor.binaryMessenger,
|
flutterEngine.dartExecutor.binaryMessenger,
|
||||||
api = PlayerSettingsObject
|
api = PlayerSettingsObject
|
||||||
|
|
@ -54,9 +59,9 @@ class MainActivity : AudioServiceFragmentActivity(), NativeVideoActivity {
|
||||||
StartResult(resultValue = "Cancelled")
|
StartResult(resultValue = "Cancelled")
|
||||||
}
|
}
|
||||||
|
|
||||||
callback?.invoke(Result.success(startResult))
|
|
||||||
VideoPlayerObject.implementation.player?.stop()
|
VideoPlayerObject.implementation.player?.stop()
|
||||||
VideoPlayerObject.implementation.player?.release()
|
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.compose.ui.util.fastCoerceIn
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import kotlinx.coroutines.delay
|
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.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.capitalize
|
import nl.jknaapen.fladder.utility.capitalize
|
||||||
import nl.jknaapen.fladder.utility.formatTime
|
import nl.jknaapen.fladder.utility.formatTime
|
||||||
|
|
@ -122,12 +124,14 @@ internal fun ProgressBar(
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
|
Translate({ Localized.endsAt(endTimeString ?: "", it) }, endTimeString) { translation ->
|
||||||
val progressBarTopLabel = listOf(
|
val progressBarTopLabel = listOf(
|
||||||
playableData?.currentItem?.subTitle,
|
playableData?.currentItem?.subTitle,
|
||||||
endTimeString,
|
translation,
|
||||||
)
|
)
|
||||||
|
|
||||||
val label = progressBarTopLabel.joinToString(separator = " - ")
|
val label = progressBarTopLabel.filterNot { it.isNullOrBlank() }
|
||||||
|
.joinToString(separator = " - ")
|
||||||
if (label.isNotBlank()) {
|
if (label.isNotBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = label,
|
||||||
|
|
@ -137,6 +141,7 @@ internal fun ProgressBar(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
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.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import nl.jknaapen.fladder.objects.Localized
|
||||||
import nl.jknaapen.fladder.objects.PlayerSettingsObject
|
import nl.jknaapen.fladder.objects.PlayerSettingsObject
|
||||||
|
import nl.jknaapen.fladder.objects.Translate
|
||||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.defaultSelected
|
import nl.jknaapen.fladder.utility.defaultSelected
|
||||||
import nl.jknaapen.fladder.utility.leanBackEnabled
|
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(
|
Text(
|
||||||
"Skip ${it.name.lowercase()}",
|
translation,
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -145,6 +153,7 @@ internal fun BoxScope.SegmentSkipOverlay(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val MediaSegmentType.toSegment: SegmentType
|
private val MediaSegmentType.toSegment: SegmentType
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
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.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.clearAudioTrack
|
import nl.jknaapen.fladder.utility.clearAudioTrack
|
||||||
import nl.jknaapen.fladder.utility.setInternalAudioTrack
|
import nl.jknaapen.fladder.utility.setInternalAudioTrack
|
||||||
|
|
@ -82,7 +84,9 @@ fun AudioPicker(
|
||||||
},
|
},
|
||||||
selected = selectedOff
|
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.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil3.compose.AsyncImage
|
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.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.highlightOnFocus
|
import nl.jknaapen.fladder.utility.highlightOnFocus
|
||||||
|
|
||||||
|
|
@ -78,11 +80,14 @@ internal fun ChapterSelectionSheet(
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
|
Translate({ Localized.chapters(chapters.size.toLong(), it) }) {
|
||||||
Text(
|
Text(
|
||||||
"Chapters",
|
it,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = Color.White
|
color = Color.White
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LazyRow(
|
LazyRow(
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
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.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.clearSubtitleTrack
|
import nl.jknaapen.fladder.utility.clearSubtitleTrack
|
||||||
import nl.jknaapen.fladder.utility.setInternalSubtitleTrack
|
import nl.jknaapen.fladder.utility.setInternalSubtitleTrack
|
||||||
|
|
@ -79,9 +81,9 @@ fun SubtitlePicker(
|
||||||
},
|
},
|
||||||
selected = selectedOff
|
selected = selectedOff
|
||||||
) {
|
) {
|
||||||
Text(
|
Translate(Localized::off) {
|
||||||
text = "Off",
|
Text(it)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internalSubTracks.forEachIndexed { index, subtitle ->
|
internalSubTracks.forEachIndexed { index, subtitle ->
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,9 @@ import io.github.rabehx.iconsax.filled.Next
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import nl.jknaapen.fladder.composables.controls.CustomButton
|
import nl.jknaapen.fladder.composables.controls.CustomButton
|
||||||
|
import nl.jknaapen.fladder.objects.Localized
|
||||||
import nl.jknaapen.fladder.objects.PlayerSettingsObject
|
import nl.jknaapen.fladder.objects.PlayerSettingsObject
|
||||||
|
import nl.jknaapen.fladder.objects.Translate
|
||||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||||
import nl.jknaapen.fladder.utility.conditional
|
import nl.jknaapen.fladder.utility.conditional
|
||||||
import nl.jknaapen.fladder.utility.highlightOnFocus
|
import nl.jknaapen.fladder.utility.highlightOnFocus
|
||||||
|
|
@ -168,14 +170,25 @@ internal fun NextUpOverlay(
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
Translate(
|
||||||
|
{
|
||||||
|
Localized.nextUpInSeconds(
|
||||||
|
timeUntilNextVideo.toLong(),
|
||||||
|
callback = it
|
||||||
|
)
|
||||||
|
},
|
||||||
|
key = timeUntilNextVideo,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
"Next-up in $timeUntilNextVideo seconds",
|
it,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(alignment = Alignment.CenterHorizontally)
|
.align(alignment = Alignment.CenterHorizontally)
|
||||||
|
|
@ -199,7 +212,9 @@ internal fun NextUpOverlay(
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
Text("Next")
|
Translate(Localized::next) { value ->
|
||||||
|
Text(value)
|
||||||
|
}
|
||||||
Icon(Iconsax.Filled.Next, contentDescription = "Play next video")
|
Icon(Iconsax.Filled.Next, contentDescription = "Play next video")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +228,9 @@ internal fun NextUpOverlay(
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
Text("Close")
|
Translate(Localized::close) {
|
||||||
|
Text(it)
|
||||||
|
}
|
||||||
Icon(Iconsax.Filled.CloseCircle, contentDescription = "Close Icon")
|
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.messengers.VideoPlayerImplementation
|
||||||
import nl.jknaapen.fladder.utility.InternalTrack
|
import nl.jknaapen.fladder.utility.InternalTrack
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.toJavaInstant
|
import kotlin.time.toJavaInstant
|
||||||
|
|
@ -43,9 +42,8 @@ object VideoPlayerObject {
|
||||||
val remainingMs = (dur - pos).coerceAtLeast(0L)
|
val remainingMs = (dur - pos).coerceAtLeast(0L)
|
||||||
val endInstant = startInstant.toJavaInstant().plusMillis(remainingMs)
|
val endInstant = startInstant.toJavaInstant().plusMillis(remainingMs)
|
||||||
val endZoned = endInstant.atZone(zone)
|
val endZoned = endInstant.atZone(zone)
|
||||||
val formatter = DateTimeFormatter.ofPattern("hh:mm a")
|
|
||||||
|
|
||||||
"ends at ${endZoned.format(formatter)}"
|
endZoned.toOffsetDateTime().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentSubtitleTrackIndex =
|
val currentSubtitleTrackIndex =
|
||||||
|
|
|
||||||
|
|
@ -1348,5 +1348,13 @@
|
||||||
"mediaTunnelingTitle": "Media tunneling",
|
"mediaTunnelingTitle": "Media tunneling",
|
||||||
"mediaTunnelingDesc": "Enable media tunneling for native player",
|
"mediaTunnelingDesc": "Enable media tunneling for native player",
|
||||||
"clientSettingsUseSystemIMETitle": "Use system keyboard",
|
"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/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
@ -100,6 +102,7 @@ Future<void> _playVideo(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
log("Finished refreshing");
|
||||||
await context.refreshData();
|
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/l10n/generated/app_localizations.dart';
|
||||||
import 'package:fladder/providers/sync/background_download_provider.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
|
///Only use for base translations, under normal circumstances ALWAYS use the widgets provided context
|
||||||
final localizationContextProvider = StateProvider<BuildContext?>((ref) => null);
|
final localizationContextProvider = StateProvider<BuildContext?>((ref) => null);
|
||||||
|
|
@ -26,6 +27,7 @@ class LocalizationContextWrapper extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LocalizationContextWrapperState extends ConsumerState<LocalizationContextWrapper> {
|
class _LocalizationContextWrapperState extends ConsumerState<LocalizationContextWrapper> {
|
||||||
|
_TranslationsMessgener? _messenger;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -41,6 +43,11 @@ class _LocalizationContextWrapperState extends ConsumerState<LocalizationContext
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateLanguageContext() {
|
void updateLanguageContext() {
|
||||||
|
if (_messenger == null) {
|
||||||
|
_messenger = _TranslationsMessgener(context: context);
|
||||||
|
messenger.TranslationsPigeon.setUp(_messenger);
|
||||||
|
}
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((value) {
|
WidgetsBinding.instance.addPostFrameCallback((value) {
|
||||||
ref.read(localizationContextProvider.notifier).update((cb) => context);
|
ref.read(localizationContextProvider.notifier).update((cb) => context);
|
||||||
ref.read(backgroundDownloaderProvider.notifier).updateTranslations(context);
|
ref.read(backgroundDownloaderProvider.notifier).updateTranslations(context);
|
||||||
|
|
@ -58,3 +65,36 @@ extension LocaleDisplayCodeExtension on Locale {
|
||||||
: languageCode.toUpperCase();
|
: 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);
|
Future<void> loadVideo(String url, bool play) async => player.open(url, play);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> open(BuildContext newContext) async {
|
Future<StartResult> open(BuildContext newContext) async {
|
||||||
nativeActivityStarted = true;
|
nativeActivityStarted = true;
|
||||||
NativeVideoActivity().launchActivity();
|
return NativeVideoActivity().launchActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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