From 32750db15aadf23c14a4d9b1576798a642af5192 Mon Sep 17 00:00:00 2001 From: AbdallahMehiz Date: Sat, 24 Aug 2024 06:30:21 +0100 Subject: [PATCH] feat: about screen *inspired* by mihon/aniyomi --- app/build.gradle.kts | 3 + .../mpvkt/presentation/crash/CrashActivity.kt | 34 ++-- .../live/mehiz/mpvkt/ui/home/HomeScreen.kt | 8 + .../mehiz/mpvkt/ui/player/PlayerActivity.kt | 5 +- .../mehiz/mpvkt/ui/player/PlayerViewModel.kt | 4 +- .../mehiz/mpvkt/ui/preferences/AboutScreen.kt | 157 ++++++++++++++++++ .../mpvkt/ui/preferences/PreferencesScreen.kt | 14 ++ app/src/main/res/values/strings.xml | 16 +- gradle/libs.versions.toml | 7 + 9 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/live/mehiz/mpvkt/ui/preferences/AboutScreen.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 172e48f..63c51f4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,6 +11,7 @@ plugins { alias(libs.plugins.kotlin.compose.compiler) alias(libs.plugins.room) alias(libs.plugins.detekt) + alias(libs.plugins.about.libraries) } android { @@ -121,6 +122,8 @@ dependencies { implementation(libs.bundles.koin) implementation(libs.bundles.voyager) implementation(libs.compose.prefs) + implementation(libs.bundles.about.libs) + implementation(libs.simple.icons) implementation(libs.room.runtime) ksp(libs.room.compiler) diff --git a/app/src/main/java/live/mehiz/mpvkt/presentation/crash/CrashActivity.kt b/app/src/main/java/live/mehiz/mpvkt/presentation/crash/CrashActivity.kt index 3673f4e..43ca381 100644 --- a/app/src/main/java/live/mehiz/mpvkt/presentation/crash/CrashActivity.kt +++ b/app/src/main/java/live/mehiz/mpvkt/presentation/crash/CrashActivity.kt @@ -89,19 +89,6 @@ class CrashActivity : ComponentActivity() { return logcat.toString() } - private fun collectDeviceInfo(): String { - return """ - App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.GIT_SHA}/${BuildConfig.BUILD_TIME}) - Android version: ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT}) - Device brand: ${Build.BRAND} - Device manufacturer: ${Build.MANUFACTURER} - Device model: ${Build.MODEL} (${Build.DEVICE}) - MPV version: ${Utils.VERSIONS.mpv} - ffmpeg version: ${Utils.VERSIONS.ffmpeg} - libplacebo version: ${Utils.VERSIONS.libPlacebo} - """.trimIndent() - } - private fun concatLogs( deviceInfo: String, crashLogs: String, @@ -120,7 +107,7 @@ class CrashActivity : ComponentActivity() { private suspend fun dumpLogs( exceptionString: String, - logcat: String + logcat: String, ) { withContext(NonCancellable) { val file = File(applicationContext.cacheDir, "mpvKt_logs.txt") @@ -179,8 +166,8 @@ class CrashActivity : ComponentActivity() { clipboardManager.setPrimaryClip( ClipData.newPlainText( null, - concatLogs(collectDeviceInfo(), exceptionString, logcat) - ) + concatLogs(collectDeviceInfo(), exceptionString, logcat), + ), ) }, ) { @@ -239,7 +226,7 @@ class CrashActivity : ComponentActivity() { @Composable fun LogsContainer( logs: String, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { LazyRow( modifier = modifier @@ -259,3 +246,16 @@ class CrashActivity : ComponentActivity() { } } } + +fun collectDeviceInfo(): String { + return """ + App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.GIT_SHA}/${BuildConfig.BUILD_TIME}) + Android version: ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT}) + Device brand: ${Build.BRAND} + Device manufacturer: ${Build.MANUFACTURER} + Device model: ${Build.MODEL} (${Build.DEVICE}) + MPV version: ${Utils.VERSIONS.mpv} + ffmpeg version: ${Utils.VERSIONS.ffmpeg} + libplacebo version: ${Utils.VERSIONS.libPlacebo} + """.trimIndent() +} diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt b/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt index f1557f0..9d87772 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -33,6 +34,7 @@ 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.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import cafe.adriel.voyager.navigator.LocalNavigator @@ -59,6 +61,12 @@ object HomeScreen : Screen() { Icon(Icons.Default.Settings, null) } }, + navigationIcon = { + Image( + painter = painterResource(id = R.drawable.ic_launcher_foreground), + contentDescription = "app_logo", + ) + }, ) }, ) { padding -> diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt index d210735..40d53f5 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt @@ -459,10 +459,10 @@ class PlayerActivity : AppCompatActivity() { internal fun onObserverEvent(property: String, value: String) { when (property) { "speed" -> viewModel.playbackSpeed.update { value.toFloat() } - "hwdec-current" -> Decoder.entries.find { it.value == value }?.let { viewModel.updateDecoder(it) } "aid" -> trackId(value)?.let { viewModel.selectAudio(it) } "sid" -> trackId(value)?.let { viewModel.setSubtitle(it, viewModel.selectedSubtitles.value.second) } "secondary-sid" -> trackId(value)?.let { viewModel.setSubtitle(viewModel.selectedSubtitles.value.first, it) } + "hwdec", "hwdec-current" -> viewModel.getDecoder() } } @@ -674,6 +674,7 @@ class PlayerActivity : AppCompatActivity() { "sid" to MPVLib.mpvFormat.MPV_FORMAT_STRING, "secondary-sid" to MPVLib.mpvFormat.MPV_FORMAT_STRING, "aid" to MPVLib.mpvFormat.MPV_FORMAT_STRING, - "hwdec-current" to MPVLib.mpvFormat.MPV_FORMAT_STRING + "hwdec-current" to MPVLib.mpvFormat.MPV_FORMAT_STRING, + "hwdec" to MPVLib.mpvFormat.MPV_FORMAT_STRING ) } diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt index 1ebca41..57e8447 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt @@ -96,8 +96,8 @@ class PlayerViewModel( } } - fun cancelTimer() { - timerJob?.cancel() + fun getDecoder() { + _currentDecoder.update { getDecoderFromValue(activity.player.hwdecActive) } } fun cycleDecoders() { diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/preferences/AboutScreen.kt b/app/src/main/java/live/mehiz/mpvkt/ui/preferences/AboutScreen.kt new file mode 100644 index 0000000..e715477 --- /dev/null +++ b/app/src/main/java/live/mehiz/mpvkt/ui/preferences/AboutScreen.kt @@ -0,0 +1,157 @@ +package live.mehiz.mpvkt.ui.preferences + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer +import compose.icons.SimpleIcons +import compose.icons.simpleicons.Github +import live.mehiz.mpvkt.BuildConfig +import live.mehiz.mpvkt.R +import live.mehiz.mpvkt.presentation.Screen +import live.mehiz.mpvkt.presentation.crash.collectDeviceInfo +import live.mehiz.mpvkt.ui.theme.spacing +import me.zhanghai.compose.preference.Preference +import me.zhanghai.compose.preference.ProvidePreferenceLocals +import java.util.Locale + +object AboutScreen : Screen() { + @OptIn(ExperimentalMaterial3Api::class) + @Composable + override fun Content() { + val context = LocalContext.current + val navigator = LocalNavigator.currentOrThrow + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = stringResource(id = R.string.pref_about_title)) }, + navigationIcon = { + IconButton(onClick = { navigator.pop() }) { + Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) + } + }, + ) + }, + ) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .verticalScroll(rememberScrollState()), + ) { + Column( + Modifier + .fillMaxWidth() + .padding(bottom = MaterialTheme.spacing.large), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + painterResource(id = R.drawable.ic_launcher_foreground), + null, + modifier = Modifier.size(160.dp), + ) + Text( + text = stringResource(id = R.string.app_name), + style = MaterialTheme.typography.headlineSmall, + ) + } + HorizontalDivider() + ProvidePreferenceLocals { + Preference( + title = { Text(text = "App version") }, + summary = { + Text( + text = BuildConfig.BUILD_TYPE.replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase(Locale.ROOT) + } else { + it.toString() + } + } + " " + BuildConfig.VERSION_NAME + " " + "(${BuildConfig.BUILD_TIME})", + ) + }, + onClick = { + val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboardManager.setPrimaryClip(ClipData.newPlainText("app_version_data", collectDeviceInfo())) + }, + ) + Preference( + title = { Text(text = stringResource(id = R.string.pref_about_oss_libraries)) }, + onClick = { navigator.push(LibrariesScreen) }, + ) + } + Row( + Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + IconButton( + onClick = { + context.startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.github_repo_url))), + ) + }, + ) { + Icon(imageVector = SimpleIcons.Github, contentDescription = null) + } + } + } + } + } +} + +object LibrariesScreen : Screen() { + @OptIn(ExperimentalMaterial3Api::class) + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + Scaffold( + topBar = { + TopAppBar( + title = { + Text(text = "Open source libraries") + }, + navigationIcon = { + IconButton( + onClick = { + navigator.pop() + }, + ) { + Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) + } + }, + ) + }, + ) { paddingValues -> + LibrariesContainer(modifier = Modifier.padding(paddingValues)) + } + } +} diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/preferences/PreferencesScreen.kt b/app/src/main/java/live/mehiz/mpvkt/ui/preferences/PreferencesScreen.kt index 96fbe47..93b5e10 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/preferences/PreferencesScreen.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/preferences/PreferencesScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.outlined.Audiotrack import androidx.compose.material.icons.outlined.Code +import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Memory import androidx.compose.material.icons.outlined.Palette import androidx.compose.material.icons.outlined.PlayCircle @@ -53,39 +54,52 @@ object PreferencesScreen : Screen() { preference( key = "appearance", title = { Text(text = stringResource(id = R.string.pref_appearance_title)) }, + summary = { Text(text = stringResource(id = R.string.pref_appearance_summary)) }, icon = { Icon(Icons.Outlined.Palette, null) }, onClick = { navigator.push(AppearancePreferencesScreen) }, ) preference( key = "player", title = { Text(text = stringResource(id = R.string.pref_player)) }, + summary = { Text(text = stringResource(id = R.string.pref_player_summary)) }, icon = { Icon(Icons.Outlined.PlayCircle, null) }, onClick = { navigator.push(PlayerPreferencesScreen) }, ) preference( key = "decoder", title = { Text(text = stringResource(id = R.string.pref_decoder)) }, + summary = { Text(text = stringResource(id = R.string.pref_decoder_summary)) }, icon = { Icon(Icons.Outlined.Memory, null) }, onClick = { navigator.push(DecoderPreferencesScreen) }, ) preference( key = "subtitles", title = { Text(text = stringResource(id = R.string.pref_subtitles)) }, + summary = { Text(text = stringResource(id = R.string.pref_subtitles_summary)) }, icon = { Icon(Icons.Outlined.Subtitles, null) }, onClick = { navigator.push(SubtitlesPreferencesScreen) }, ) preference( key = "audio", title = { Text(text = stringResource(id = R.string.pref_audio)) }, + summary = { Text(text = stringResource(id = R.string.pref_audio_summary)) }, icon = { Icon(Icons.Outlined.Audiotrack, null) }, onClick = { navigator.push(AudioPreferencesScreen) }, ) preference( key = "advanced", title = { Text(text = stringResource(R.string.pref_advanced)) }, + summary = { Text(text = stringResource(id = R.string.pref_advanced_summary)) }, icon = { Icon(Icons.Outlined.Code, null) }, onClick = { navigator.push(AdvancedPreferencesScreen) } ) + preference( + key = "about", + title = { Text(text = stringResource(id = R.string.pref_about_title)) }, + summary = { Text(text = stringResource(id = R.string.pref_about_summary)) }, + icon = { Icon(Icons.Outlined.Info, null) }, + onClick = { navigator.push(AboutScreen) }, + ) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf88715..e60b59f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,7 +5,7 @@ Share Confirm Cancel - Ok + OK ms Pick a file @@ -15,7 +15,9 @@ Invalid protocol Preferences + Appearance + Dark mode, material you Theme System Light @@ -25,6 +27,7 @@ Only available for Android 12 and newer. Player + Orientation, gestures and controls Orientation Free Video @@ -54,6 +57,7 @@ Only appears if the video has chapters. Decoder + Hardware decoding, pixel format, debanding Try hardware decoding Use gpu-next A new rendering backend. @@ -62,10 +66,12 @@ May fix black screens on some video codecs, can also improve performance at the cost of quality. Subtitles + Preferred languages, fonts Preferred languages Fonts directory Audio + Preferred languages, audio channels, pitch correction Enable audio pitch correction Prevents the audio from becoming high-pitched at faster speeds and low-pitched at slower speeds. Audio channels @@ -76,6 +82,7 @@ Reversed Stereo Advanced + Configuration location, mpv.conf, input.conf pick mpv configuration storage location Edit mpv.conf Edit input.conf @@ -88,6 +95,11 @@ Clear cached mpv configurations Cleared cached mpv configurations + About + Acknowledgments, licenses + App version + Open source libraries + Add external subtitles Add external audio tracks Off @@ -155,6 +167,8 @@ Sleep timer ended + https://github.com/abdallahmehiz/mpvKt + %1$d item %1$d items diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c71c642..7f5c7ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,7 @@ voyager = "1.0.0" material3Android = "1.2.1" room = "2.6.1" detekt = "1.23.6" +about-libs = "11.2.3" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -57,9 +58,14 @@ kotlinx-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collec truetype-parser = { module = "io.github.yubyf:truetypeparser-light", version = "2.1.4" } fsaf = { module = "com.github.K1rakishou:Fuck-Storage-Access-Framework", version = "1.1.3" } + +about-libs-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "about-libs" } +about-libs-ui-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "about-libs" } +simple-icons = { module = "br.com.devsrsouza.compose.icons:simple-icons", version = "1.1.0" } [bundles] koin = ["koin-core", "koin-android", "koin-compose"] voyager = ["voyager-navigator", "voyager-transitions"] +about-libs = ["about-libs-core", "about-libs-ui-m3"] [plugins] ksp = { id = "com.google.devtools.ksp", version = "2.0.0-1.0.23" } @@ -68,3 +74,4 @@ jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } room = { id = "androidx.room", version.ref = "room" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "about-libs" }