From 156a6fe067babefa748548a01ccfc4762883ee2c Mon Sep 17 00:00:00 2001 From: beyond Date: Fri, 28 Aug 2026 06:13:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=98=E5=8C=96=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screens/VideoPlayerScreen.kt | 285 ++++++++++++------ .../myapplication/ui/design/LumaComponents.kt | 3 +- .../myapplication/ui/design/LumaIcons.kt | 2 + app/src/main/res/drawable/aurora_signal.xml | 35 +++ .../res/drawable/aurora_signal_selected.xml | 35 +++ app/src/main/res/drawable/aurora_wifi.xml | 31 ++ .../res/drawable/aurora_wifi_selected.xml | 31 ++ app/src/main/res/values-en/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 327 insertions(+), 97 deletions(-) create mode 100644 app/src/main/res/drawable/aurora_signal.xml create mode 100644 app/src/main/res/drawable/aurora_signal_selected.xml create mode 100644 app/src/main/res/drawable/aurora_wifi.xml create mode 100644 app/src/main/res/drawable/aurora_wifi_selected.xml diff --git a/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt b/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt index 0f8077b..e6555f2 100644 --- a/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt +++ b/app/src/main/kotlin/com/example/myapplication/screens/VideoPlayerScreen.kt @@ -12,12 +12,14 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.background +import androidx.compose.foundation.Canvas import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -40,6 +42,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -49,6 +52,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalContext @@ -59,6 +67,9 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner @@ -70,18 +81,11 @@ import androidx.media3.common.TrackSelectionOverride import androidx.media3.common.util.UnstableApi import android.net.ConnectivityManager import android.net.NetworkCapabilities -import android.os.BatteryManager import android.os.SystemClock -import androidx.compose.foundation.Canvas +import android.os.BatteryManager import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.awaitLongPressOrCancellation -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.input.pointer.pointerInput import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.analytics.AnalyticsListener @@ -118,6 +122,24 @@ private fun formatNetworkSpeed(bps: Long): String { else -> "$bps B/s" } } +private fun formatVideoCodec(mimeType: String?): String = when (mimeType) { + "video/avc" -> "H.264" + "video/hevc" -> "H.265" + "video/av01" -> "AV1" + "video/x-vnd.on2.vp9" -> "VP9" + "video/x-vnd.on2.vp8" -> "VP8" + "video/mp4v-es" -> "MPEG-4" + "video/mpeg2" -> "MPEG-2" + "video/3gpp" -> "H.263" + "video/x-ms-wmv" -> "WMV" + else -> mimeType?.substringAfterLast('/') ?: "" +} + +private fun formatBitrate(bps: Int): String = when { + bps >= 1_000_000 -> String.format("%.1f Mbps", bps / 1_000_000f) + bps >= 1_000 -> String.format("%.0f Kbps", bps / 1_000f) + else -> "$bps bps" +} private class SpeedMonitor { private var lastTotalBytes = 0L @@ -216,14 +238,18 @@ fun VideoPlayerScreen( var hasNetworkSample by remember { mutableStateOf(false) } var batteryPercent by remember { mutableIntStateOf(-1) } var isCharging by remember { mutableStateOf(false) } - var isWifi by remember { mutableStateOf(false) } var topTimeText by remember { mutableStateOf("") } + var isWifi by remember { mutableStateOf(false) } + var videoCodecText by remember { mutableStateOf("") } + var videoBitrateText by remember { mutableStateOf("") } + var videoFrameRateText by remember { mutableStateOf("") } var speedUpVisible by remember { mutableStateOf(false) } var speedUpValue by remember { mutableFloatStateOf(2f) } var controlsVisible by remember { mutableStateOf(true) } - LaunchedEffect(controlsVisible, isPlaying) { - if (controlsVisible && isPlaying) { + var controlsLocked by remember { mutableStateOf(false) } + LaunchedEffect(controlsVisible, isPlaying, controlsLocked) { + if (controlsVisible && isPlaying && !controlsLocked) { delay(5000) controlsVisible = false } @@ -241,18 +267,18 @@ fun VideoPlayerScreen( LaunchedEffect(Unit) { while (true) { - topTimeText = java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault()).format(java.util.Date()) - delay(1000) + val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager + isWifi = cm?.activeNetwork?.let { net -> + cm.getNetworkCapabilities(net)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true + } ?: false + delay(2000) } } LaunchedEffect(Unit) { while (true) { - val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager - isWifi = cm?.allNetworks?.any { cap -> - cm.getNetworkCapabilities(cap)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true - } ?: false - delay(2000) + topTimeText = java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault()).format(java.util.Date()) + delay(1000) } } @@ -271,6 +297,18 @@ fun VideoPlayerScreen( } } + LaunchedEffect(player) { + while (true) { + val vf = player.videoFormat + if (vf != null) { + videoCodecText = formatVideoCodec(vf.sampleMimeType) + videoBitrateText = formatBitrate(vf.bitrate) + videoFrameRateText = if (vf.frameRate > 0f) String.format("%.0f fps", vf.frameRate) else "" + } + delay(1000) + } + } + LaunchedEffect(Unit) { while (true) { val speed = speedMonitor.currentBytesPerSec() @@ -292,8 +330,19 @@ fun VideoPlayerScreen( DisposableEffect(Unit) { val activity = context.findActivity() val prev = activity?.requestedOrientation ?: ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + val window = activity?.window + val insetsController = window?.let { WindowCompat.getInsetsController(it, it.decorView) } + val prevBehavior = insetsController?.systemBarsBehavior + insetsController?.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + insetsController?.hide(WindowInsetsCompat.Type.systemBars()) activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE - onDispose { activity?.requestedOrientation = prev } + onDispose { + insetsController?.let { controller -> + controller.show(WindowInsetsCompat.Type.systemBars()) + controller.systemBarsBehavior = prevBehavior ?: WindowInsetsControllerCompat.BEHAVIOR_DEFAULT + } + activity?.requestedOrientation = prev + } } DisposableEffect(playbackUrl) { playerError = null @@ -367,7 +416,7 @@ fun VideoPlayerScreen( .background(Color.Black) .pointerInput(player) { awaitEachGesture { - val down = awaitFirstDown(requireUnconsumed = false) + val down = awaitFirstDown(requireUnconsumed = true) val longPress = awaitLongPressOrCancellation(down.id) if (longPress != null) { // 长按:2倍速播放,并显示倍速胶囊 @@ -435,7 +484,8 @@ fun VideoPlayerScreen( enter = fadeIn(), exit = fadeOut() ) { - Box(Modifier.fillMaxSize()) { + BoxWithConstraints(Modifier.fillMaxSize()) { + val capsuleInset = maxWidth * 0.075f + 20.dp Box( Modifier .fillMaxSize() @@ -459,7 +509,7 @@ fun VideoPlayerScreen( icon = LumaIcon.Replay, contentDescription = stringResource(Res.string.player_seek_backward), active = false, - size = 48.dp, + size = 32.dp, ) { val pos = player.currentPosition - 10_000L player.seekTo(maxOf(0L, pos)) @@ -469,7 +519,7 @@ fun VideoPlayerScreen( icon = LumaIcon.Previous, contentDescription = stringResource(Res.string.player_previous_episode), active = false, - size = 56.dp, + size = 40.dp, ) { val idx = episodes.indexOfFirst { it.id == currentEpisodeId } if (idx > 0) onSelectEpisode(episodes[idx - 1]) @@ -478,8 +528,8 @@ fun VideoPlayerScreen( icon = if (isPlaying) LumaIcon.Pause else LumaIcon.Play, contentDescription = stringResource(if (isPlaying) Res.string.player_pause else Res.string.player_play), active = true, - size = 72.dp, - iconSize = 40.dp, + size = 48.dp, + iconSize = 28.dp, ) { if (player.isPlaying) { player.pause() @@ -491,7 +541,7 @@ fun VideoPlayerScreen( icon = LumaIcon.Next, contentDescription = stringResource(Res.string.player_next_episode), active = false, - size = 56.dp, + size = 40.dp, ) { val idx = episodes.indexOfFirst { it.id == currentEpisodeId } if (idx >= 0 && idx < episodes.lastIndex) onSelectEpisode(episodes[idx + 1]) @@ -500,24 +550,25 @@ fun VideoPlayerScreen( icon = LumaIcon.Speed, contentDescription = stringResource(Res.string.player_seek_forward), active = false, - size = 48.dp, + size = 32.dp, ) { val pos = player.currentPosition + 10_000L player.seekTo(minOf(pos, player.duration)) } } - Column(Modifier.fillMaxWidth().statusBarsPadding().padding(16.dp)) { + Column(Modifier.fillMaxWidth().statusBarsPadding().padding(top = 16.dp)) { + // 第一排:当前时间(左)|网速 + 电量(右) Row( - Modifier.fillMaxWidth(), + Modifier.fillMaxWidth().padding(horizontal = capsuleInset), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Text(topTimeText, color = Color.White, fontSize = 13.sp) - Row(horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp)) { if (playbackUrl != null) { Row(verticalAlignment = Alignment.CenterVertically) { - LumaIconView(LumaIcon.Network, contentDescription = null, tint = Color.White, modifier = Modifier.size(16.dp)) + LumaIconView(if (isWifi) LumaIcon.Wifi else LumaIcon.Signal, contentDescription = null, tint = Color.White, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(4.dp)) if (hasNetworkSample) Text(formatNetworkSpeed(networkSpeed), color = Color.White, fontSize = 13.sp) } @@ -531,28 +582,32 @@ fun VideoPlayerScreen( } } } - Spacer(Modifier.height(10.dp)) + Spacer(Modifier.height(8.dp)) + // 第二排:关闭按钮(左)|Emby 艺术字(紧跟关闭按钮右侧)|字幕/剧集按钮(右) Row( - Modifier.fillMaxWidth(), + Modifier.fillMaxWidth().padding(horizontal = capsuleInset), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { - Column(Modifier.weight(1f).padding(end = 12.dp), verticalArrangement = Arrangement.Center) { - if (titleLogoUrl.isNotBlank()) { - AsyncImage( - model = titleLogoUrl, - contentDescription = null, - contentScale = ContentScale.Fit, - modifier = Modifier.height(28.dp).widthIn(max = 220.dp), - ) - } else { - Text(title, color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis) - } - if (serverName.isNotBlank()) { - Text(serverName, color = Color.White.copy(alpha = 0.8f), fontSize = 12.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) - } -} - Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { + LiquidSurfaceButton( + icon = LumaIcon.Close, + contentDescription = stringResource(Res.string.player_close), + active = false, + size = 40.dp, + ) { onClose() } + if (titleLogoUrl.isNotBlank()) { + AsyncImage( + model = titleLogoUrl, + contentDescription = null, + contentScale = ContentScale.Fit, + modifier = Modifier.height(28.dp).widthIn(max = 220.dp), + ) + } else { + Text(title, color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis) + } + } + Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { LiquidSurfaceButton( icon = LumaIcon.Subtitle, contentDescription = stringResource(Res.string.player_subtitle), @@ -571,6 +626,26 @@ fun VideoPlayerScreen( } } } + if (serverName.isNotBlank()) { + Spacer(Modifier.height(8.dp)) + // 第三排:服务器图标 + 服务器名字 + Row(Modifier.fillMaxWidth().padding(start = capsuleInset + 52.dp, end = capsuleInset), verticalAlignment = Alignment.CenterVertically) { + Box(Modifier.size(30.dp).clip(CircleShape), contentAlignment = Alignment.Center) { + if (serverIconUrl.isNotBlank()) { + AsyncImage( + model = serverIconUrl, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize(), + ) + } else { + LumaIconView(LumaIcon.Server, contentDescription = null, tint = Color.White, modifier = Modifier.size(24.dp)) + } + } + Spacer(Modifier.width(8.dp)) + Text(serverName, color = Color.White.copy(alpha = 0.8f), fontSize = 15.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) + } + } } if (playbackUrl != null) { @@ -578,56 +653,73 @@ fun VideoPlayerScreen( Modifier .align(Alignment.BottomCenter) .fillMaxWidth(0.85f) - .padding(bottom = 32.dp) - .lumaGlass( - backdrop = null, - radius = 24.dp, - level = LumaGlassLevel.Strong, - tint = Color.Black.copy(alpha = 0.40f), - borderColor = Color.White.copy(alpha = 0.16f), - shadowElevation = 4.dp, - ) - .padding(horizontal = 20.dp, vertical = 12.dp), + .padding(bottom = 32.dp), ) { - var totalDurationMs by remember { mutableLongStateOf(0L) } - var currentPositionMs by remember { mutableLongStateOf(0L) } - var isSeeking by remember { mutableStateOf(false) } - var seekPosition by remember { mutableFloatStateOf(0f) } - var bufferProgress by remember { mutableFloatStateOf(0f) } - - LaunchedEffect(player) { - while (true) { - if (player.duration > 0) { - totalDurationMs = player.duration - if (!isSeeking) { - currentPositionMs = player.currentPosition - progress = player.currentPosition.toFloat() / player.duration.toFloat() - val b = (player.bufferedPosition.toFloat() / player.duration).coerceIn(0f, 1f) - bufferProgress = maxOf(b, progress) - } - } - delay(250) - } + // 技术信息行(进度胶囊上方、胶囊外部):内核|格式|码率|帧率 + Row( + Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Text("ExoPlayer", color = Color.White.copy(alpha = 0.75f), fontSize = 12.sp) + if (videoCodecText.isNotBlank()) Text(videoCodecText, color = Color.White.copy(alpha = 0.75f), fontSize = 12.sp) + if (videoBitrateText.isNotBlank()) Text(videoBitrateText, color = Color.White.copy(alpha = 0.75f), fontSize = 12.sp) + if (videoFrameRateText.isNotBlank()) Text(videoFrameRateText, color = Color.White.copy(alpha = 0.75f), fontSize = 12.sp) } + Column( + Modifier + .fillMaxWidth() + .lumaGlass( + backdrop = null, + radius = 24.dp, + level = LumaGlassLevel.Strong, + tint = Color.Black.copy(alpha = 0.40f), + borderColor = Color.White.copy(alpha = 0.22f), + shadowElevation = 4.dp, + ) + .padding(horizontal = 16.dp, vertical = 6.dp), + ) { + var totalDurationMs by remember { mutableLongStateOf(0L) } + var currentPositionMs by remember { mutableLongStateOf(0L) } + var isSeeking by remember { mutableStateOf(false) } + var seekPosition by remember { mutableFloatStateOf(0f) } + var bufferProgress by remember { mutableFloatStateOf(0f) } - Row(verticalAlignment = Alignment.CenterVertically) { - Text(formatMs(currentPositionMs), color = Color.White, fontSize = 12.sp) - LumaSeekBar( - value = if (isSeeking) seekPosition else progress, - onValueChange = { v -> isSeeking = true; seekPosition = v }, - onValueChangeFinished = { - player.seekTo((seekPosition * player.duration).toLong()) - isSeeking = false - latestOnPlaybackProgress(player.currentPosition, !player.isPlaying, "TimeUpdate") - }, - modifier = Modifier.weight(1f).padding(horizontal = 12.dp), - activeColor = Color.White, - trackColor = Color.White.copy(alpha = 0.12f), - bufferColor = Color.White.copy(alpha = 0.28f), - bufferedValue = bufferProgress, - thumbColor = Color.White, - ) - Text(formatMs(totalDurationMs), color = Color.White, fontSize = 12.sp) + LaunchedEffect(player) { + while (true) { + if (player.duration > 0) { + totalDurationMs = player.duration + if (!isSeeking) { + currentPositionMs = player.currentPosition + progress = player.currentPosition.toFloat() / player.duration.toFloat() + val b = (player.bufferedPosition.toFloat() / player.duration).coerceIn(0f, 1f) + bufferProgress = maxOf(b, progress) + } + } + delay(250) + } + } + + Row(verticalAlignment = Alignment.CenterVertically) { + Text(formatMs(currentPositionMs), color = Color.White, fontSize = 12.sp) + LumaSeekBar( + value = if (isSeeking) seekPosition else progress, + onValueChange = { v -> isSeeking = true; seekPosition = v; controlsLocked = true }, + onValueChangeFinished = { + player.seekTo((seekPosition * player.duration).toLong()) + isSeeking = false + controlsLocked = false + latestOnPlaybackProgress(player.currentPosition, !player.isPlaying, "TimeUpdate") + }, + modifier = Modifier.weight(1f).padding(horizontal = 12.dp), + activeColor = Color.White, + trackColor = Color.White.copy(alpha = 0.12f), + bufferColor = Color.White.copy(alpha = 0.45f), + bufferedValue = bufferProgress, + thumbColor = Color.White, + ) + Text(formatMs(totalDurationMs), color = Color.White, fontSize = 12.sp) + } } } } @@ -750,6 +842,7 @@ private fun LiquidGlassPanel( ) { Column( Modifier + .padding(top = 16.dp, bottom = 16.dp, end = 16.dp) .fillMaxHeight() .width(320.dp) .lumaGlass( diff --git a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt index 127dab7..1f40161 100644 --- a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt +++ b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaComponents.kt @@ -176,7 +176,7 @@ fun LumaSeekBar( Canvas( modifier - .heightIn(min = 48.dp) + .heightIn(min = 30.dp) .onSizeChanged { trackWidthPx = it.width.toFloat() } .semantics { progressBarRangeInfo = ProgressBarRangeInfo(currentValue, 0f..1f) @@ -189,6 +189,7 @@ fun LumaSeekBar( .pointerInput(trackWidthPx) { awaitEachGesture { val down = awaitFirstDown(requireUnconsumed = false) + down.consume() dragging = true updateFromPosition(down.position.x) var lastX = down.position.x diff --git a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaIcons.kt b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaIcons.kt index 12aebe0..ffc0906 100644 --- a/app/src/main/kotlin/com/example/myapplication/ui/design/LumaIcons.kt +++ b/app/src/main/kotlin/com/example/myapplication/ui/design/LumaIcons.kt @@ -54,6 +54,8 @@ enum class LumaIcon( Database(R.drawable.aurora_database, R.drawable.aurora_database_selected), Cloud(R.drawable.aurora_cloud, R.drawable.aurora_cloud_selected), Network(R.drawable.aurora_network, R.drawable.aurora_network_selected), + Wifi(R.drawable.aurora_wifi, R.drawable.aurora_wifi_selected), + Signal(R.drawable.aurora_signal, R.drawable.aurora_signal_selected), Globe(R.drawable.aurora_globe, R.drawable.aurora_globe_selected), Shield(R.drawable.aurora_shield, R.drawable.aurora_shield_selected), Key(R.drawable.aurora_key, R.drawable.aurora_key_selected), diff --git a/app/src/main/res/drawable/aurora_signal.xml b/app/src/main/res/drawable/aurora_signal.xml new file mode 100644 index 0000000..5e34d5f --- /dev/null +++ b/app/src/main/res/drawable/aurora_signal.xml @@ -0,0 +1,35 @@ + + + + + + + diff --git a/app/src/main/res/drawable/aurora_signal_selected.xml b/app/src/main/res/drawable/aurora_signal_selected.xml new file mode 100644 index 0000000..e10001d --- /dev/null +++ b/app/src/main/res/drawable/aurora_signal_selected.xml @@ -0,0 +1,35 @@ + + + + + + + diff --git a/app/src/main/res/drawable/aurora_wifi.xml b/app/src/main/res/drawable/aurora_wifi.xml new file mode 100644 index 0000000..5d70b50 --- /dev/null +++ b/app/src/main/res/drawable/aurora_wifi.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/drawable/aurora_wifi_selected.xml b/app/src/main/res/drawable/aurora_wifi_selected.xml new file mode 100644 index 0000000..27ef341 --- /dev/null +++ b/app/src/main/res/drawable/aurora_wifi_selected.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 59d8f84..0303158 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -137,6 +137,7 @@ Playback failed Subtitles Episode list + Close Seek back 10 seconds Previous episode Play diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 80e41eb..e134918 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -137,6 +137,7 @@ 播放失败 字幕 剧集列表 + 关闭 后退 10 秒 上一集 播放