From 9e3178c07e8325422cb49395a09860488a9a9f60 Mon Sep 17 00:00:00 2001 From: SkyD666 Date: Sun, 18 Aug 2024 20:17:41 +0800 Subject: [PATCH] [feature|optimize|fix] Supports setting the max cache size and max back cache size of the player; Search, Article screen support show empty tips; fix long title on media library screen; optimize code --- app/build.gradle.kts | 2 +- .../java/com/skyd/anivu/ext/CollectionExt.kt | 7 +- .../main/java/com/skyd/anivu/ext/NumberExt.kt | 2 +- .../java/com/skyd/anivu/ext/PreferenceExt.kt | 4 + .../skyd/anivu/model/preference/Settings.kt | 8 ++ .../PlayerMaxBackCacheSizePreference.kt | 26 ++++ .../player/PlayerMaxCacheSizePreference.kt | 26 ++++ .../repository/feed/ReorderGroupRepository.kt | 17 ++- .../anivu/ui/component/AnimatedPlaceholder.kt | 42 ------ .../skyd/anivu/ui/component/Placeholder.kt | 108 ++++++++++++++++ .../anivu/ui/component/dialog/SliderDialog.kt | 3 + .../com/skyd/anivu/ui/local/LocalValue.kt | 4 + .../java/com/skyd/anivu/ui/mpv/MPVView.kt | 17 ++- .../anivu/ui/mpv/controller/bar/TopBar.kt | 3 +- .../anivu/ui/screen/article/ArticleScreen.kt | 67 +++++----- .../ui/screen/download/DownloadScreen.kt | 91 +++++++------ .../skyd/anivu/ui/screen/media/MediaScreen.kt | 4 +- .../anivu/ui/screen/media/list/MediaList.kt | 40 ++---- .../ui/screen/media/sub/SubMediaScreen.kt | 9 +- .../anivu/ui/screen/search/SearchScreen.kt | 64 +++++---- .../playerconfig/PlayerConfigScreen.kt | 121 +++++++++++++++++- app/src/main/res/values-zh-rCN/strings.xml | 3 + app/src/main/res/values/strings.xml | 3 + 23 files changed, 491 insertions(+), 180 deletions(-) create mode 100644 app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxBackCacheSizePreference.kt create mode 100644 app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxCacheSizePreference.kt delete mode 100644 app/src/main/java/com/skyd/anivu/ui/component/AnimatedPlaceholder.kt create mode 100644 app/src/main/java/com/skyd/anivu/ui/component/Placeholder.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6cc46dc5..e1c054a5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { minSdk = 24 targetSdk = 35 versionCode = 22 - versionName = "2.1-alpha15" + versionName = "2.1-alpha16" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/skyd/anivu/ext/CollectionExt.kt b/app/src/main/java/com/skyd/anivu/ext/CollectionExt.kt index 62ca4aa3..dadd8e8b 100644 --- a/app/src/main/java/com/skyd/anivu/ext/CollectionExt.kt +++ b/app/src/main/java/com/skyd/anivu/ext/CollectionExt.kt @@ -11,4 +11,9 @@ fun snapshotStateMapSaver() = Saver, Any>( @Suppress("UNCHECKED_CAST") (value as? List>)?.toMutableStateMap() ?: mutableStateMapOf() } -) \ No newline at end of file +) + +fun calculateHashMapInitialCapacity( + initialCapacity: Int, + loadFactor: Float = 0.75f, +): Int = (initialCapacity / loadFactor).toInt() + 1 \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ext/NumberExt.kt b/app/src/main/java/com/skyd/anivu/ext/NumberExt.kt index fb82c3b1..64e65cb9 100644 --- a/app/src/main/java/com/skyd/anivu/ext/NumberExt.kt +++ b/app/src/main/java/com/skyd/anivu/ext/NumberExt.kt @@ -6,7 +6,7 @@ import android.text.format.Formatter import android.util.TypedValue fun Long.fileSize(context: Context): String = - Formatter.formatShortFileSize(context, this) + Formatter.formatFileSize(context, this) val Float.dp: Float get() = TypedValue.applyDimension( diff --git a/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt b/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt index f3354f73..a0ab0d71 100644 --- a/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt +++ b/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt @@ -34,6 +34,8 @@ import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference import com.skyd.anivu.model.preference.player.HardwareDecodePreference import com.skyd.anivu.model.preference.player.PlayerAutoPipPreference import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference +import com.skyd.anivu.model.preference.player.PlayerMaxBackCacheSizePreference +import com.skyd.anivu.model.preference.player.PlayerMaxCacheSizePreference import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference import com.skyd.anivu.model.preference.player.PlayerShowScreenshotButtonPreference import com.skyd.anivu.model.preference.proxy.ProxyHostnamePreference @@ -97,6 +99,8 @@ fun Preferences.toSettings(): Settings { playerShowScreenshotButton = PlayerShowScreenshotButtonPreference.fromPreferences(this), hardwareDecode = HardwareDecodePreference.fromPreferences(this), playerAutoPip = PlayerAutoPipPreference.fromPreferences(this), + playerMaxCacheSize = PlayerMaxCacheSizePreference.fromPreferences(this), + playerMaxBackCacheSize = PlayerMaxBackCacheSizePreference.fromPreferences(this), // Data useAutoDelete = UseAutoDeletePreference.fromPreferences(this), diff --git a/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt b/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt index ae73d6de..846798b7 100644 --- a/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt +++ b/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt @@ -39,6 +39,8 @@ import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference import com.skyd.anivu.model.preference.player.HardwareDecodePreference import com.skyd.anivu.model.preference.player.PlayerAutoPipPreference import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference +import com.skyd.anivu.model.preference.player.PlayerMaxBackCacheSizePreference +import com.skyd.anivu.model.preference.player.PlayerMaxCacheSizePreference import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference import com.skyd.anivu.model.preference.player.PlayerShowScreenshotButtonPreference import com.skyd.anivu.model.preference.proxy.ProxyHostnamePreference @@ -79,6 +81,8 @@ import com.skyd.anivu.ui.local.LocalParseLinkTagAsEnclosure import com.skyd.anivu.ui.local.LocalPickImageMethod import com.skyd.anivu.ui.local.LocalPlayerAutoPip import com.skyd.anivu.ui.local.LocalPlayerDoubleTap +import com.skyd.anivu.ui.local.LocalPlayerMaxBackCacheSize +import com.skyd.anivu.ui.local.LocalPlayerMaxCacheSize import com.skyd.anivu.ui.local.LocalPlayerShow85sButton import com.skyd.anivu.ui.local.LocalPlayerShowScreenshotButton import com.skyd.anivu.ui.local.LocalProxyHostname @@ -144,6 +148,8 @@ data class Settings( val playerShowScreenshotButton: Boolean = PlayerShowScreenshotButtonPreference.default, val hardwareDecode: Boolean = HardwareDecodePreference.default, val playerAutoPip: Boolean = PlayerAutoPipPreference.default, + val playerMaxCacheSize: Long = PlayerMaxCacheSizePreference.default, + val playerMaxBackCacheSize: Long = PlayerMaxBackCacheSizePreference.default, // Data val useAutoDelete: Boolean = UseAutoDeletePreference.default, val autoDeleteArticleFrequency: Long = AutoDeleteArticleFrequencyPreference.default, @@ -209,6 +215,8 @@ fun SettingsProvider( LocalPlayerShowScreenshotButton provides settings.playerShowScreenshotButton, LocalHardwareDecode provides settings.hardwareDecode, LocalPlayerAutoPip provides settings.playerAutoPip, + LocalPlayerMaxCacheSize provides settings.playerMaxCacheSize, + LocalPlayerMaxBackCacheSize provides settings.playerMaxBackCacheSize, // Data LocalUseAutoDelete provides settings.useAutoDelete, LocalAutoDeleteArticleFrequency provides settings.autoDeleteArticleFrequency, diff --git a/app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxBackCacheSizePreference.kt b/app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxBackCacheSizePreference.kt new file mode 100644 index 00000000..ee11fd84 --- /dev/null +++ b/app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxBackCacheSizePreference.kt @@ -0,0 +1,26 @@ +package com.skyd.anivu.model.preference.player + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.longPreferencesKey +import com.skyd.anivu.base.BasePreference +import com.skyd.anivu.ext.dataStore +import com.skyd.anivu.ext.put +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object PlayerMaxBackCacheSizePreference : BasePreference { + private const val PLAYER_MAX_CACHE_SIZE = "playerMaxBackCacheSize" + override val default = 20L * 1024 * 1024 // 20 MB + + val key = longPreferencesKey(PLAYER_MAX_CACHE_SIZE) + + fun put(context: Context, scope: CoroutineScope, value: Long) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(key, value) + } + } + + override fun fromPreferences(preferences: Preferences): Long = preferences[key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxCacheSizePreference.kt b/app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxCacheSizePreference.kt new file mode 100644 index 00000000..e890b977 --- /dev/null +++ b/app/src/main/java/com/skyd/anivu/model/preference/player/PlayerMaxCacheSizePreference.kt @@ -0,0 +1,26 @@ +package com.skyd.anivu.model.preference.player + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.longPreferencesKey +import com.skyd.anivu.base.BasePreference +import com.skyd.anivu.ext.dataStore +import com.skyd.anivu.ext.put +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object PlayerMaxCacheSizePreference : BasePreference { + private const val PLAYER_MAX_CACHE_SIZE = "playerMaxCacheSize" + override val default = 10L * 1024 * 1024 // 10 MB + + val key = longPreferencesKey(PLAYER_MAX_CACHE_SIZE) + + fun put(context: Context, scope: CoroutineScope, value: Long) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(key, value) + } + } + + override fun fromPreferences(preferences: Preferences): Long = preferences[key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/model/repository/feed/ReorderGroupRepository.kt b/app/src/main/java/com/skyd/anivu/model/repository/feed/ReorderGroupRepository.kt index d626d222..65933966 100644 --- a/app/src/main/java/com/skyd/anivu/model/repository/feed/ReorderGroupRepository.kt +++ b/app/src/main/java/com/skyd/anivu/model/repository/feed/ReorderGroupRepository.kt @@ -1,6 +1,7 @@ package com.skyd.anivu.model.repository.feed import com.skyd.anivu.base.BaseRepository +import com.skyd.anivu.ext.calculateHashMapInitialCapacity import com.skyd.anivu.model.bean.GroupBean import com.skyd.anivu.model.bean.GroupVo import com.skyd.anivu.model.bean.GroupWithFeedBean @@ -17,7 +18,9 @@ class ReorderGroupRepository @Inject constructor( ) : BaseRepository() { private fun sortGroups(groupList: List): List { val groupsMap = groupList.associateBy { it.groupId } - val hasPreviousGroups = mutableSetOf() + val hasPreviousGroups = LinkedHashSet( + calculateHashMapInitialCapacity(groupList.size) + ) groupList.forEach { group -> val nextGroupId = group.nextGroupId if (nextGroupId != null) { @@ -25,7 +28,9 @@ class ReorderGroupRepository @Inject constructor( } } val noPreviousGroups = (groupList - hasPreviousGroups).sortedBy { it.name } - val sortedList = mutableSetOf() + val sortedList = LinkedHashSet( + calculateHashMapInitialCapacity(groupList.size) + ) noPreviousGroups.forEach { group -> var currentGroup: GroupBean? = group var currentGroupId: String? = group.groupId @@ -44,7 +49,9 @@ class ReorderGroupRepository @Inject constructor( groupList: List ): List { val groupsMap = groupList.associateBy { it.group.groupId } - val hasPreviousGroups = mutableSetOf() + val hasPreviousGroups = LinkedHashSet( + calculateHashMapInitialCapacity(groupList.size) + ) groupList.forEach { group -> val nextGroupId = group.group.nextGroupId if (nextGroupId != null) { @@ -52,7 +59,9 @@ class ReorderGroupRepository @Inject constructor( } } val noPreviousGroups = (groupList - hasPreviousGroups).sortedBy { it.group.name } - val sortedList = mutableSetOf() + val sortedList = LinkedHashSet( + calculateHashMapInitialCapacity(groupList.size) + ) noPreviousGroups.forEach { group -> var currentGroup: GroupWithFeedBean? = group var currentGroupId: String? = group.group.groupId diff --git a/app/src/main/java/com/skyd/anivu/ui/component/AnimatedPlaceholder.kt b/app/src/main/java/com/skyd/anivu/ui/component/AnimatedPlaceholder.kt deleted file mode 100644 index 30108f0a..00000000 --- a/app/src/main/java/com/skyd/anivu/ui/component/AnimatedPlaceholder.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.skyd.anivu.ui.component - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp - -@Composable -fun AnimatedPlaceholder( - modifier: Modifier = Modifier, - @androidx.annotation.RawRes resId: Int, - tip: String, -) { - Box( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()), contentAlignment = Alignment.Center - ) { - Column( - modifier = modifier, - horizontalAlignment = Alignment.CenterHorizontally - ) { - AniVuLottieAnimation(resId = resId) - Text( - modifier = Modifier.padding(top = 10.dp), - text = tip, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onBackground, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/component/Placeholder.kt b/app/src/main/java/com/skyd/anivu/ui/component/Placeholder.kt new file mode 100644 index 00000000..e3ddb1bb --- /dev/null +++ b/app/src/main/java/com/skyd/anivu/ui/component/Placeholder.kt @@ -0,0 +1,108 @@ +package com.skyd.anivu.ui.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.sizeIn +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.skyd.anivu.R + +@Composable +fun EmptyPlaceholder( + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(), +) { + AnimatedPlaceholder( + modifier = modifier, + contentPadding = contentPadding, + resId = R.raw.lottie_empty_1, + tip = stringResource(id = R.string.empty_tip_1), + ) +} + +@Composable +fun AnimatedPlaceholder( + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(), + @androidx.annotation.RawRes resId: Int, + tip: String, +) { + WithTextPlaceholder( + modifier = modifier, + contentPadding = contentPadding, + text = tip, + ) { + AniVuLottieAnimation(resId = resId) + } +} + +@Composable +fun WithTextPlaceholder( + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(), + text: String, + content: @Composable BoxScope.() -> Unit, +) { + BasePlaceholder( + modifier = modifier, + contentPadding = contentPadding, + ) { + Column( + modifier = Modifier.padding(vertical = 20.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Box( + modifier = Modifier.sizeIn(maxHeight = 240.dp, maxWidth = 220.dp), + contentAlignment = Alignment.Center, + content = content, + ) + Text( + modifier = Modifier.padding(top = 10.dp), + text = text, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onBackground, + ) + } + } +} + +@Composable +fun CircularProgressPlaceholder( + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(), +) { + BasePlaceholder( + modifier = modifier, + contentPadding = contentPadding, + ) { + CircularProgressIndicator() + } +} + +@Composable +fun BasePlaceholder( + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(), + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = Modifier + .fillMaxSize() + .then(modifier) + .padding(contentPadding), + contentAlignment = Alignment.Center, + content = content, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/component/dialog/SliderDialog.kt b/app/src/main/java/com/skyd/anivu/ui/component/dialog/SliderDialog.kt index 01d775ee..5b2e5748 100644 --- a/app/src/main/java/com/skyd/anivu/ui/component/dialog/SliderDialog.kt +++ b/app/src/main/java/com/skyd/anivu/ui/component/dialog/SliderDialog.kt @@ -1,5 +1,6 @@ package com.skyd.anivu.ui.component.dialog +import androidx.annotation.IntRange import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -18,6 +19,7 @@ fun SliderDialog( value: Float, onValueChange: (Float) -> Unit, valueRange: ClosedFloatingPointRange = 0f..1f, + @IntRange(from = 0) steps: Int = 0, valueLabel: @Composable (() -> Unit)? = null, icon: @Composable (() -> Unit)? = { Icon( @@ -43,6 +45,7 @@ fun SliderDialog( value = value, onValueChange = onValueChange, valueRange = valueRange, + steps = steps, ) } }, diff --git a/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt b/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt index c9e2aee7..a52cf422 100644 --- a/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt +++ b/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt @@ -35,6 +35,8 @@ import com.skyd.anivu.model.preference.data.medialib.MediaLibLocationPreference import com.skyd.anivu.model.preference.player.HardwareDecodePreference import com.skyd.anivu.model.preference.player.PlayerAutoPipPreference import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference +import com.skyd.anivu.model.preference.player.PlayerMaxBackCacheSizePreference +import com.skyd.anivu.model.preference.player.PlayerMaxCacheSizePreference import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference import com.skyd.anivu.model.preference.player.PlayerShowScreenshotButtonPreference import com.skyd.anivu.model.preference.proxy.ProxyHostnamePreference @@ -112,6 +114,8 @@ val LocalPlayerShowScreenshotButton = compositionLocalOf { PlayerShowScreenshotButtonPreference.default } val LocalHardwareDecode = compositionLocalOf { HardwareDecodePreference.default } val LocalPlayerAutoPip = compositionLocalOf { PlayerAutoPipPreference.default } +val LocalPlayerMaxCacheSize = compositionLocalOf { PlayerMaxCacheSizePreference.default } +val LocalPlayerMaxBackCacheSize = compositionLocalOf { PlayerMaxBackCacheSizePreference.default } // Data val LocalUseAutoDelete = compositionLocalOf { UseAutoDeletePreference.default } diff --git a/app/src/main/java/com/skyd/anivu/ui/mpv/MPVView.kt b/app/src/main/java/com/skyd/anivu/ui/mpv/MPVView.kt index 249bd069..c040d343 100644 --- a/app/src/main/java/com/skyd/anivu/ui/mpv/MPVView.kt +++ b/app/src/main/java/com/skyd/anivu/ui/mpv/MPVView.kt @@ -1,7 +1,6 @@ package com.skyd.anivu.ui.mpv import android.content.Context -import android.os.Build import android.util.AttributeSet import android.util.Log import android.view.KeyCharacterMap @@ -13,6 +12,8 @@ import com.skyd.anivu.config.Const import com.skyd.anivu.ext.dataStore import com.skyd.anivu.ext.getOrDefault import com.skyd.anivu.model.preference.player.HardwareDecodePreference +import com.skyd.anivu.model.preference.player.PlayerMaxBackCacheSizePreference +import com.skyd.anivu.model.preference.player.PlayerMaxCacheSizePreference import com.skyd.anivu.ui.mpv.controller.bar.toDurationString import `is`.xyz.mpv.MPVLib import `is`.xyz.mpv.MPVLib.mpvFormat.MPV_FORMAT_DOUBLE @@ -89,6 +90,7 @@ class MPVView(context: Context, attrs: AttributeSet?) : SurfaceView(context, att val disp = wm.defaultDisplay val refreshRate = disp.mode.refreshRate + val dataStore = context.dataStore Log.v(TAG, "Display ${disp.displayId} reports FPS of $refreshRate") MPVLib.setOptionString("display-fps-override", refreshRate.toString()) MPVLib.setOptionString("video-sync", "audio") @@ -98,14 +100,19 @@ class MPVView(context: Context, attrs: AttributeSet?) : SurfaceView(context, att MPVLib.setOptionString("opengl-es", "yes") MPVLib.setOptionString( "hwdec", - if (context.dataStore.getOrDefault(HardwareDecodePreference)) "auto" else "no" + if (dataStore.getOrDefault(HardwareDecodePreference)) "auto" else "no" ) MPVLib.setOptionString("ao", "audiotrack,opensles") MPVLib.setOptionString("input-default-bindings", "yes") // Limit demuxer cache since the defaults are too high for mobile devices - val cacheMegs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) 64 else 32 - MPVLib.setOptionString("demuxer-max-bytes", "${cacheMegs * 1024 * 1024}") - MPVLib.setOptionString("demuxer-max-back-bytes", "${cacheMegs * 1024 * 1024}") + MPVLib.setOptionString( + "demuxer-max-bytes", + dataStore.getOrDefault(PlayerMaxCacheSizePreference).toString(), + ) + MPVLib.setOptionString( + "demuxer-max-back-bytes", + dataStore.getOrDefault(PlayerMaxBackCacheSizePreference).toString(), + ) MPVLib.setOptionString("screenshot-directory", Const.PICTURES_DIR.path) } diff --git a/app/src/main/java/com/skyd/anivu/ui/mpv/controller/bar/TopBar.kt b/app/src/main/java/com/skyd/anivu/ui/mpv/controller/bar/TopBar.kt index 35923b84..010e4a9b 100644 --- a/app/src/main/java/com/skyd/anivu/ui/mpv/controller/bar/TopBar.kt +++ b/app/src/main/java/com/skyd/anivu/ui/mpv/controller/bar/TopBar.kt @@ -73,14 +73,13 @@ internal fun TopBar( Text( modifier = Modifier .weight(1f) - .basicMarquee(), + .basicMarquee(iterations = Int.MAX_VALUE), text = title, style = MaterialTheme.typography.titleMedium, color = Color.White, maxLines = 1, ) Spacer(modifier = Modifier.width(3.dp)) - Spacer(modifier = Modifier.weight(1f)) BarIconButton( modifier = Modifier.padding(2.dp), onClick = topBarCallback.onPictureInPicture, diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/article/ArticleScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/article/ArticleScreen.kt index 3902c705..351be0c5 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/article/ArticleScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/article/ArticleScreen.kt @@ -58,7 +58,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import androidx.navigation.NavOptions -import androidx.paging.PagingData import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.skyd.anivu.R @@ -73,6 +72,8 @@ import com.skyd.anivu.ui.component.AniVuFloatingActionButton import com.skyd.anivu.ui.component.AniVuIconButton import com.skyd.anivu.ui.component.AniVuTopBar import com.skyd.anivu.ui.component.BackIcon +import com.skyd.anivu.ui.component.CircularProgressPlaceholder +import com.skyd.anivu.ui.component.EmptyPlaceholder import com.skyd.anivu.ui.component.dialog.AniVuDialog import com.skyd.anivu.ui.component.dialog.WaitingDialog import com.skyd.anivu.ui.component.lazyverticalgrid.AniVuLazyVerticalGrid @@ -86,7 +87,6 @@ import com.skyd.anivu.ui.local.LocalShowArticlePullRefresh import com.skyd.anivu.ui.local.LocalShowArticleTopBarRefresh import com.skyd.anivu.ui.screen.search.SearchDomain import com.skyd.anivu.ui.screen.search.openSearchScreen -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch @@ -325,21 +325,24 @@ private fun Content( HorizontalDivider() } } - val articleListState = uiState.articleListState - ArticleList( - modifier = Modifier.nestedScroll(nestedScrollConnection), - articles = ((articleListState as? ArticleListState.Success) - ?.articlePagingDataFlow - ?: flowOf(PagingData.empty())).collectAsLazyPagingItems(), - listState = listState, - onFavorite = onFavorite, - onRead = onRead, - contentPadding = PaddingValues( - start = contentPadding.calculateStartPadding(LocalLayoutDirection.current), - end = contentPadding.calculateEndPadding(LocalLayoutDirection.current), - bottom = contentPadding.calculateBottomPadding(), - ) + PaddingValues(vertical = 4.dp), - ) + + val currentContentPadding = PaddingValues( + start = contentPadding.calculateStartPadding(LocalLayoutDirection.current), + end = contentPadding.calculateEndPadding(LocalLayoutDirection.current), + bottom = contentPadding.calculateBottomPadding(), + ) + PaddingValues(vertical = 4.dp) + when (val articleListState = uiState.articleListState) { + is ArticleListState.Failed -> Unit + is ArticleListState.Init -> CircularProgressPlaceholder(contentPadding = currentContentPadding) + is ArticleListState.Success -> ArticleList( + modifier = Modifier.nestedScroll(nestedScrollConnection), + articles = articleListState.articlePagingDataFlow.collectAsLazyPagingItems(), + listState = listState, + onFavorite = onFavorite, + onRead = onRead, + contentPadding = currentContentPadding, + ) + } } if (LocalShowArticlePullRefresh.current) { @@ -361,18 +364,22 @@ private fun ArticleList( onRead: (ArticleWithFeed, Boolean) -> Unit, contentPadding: PaddingValues, ) { - val adapter = remember { - LazyGridAdapter(mutableListOf(Article1Proxy(onFavorite = onFavorite, onRead = onRead))) + if (articles.itemCount > 0) { + val adapter = remember { + LazyGridAdapter(mutableListOf(Article1Proxy(onFavorite = onFavorite, onRead = onRead))) + } + AniVuLazyVerticalGrid( + modifier = modifier.fillMaxSize(), + columns = GridCells.Adaptive(LocalArticleItemMinWidth.current.dp), + dataList = articles, + listState = listState, + adapter = adapter, + contentPadding = contentPadding + PaddingValues(horizontal = 12.dp, vertical = 6.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + key = { _, item -> (item as ArticleWithFeed).articleWithEnclosure.article.articleId }, + ) + } else { + EmptyPlaceholder(contentPadding = contentPadding) } - AniVuLazyVerticalGrid( - modifier = modifier.fillMaxSize(), - columns = GridCells.Adaptive(LocalArticleItemMinWidth.current.dp), - dataList = articles, - listState = listState, - adapter = adapter, - contentPadding = contentPadding + PaddingValues(horizontal = 12.dp, vertical = 6.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), - horizontalArrangement = Arrangement.spacedBy(12.dp), - key = { _, item -> (item as ArticleWithFeed).articleWithEnclosure.article.articleId }, - ) } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/download/DownloadScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/download/DownloadScreen.kt index fb5ce6fb..db18cf1c 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/download/DownloadScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/download/DownloadScreen.kt @@ -33,11 +33,14 @@ import com.skyd.anivu.base.mvi.getDispatcher import com.skyd.anivu.ext.navigate import com.skyd.anivu.ext.plus import com.skyd.anivu.ext.showSnackbar +import com.skyd.anivu.model.bean.download.DownloadInfoBean import com.skyd.anivu.model.worker.download.DownloadTorrentWorker import com.skyd.anivu.model.worker.download.doIfMagnetOrTorrentLink import com.skyd.anivu.ui.component.AniVuFloatingActionButton import com.skyd.anivu.ui.component.AniVuTopBar import com.skyd.anivu.ui.component.AniVuTopBarStyle +import com.skyd.anivu.ui.component.CircularProgressPlaceholder +import com.skyd.anivu.ui.component.EmptyPlaceholder import com.skyd.anivu.ui.component.deeplink.DeepLinkData import com.skyd.anivu.ui.component.dialog.TextFieldDialog @@ -95,46 +98,14 @@ fun DownloadScreen(downloadLink: String? = null, viewModel: DownloadViewModel = } ) { paddingValues -> when (val downloadListState = uiState.downloadListState) { - is DownloadListState.Failed, + is DownloadListState.Failed -> Unit DownloadListState.Init, - DownloadListState.Loading -> Unit + DownloadListState.Loading -> CircularProgressPlaceholder(contentPadding = paddingValues) - is DownloadListState.Success -> { - LazyColumn( - contentPadding = paddingValues + PaddingValues(bottom = fabHeight + 16.dp), - ) { - itemsIndexed( - items = downloadListState.downloadInfoBeanList, - key = { _, item -> item.link }, - ) { index, item -> - if (index > 0) HorizontalDivider() - DownloadItem( - data = item, - onPause = { - DownloadTorrentWorker.pause( - context = context, - requestId = it.downloadRequestId, - link = it.link, - ) - }, - onResume = { video -> - DownloadTorrentWorker.startWorker( - context = context, - torrentLink = video.link, - requestId = video.downloadRequestId, - ) - }, - onCancel = { video -> - DownloadTorrentWorker.cancel( - context = context, - requestId = video.downloadRequestId, - link = video.link, - ) - }, - ) - } - } - } + is DownloadListState.Success -> DownloadList( + downloadInfoBeanList = downloadListState.downloadInfoBeanList, + contentPadding = paddingValues + PaddingValues(bottom = fabHeight + 16.dp) + ) } } @@ -161,4 +132,48 @@ fun DownloadScreen(downloadLink: String? = null, viewModel: DownloadViewModel = ) }, ) +} + +@Composable +private fun DownloadList( + downloadInfoBeanList: List, + contentPadding: PaddingValues, +) { + if (downloadInfoBeanList.isNotEmpty()) { + val context = LocalContext.current + LazyColumn(contentPadding = contentPadding) { + itemsIndexed( + items = downloadInfoBeanList, + key = { _, item -> item.link }, + ) { index, item -> + if (index > 0) HorizontalDivider() + DownloadItem( + data = item, + onPause = { + DownloadTorrentWorker.pause( + context = context, + requestId = it.downloadRequestId, + link = it.link, + ) + }, + onResume = { video -> + DownloadTorrentWorker.startWorker( + context = context, + torrentLink = video.link, + requestId = video.downloadRequestId, + ) + }, + onCancel = { video -> + DownloadTorrentWorker.cancel( + context = context, + requestId = video.downloadRequestId, + link = video.link, + ) + }, + ) + } + } + } else { + EmptyPlaceholder(contentPadding = contentPadding) + } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/media/MediaScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/media/MediaScreen.kt index 6dcd9db3..9cc15115 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/media/MediaScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/media/MediaScreen.kt @@ -208,7 +208,7 @@ fun MediaScreen(path: String, viewModel: MediaViewModel = hiltViewModel()) { Text( modifier = Modifier .widthIn(max = 220.dp) - .basicMarquee(), + .basicMarquee(iterations = Int.MAX_VALUE), text = group.first.name, maxLines = 1, ) @@ -221,7 +221,7 @@ fun MediaScreen(path: String, viewModel: MediaViewModel = hiltViewModel()) { HorizontalPager(state = pagerState) { index -> MediaList( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - contentPadding = PaddingValues(bottom = fabHeight), + fabPadding = PaddingValues(bottom = fabHeight), path = path, groupInfo = GroupInfo( group = uiState.groups[index].first, diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/media/list/MediaList.kt b/app/src/main/java/com/skyd/anivu/ui/screen/media/list/MediaList.kt index 9776857e..5dc20284 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/media/list/MediaList.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/media/list/MediaList.kt @@ -6,10 +6,11 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState @@ -26,11 +27,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.skyd.anivu.R import com.skyd.anivu.base.mvi.getDispatcher import com.skyd.anivu.ext.activity import com.skyd.anivu.ext.plus @@ -39,10 +38,11 @@ import com.skyd.anivu.ext.toUri import com.skyd.anivu.model.bean.MediaGroupBean import com.skyd.anivu.model.bean.VideoBean import com.skyd.anivu.ui.activity.PlayActivity -import com.skyd.anivu.ui.component.AnimatedPlaceholder +import com.skyd.anivu.ui.component.CircularProgressPlaceholder +import com.skyd.anivu.ui.component.EmptyPlaceholder +import com.skyd.anivu.ui.local.LocalNavController import com.skyd.anivu.ui.screen.media.CreateGroupDialog import com.skyd.anivu.ui.screen.media.sub.openSubMediaScreen -import com.skyd.anivu.ui.local.LocalNavController class GroupInfo( val group: MediaGroupBean, @@ -55,6 +55,7 @@ class GroupInfo( internal fun MediaList( modifier: Modifier = Modifier, contentPadding: PaddingValues = PaddingValues(), + fabPadding: PaddingValues = PaddingValues(), path: String, groupInfo: GroupInfo? = null, viewModel: MediaListViewModel = hiltViewModel(key = path + groupInfo?.group) @@ -83,22 +84,20 @@ internal fun MediaList( val state = rememberPullRefreshState( refreshing = uiState.listState.loading, onRefresh = { - dispatch( - MediaListIntent.Refresh( - path = path, - group = groupInfo?.group - ) - ) + dispatch(MediaListIntent.Refresh(path = path, group = groupInfo?.group)) }, ) Box(modifier = Modifier.pullRefresh(state)) { when (val listState = uiState.listState) { - is ListState.Failed, - is ListState.Init -> Unit + is ListState.Failed -> Unit + is ListState.Init -> CircularProgressPlaceholder(contentPadding = innerPadding + contentPadding) is ListState.Success -> { if (listState.list.isEmpty()) { - EmptyPlaceholder() + EmptyPlaceholder( + modifier = Modifier.verticalScroll(rememberScrollState()), + contentPadding = innerPadding + contentPadding + ) } else { MediaList( modifier = modifier, @@ -128,7 +127,7 @@ internal fun MediaList( refreshing = uiState.listState.loading, state = state, modifier = Modifier - .padding(contentPadding) + .padding(contentPadding + fabPadding) .align(Alignment.TopCenter), ) } @@ -149,17 +148,6 @@ internal fun MediaList( } } -@Composable -private fun EmptyPlaceholder() { - AnimatedPlaceholder( - modifier = Modifier - .width(220.dp) - .padding(vertical = 20.dp), - resId = R.raw.lottie_empty_1, - tip = stringResource(id = R.string.empty_tip_1), - ) -} - @Composable private fun MediaList( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/media/sub/SubMediaScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/media/sub/SubMediaScreen.kt index 2c81a32a..0f4a9bb8 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/media/sub/SubMediaScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/media/sub/SubMediaScreen.kt @@ -1,5 +1,6 @@ package com.skyd.anivu.ui.screen.media.sub +import androidx.compose.foundation.basicMarquee import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.Icon @@ -72,7 +73,13 @@ private fun SubMediaScreen(path: String) { AniVuTopBar( style = AniVuTopBarStyle.Large, scrollBehavior = scrollBehavior, - title = { Text(text = path.substringAfterLast("/")) }, + title = { + Text( + modifier = Modifier.basicMarquee(iterations = Int.MAX_VALUE), + text = path.substringAfterLast("/"), + maxLines = 1, + ) + }, ) } ) { paddingValues -> diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/search/SearchScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/search/SearchScreen.kt index 554b917e..06401533 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/search/SearchScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/search/SearchScreen.kt @@ -21,12 +21,13 @@ import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowUpward import androidx.compose.material.icons.outlined.Clear -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.LocalAbsoluteTonalElevation @@ -74,6 +75,8 @@ import com.skyd.anivu.model.bean.FeedViewBean import com.skyd.anivu.ui.component.AniVuFloatingActionButton import com.skyd.anivu.ui.component.AniVuIconButton import com.skyd.anivu.ui.component.BackIcon +import com.skyd.anivu.ui.component.CircularProgressPlaceholder +import com.skyd.anivu.ui.component.EmptyPlaceholder import com.skyd.anivu.ui.component.dialog.WaitingDialog import com.skyd.anivu.ui.component.lazyverticalgrid.AniVuLazyVerticalGrid import com.skyd.anivu.ui.component.lazyverticalgrid.adapter.LazyGridAdapter @@ -192,6 +195,7 @@ fun SearchScreen( searchFieldValueState = TextFieldValue( text = "", selection = TextRange(0) ) + dispatch(SearchIntent.UpdateQuery(searchFieldValueState.text)) } } ) @@ -206,32 +210,40 @@ fun SearchScreen( ) { innerPaddings -> when (val searchResultState = uiState.searchResultState) { is SearchResultState.Failed -> Unit - SearchResultState.Init -> CircularProgressIndicator() - SearchResultState.Loading -> CircularProgressIndicator() - is SearchResultState.Success -> SearchResultList( - result = searchResultState.result.collectAsLazyPagingItems(), - listState = searchResultListState, - onFavorite = { articleWithFeed, favorite -> - dispatch( - SearchIntent.Favorite( - articleId = articleWithFeed.articleWithEnclosure.article.articleId, - favorite = favorite, - ) - ) - }, - onRead = { articleWithFeed, read -> - dispatch( - SearchIntent.Read( - articleId = articleWithFeed.articleWithEnclosure.article.articleId, - read = read, - ) + SearchResultState.Init, + SearchResultState.Loading -> CircularProgressPlaceholder(contentPadding = innerPaddings) + + is SearchResultState.Success -> { + val result = searchResultState.result.collectAsLazyPagingItems() + if (result.itemCount > 0) { + SearchResultList( + result = result, + listState = searchResultListState, + onFavorite = { articleWithFeed, favorite -> + dispatch( + SearchIntent.Favorite( + articleId = articleWithFeed.articleWithEnclosure.article.articleId, + favorite = favorite, + ) + ) + }, + onRead = { articleWithFeed, read -> + dispatch( + SearchIntent.Read( + articleId = articleWithFeed.articleWithEnclosure.article.articleId, + read = read, + ) + ) + }, + contentPadding = innerPaddings + PaddingValues( + top = 4.dp, + bottom = 4.dp + fabHeight, + ), ) - }, - contentPadding = innerPaddings + PaddingValues( - top = 4.dp, - bottom = 4.dp + fabHeight, - ), - ) + } else { + EmptyPlaceholder(contentPadding = innerPaddings) + } + } } WaitingDialog(visible = uiState.loadingDialog) diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/settings/playerconfig/PlayerConfigScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/settings/playerconfig/PlayerConfigScreen.kt index e0eebfa3..c7f67532 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/settings/playerconfig/PlayerConfigScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/settings/playerconfig/PlayerConfigScreen.kt @@ -1,44 +1,64 @@ package com.skyd.anivu.ui.screen.settings.playerconfig +import android.os.Build +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowLeft +import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowRight import androidx.compose.material.icons.outlined.FastForward import androidx.compose.material.icons.outlined.PhotoCamera import androidx.compose.material.icons.outlined.PictureInPictureAlt +import androidx.compose.material.icons.outlined.Restore +import androidx.compose.material.icons.outlined.Save import androidx.compose.material.icons.outlined.TouchApp +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.skyd.anivu.R +import com.skyd.anivu.ext.fileSize import com.skyd.anivu.model.preference.player.PlayerAutoPipPreference import com.skyd.anivu.model.preference.player.PlayerDoubleTapPreference +import com.skyd.anivu.model.preference.player.PlayerMaxBackCacheSizePreference +import com.skyd.anivu.model.preference.player.PlayerMaxCacheSizePreference import com.skyd.anivu.model.preference.player.PlayerShow85sButtonPreference import com.skyd.anivu.model.preference.player.PlayerShowScreenshotButtonPreference +import com.skyd.anivu.ui.component.AniVuIconButton import com.skyd.anivu.ui.component.AniVuTopBar import com.skyd.anivu.ui.component.AniVuTopBarStyle import com.skyd.anivu.ui.component.BaseSettingsItem import com.skyd.anivu.ui.component.CategorySettingsItem import com.skyd.anivu.ui.component.CheckableListMenu import com.skyd.anivu.ui.component.SwitchSettingsItem -import com.skyd.anivu.ui.screen.settings.playerconfig.advanced.PLAYER_CONFIG_ADVANCED_SCREEN_ROUTE +import com.skyd.anivu.ui.component.dialog.SliderDialog import com.skyd.anivu.ui.local.LocalNavController import com.skyd.anivu.ui.local.LocalPlayerAutoPip import com.skyd.anivu.ui.local.LocalPlayerDoubleTap +import com.skyd.anivu.ui.local.LocalPlayerMaxBackCacheSize +import com.skyd.anivu.ui.local.LocalPlayerMaxCacheSize import com.skyd.anivu.ui.local.LocalPlayerShow85sButton import com.skyd.anivu.ui.local.LocalPlayerShowScreenshotButton +import com.skyd.anivu.ui.screen.settings.playerconfig.advanced.PLAYER_CONFIG_ADVANCED_SCREEN_ROUTE const val PLAYER_CONFIG_SCREEN_ROUTE = "playerConfigScreen" @@ -50,6 +70,8 @@ fun PlayerConfigScreen() { val scope = rememberCoroutineScope() val navController = LocalNavController.current var expandDoubleTapMenu by rememberSaveable { mutableStateOf(false) } + var openMaxCacheSizeDialog by rememberSaveable { mutableStateOf(false) } + var openMaxBackCacheSizeDialog by rememberSaveable { mutableStateOf(false) } Scaffold( topBar = { @@ -133,6 +155,25 @@ fun PlayerConfigScreen() { } ) } + item { + CategorySettingsItem(text = stringResource(id = R.string.player_config_screen_cache_category)) + } + item { + BaseSettingsItem( + icon = rememberVectorPainter(Icons.AutoMirrored.Outlined.KeyboardArrowRight), + text = stringResource(id = R.string.player_config_screen_max_cache_size), + descriptionText = LocalPlayerMaxCacheSize.current.fileSize(context), + onClick = { openMaxCacheSizeDialog = true } + ) + } + item { + BaseSettingsItem( + icon = rememberVectorPainter(Icons.AutoMirrored.Outlined.KeyboardArrowLeft), + text = stringResource(id = R.string.player_config_screen_max_back_cache_size), + descriptionText = LocalPlayerMaxBackCacheSize.current.fileSize(context), + onClick = { openMaxBackCacheSizeDialog = true } + ) + } item { CategorySettingsItem(text = stringResource(id = R.string.player_config_screen_advanced_category)) } @@ -145,6 +186,39 @@ fun PlayerConfigScreen() { ) } } + + if (openMaxCacheSizeDialog) { + MaxCacheSizeDialog( + onDismissRequest = { openMaxCacheSizeDialog = false }, + title = stringResource(id = R.string.player_config_screen_max_cache_size), + initValue = LocalPlayerMaxCacheSize.current, + defaultValue = { PlayerMaxCacheSizePreference.default }, + onConfirm = { + PlayerMaxCacheSizePreference.put( + context = context, + scope = scope, + value = it, + ) + openMaxCacheSizeDialog = false + } + ) + } + if (openMaxBackCacheSizeDialog) { + MaxCacheSizeDialog( + onDismissRequest = { openMaxBackCacheSizeDialog = false }, + title = stringResource(id = R.string.player_config_screen_max_back_cache_size), + initValue = LocalPlayerMaxBackCacheSize.current, + defaultValue = { PlayerMaxBackCacheSizePreference.default }, + onConfirm = { + PlayerMaxBackCacheSizePreference.put( + context = context, + scope = scope, + value = it, + ) + openMaxBackCacheSizeDialog = false + } + ) + } } } @@ -162,4 +236,49 @@ private fun DoubleTapMenu(expanded: Boolean, onDismissRequest: () -> Unit) { onChecked = { PlayerDoubleTapPreference.put(context, scope, it) }, onDismissRequest = onDismissRequest, ) +} + +@Composable +internal fun MaxCacheSizeDialog( + onDismissRequest: () -> Unit, + title: String, + initValue: Long, + defaultValue: () -> Long, + onConfirm: (Long) -> Unit, +) { + var value by rememberSaveable { mutableLongStateOf(initValue) } + val context = LocalContext.current + + // 64 MB / 32 MB + val maxSize = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) 64f else 32f + + SliderDialog( + onDismissRequest = onDismissRequest, + value = value / 1048576f, + onValueChange = { value = it.toLong() * 1048576 }, + valueRange = 1f..maxSize, + valueLabel = { + Box(modifier = Modifier.fillMaxWidth()) { + Text( + modifier = Modifier + .align(Alignment.Center) + .animateContentSize(), + text = value.fileSize(context), + style = MaterialTheme.typography.titleMedium, + ) + AniVuIconButton( + modifier = Modifier.align(Alignment.CenterEnd), + onClick = { value = defaultValue() }, + imageVector = Icons.Outlined.Restore, + ) + } + }, + icon = { Icon(imageVector = Icons.Outlined.Save, contentDescription = null) }, + title = { Text(text = title) }, + confirmButton = { + TextButton(onClick = { onConfirm(value) }) { + Text(text = stringResource(id = R.string.ok)) + } + } + ) } \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 19632beb..29534d1a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -310,6 +310,9 @@ 端口范围只能从 0 到 65535 更多 重排序组 + 缓存 + 最大前向缓存大小 + 最大保留缓存大小 已读 %d 项 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d547f0f..f1d7d1e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -317,6 +317,9 @@ Ports can only range from 0 to 65535 More Reorder groups + Cache + Max cache size + Max back cache size Read %d item Read %d items