Compare commits
2 Commits
af01156bdc
...
b786eb3182
| Author | SHA1 | Date | |
|---|---|---|---|
|
b786eb3182
|
|||
|
c6268d92d1
|
@@ -1,6 +1,5 @@
|
|||||||
package org.xtimms.tokusho
|
package org.xtimms.tokusho
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
@@ -10,15 +9,14 @@ import android.os.StrictMode
|
|||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||||
import org.xtimms.tokusho.core.database.MangaDatabase
|
import org.xtimms.tokusho.core.database.MangaDatabase
|
||||||
import org.xtimms.tokusho.core.updates.Updater
|
import org.xtimms.tokusho.core.updates.Updater
|
||||||
import org.xtimms.tokusho.crash.CrashActivity
|
import org.xtimms.tokusho.crash.CrashActivity
|
||||||
import org.xtimms.tokusho.crash.GlobalExceptionHandler
|
import org.xtimms.tokusho.crash.GlobalExceptionHandler
|
||||||
|
import org.xtimms.tokusho.utils.lang.processLifecycleScope
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
|
|
||||||
@@ -31,18 +29,16 @@ class App : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
MMKV.initialize(this)
|
MMKV.initialize(this)
|
||||||
context = applicationContext
|
|
||||||
packageInfo = packageManager.run {
|
packageInfo = packageManager.run {
|
||||||
if (Build.VERSION.SDK_INT >= 33) getPackageInfo(
|
if (Build.VERSION.SDK_INT >= 33) getPackageInfo(
|
||||||
packageName, PackageManager.PackageInfoFlags.of(0)
|
packageName, PackageManager.PackageInfoFlags.of(0)
|
||||||
) else getPackageInfo(packageName, 0)
|
) else getPackageInfo(packageName, 0)
|
||||||
}
|
}
|
||||||
applicationScope = CoroutineScope(SupervisorJob())
|
|
||||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
|
||||||
applicationScope.launch((Dispatchers.IO)) {
|
processLifecycleScope.launch((Dispatchers.IO)) {
|
||||||
try {
|
try {
|
||||||
Updater.deleteOutdatedApk()
|
Updater.deleteOutdatedApk(this@App)
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -76,9 +72,9 @@ class App : Application() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
lateinit var applicationScope: CoroutineScope
|
|
||||||
lateinit var packageInfo: PackageInfo
|
lateinit var packageInfo: PackageInfo
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
fun getVersionReport(): String {
|
fun getVersionReport(): String {
|
||||||
val versionName = packageInfo.versionName
|
val versionName = packageInfo.versionName
|
||||||
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
@@ -95,9 +91,6 @@ class App : Application() {
|
|||||||
.append("Device information: Android $release (API ${Build.VERSION.SDK_INT})\n")
|
.append("Device information: Android $release (API ${Build.VERSION.SDK_INT})\n")
|
||||||
.append("Supported ABIs: ${Build.SUPPORTED_ABIS.contentToString()}\n").toString()
|
.append("Supported ABIs: ${Build.SUPPORTED_ABIS.contentToString()}\n").toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
lateinit var context: Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,7 @@ import org.xtimms.tokusho.core.Navigation
|
|||||||
import org.xtimms.tokusho.core.components.BottomNavBar
|
import org.xtimms.tokusho.core.components.BottomNavBar
|
||||||
import org.xtimms.tokusho.core.components.TopAppBar
|
import org.xtimms.tokusho.core.components.TopAppBar
|
||||||
import org.xtimms.tokusho.ui.theme.TokushoTheme
|
import org.xtimms.tokusho.ui.theme.TokushoTheme
|
||||||
|
import org.xtimms.tokusho.utils.lang.processLifecycleScope
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||||
@@ -83,7 +84,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val localeListCompat =
|
val localeListCompat =
|
||||||
if (locale.isEmpty()) LocaleListCompat.getEmptyLocaleList()
|
if (locale.isEmpty()) LocaleListCompat.getEmptyLocaleList()
|
||||||
else LocaleListCompat.forLanguageTags(locale)
|
else LocaleListCompat.forLanguageTags(locale)
|
||||||
App.applicationScope.launch(Dispatchers.Main) {
|
processLifecycleScope.launch(Dispatchers.Main) {
|
||||||
AppCompatDelegate.setApplicationLocales(localeListCompat)
|
AppCompatDelegate.setApplicationLocales(localeListCompat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.xtimms.shiki.ui.theme.SEED
|
import org.xtimms.shiki.ui.theme.SEED
|
||||||
import org.xtimms.tokusho.App.Companion.applicationScope
|
|
||||||
import org.xtimms.tokusho.R
|
import org.xtimms.tokusho.R
|
||||||
import org.xtimms.tokusho.ui.monet.PaletteStyle
|
import org.xtimms.tokusho.ui.monet.PaletteStyle
|
||||||
|
import org.xtimms.tokusho.utils.lang.processLifecycleScope
|
||||||
import org.xtimms.tokusho.utils.system.languageMap
|
import org.xtimms.tokusho.utils.system.languageMap
|
||||||
|
|
||||||
private const val DYNAMIC_COLOR = "dynamic_color"
|
private const val DYNAMIC_COLOR = "dynamic_color"
|
||||||
@@ -85,7 +85,7 @@ object AppSettings {
|
|||||||
fun encodeString(key: String, string: String) = key.updateString(string)
|
fun encodeString(key: String, string: String) = key.updateString(string)
|
||||||
fun containsKey(key: String) = kv.containsKey(key)
|
fun containsKey(key: String) = kv.containsKey(key)
|
||||||
|
|
||||||
fun isAutoUpdateEnabled() = AUTO_UPDATE.getBoolean(true)
|
fun isAutoUpdateEnabled() = AUTO_UPDATE.getBoolean(false)
|
||||||
|
|
||||||
fun getLanguageConfiguration(languageNumber: Int = kv.decodeInt(LANGUAGE)) =
|
fun getLanguageConfiguration(languageNumber: Int = kv.decodeInt(LANGUAGE)) =
|
||||||
languageMap.getOrElse(languageNumber) { "" }
|
languageMap.getOrElse(languageNumber) { "" }
|
||||||
@@ -129,7 +129,7 @@ object AppSettings {
|
|||||||
darkThemeValue: Int = AppSettingsStateFlow.value.darkTheme.darkThemeValue,
|
darkThemeValue: Int = AppSettingsStateFlow.value.darkTheme.darkThemeValue,
|
||||||
isHighContrastModeEnabled: Boolean = AppSettingsStateFlow.value.darkTheme.isHighContrastModeEnabled
|
isHighContrastModeEnabled: Boolean = AppSettingsStateFlow.value.darkTheme.isHighContrastModeEnabled
|
||||||
) {
|
) {
|
||||||
applicationScope.launch(Dispatchers.IO) {
|
processLifecycleScope.launch(Dispatchers.IO) {
|
||||||
mutableAppSettingsStateFlow.update {
|
mutableAppSettingsStateFlow.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
darkTheme = AppSettingsStateFlow.value.darkTheme.copy(
|
darkTheme = AppSettingsStateFlow.value.darkTheme.copy(
|
||||||
@@ -144,7 +144,7 @@ object AppSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun modifyThemeSeedColor(colorArgb: Int, paletteStyleIndex: Int) {
|
fun modifyThemeSeedColor(colorArgb: Int, paletteStyleIndex: Int) {
|
||||||
applicationScope.launch(Dispatchers.IO) {
|
processLifecycleScope.launch(Dispatchers.IO) {
|
||||||
mutableAppSettingsStateFlow.update {
|
mutableAppSettingsStateFlow.update {
|
||||||
it.copy(seedColor = colorArgb, paletteStyleIndex = paletteStyleIndex)
|
it.copy(seedColor = colorArgb, paletteStyleIndex = paletteStyleIndex)
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ object AppSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun switchDynamicColor(enabled: Boolean = !mutableAppSettingsStateFlow.value.isDynamicColorEnabled) {
|
fun switchDynamicColor(enabled: Boolean = !mutableAppSettingsStateFlow.value.isDynamicColorEnabled) {
|
||||||
applicationScope.launch(Dispatchers.IO) {
|
processLifecycleScope.launch(Dispatchers.IO) {
|
||||||
mutableAppSettingsStateFlow.update {
|
mutableAppSettingsStateFlow.update {
|
||||||
it.copy(isDynamicColorEnabled = enabled)
|
it.copy(isDynamicColorEnabled = enabled)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,11 +39,14 @@ fun UpdateDialog(
|
|||||||
onConfirmUpdate = {
|
onConfirmUpdate = {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
Updater.downloadApk(latestRelease = latestRelease)
|
Updater.downloadApk(
|
||||||
|
context = context,
|
||||||
|
latestRelease = latestRelease
|
||||||
|
)
|
||||||
.collect { downloadStatus ->
|
.collect { downloadStatus ->
|
||||||
currentDownloadStatus = downloadStatus
|
currentDownloadStatus = downloadStatus
|
||||||
if (downloadStatus is Updater.DownloadStatus.Finished) {
|
if (downloadStatus is Updater.DownloadStatus.Finished) {
|
||||||
Updater.installLatestApk()
|
Updater.installLatestApk(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ object Updater {
|
|||||||
latestRelease
|
latestRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkForUpdate(context: Context = App.context): LatestRelease? {
|
suspend fun checkForUpdate(context: Context): LatestRelease? {
|
||||||
val currentVersion = context.getCurrentVersion()
|
val currentVersion = context.getCurrentVersion()
|
||||||
val latestRelease = getLatestRelease()
|
val latestRelease = getLatestRelease()
|
||||||
val latestVersion = latestRelease.name.toVersion()
|
val latestVersion = latestRelease.name.toVersion()
|
||||||
@@ -80,7 +80,7 @@ object Updater {
|
|||||||
private fun Context.getLatestApk() =
|
private fun Context.getLatestApk() =
|
||||||
File(getExternalFilesDir("apk"), "latest.apk")
|
File(getExternalFilesDir("apk"), "latest.apk")
|
||||||
|
|
||||||
fun installLatestApk(context: Context = App.context) = context.run {
|
fun installLatestApk(context: Context) = context.run {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val contentUri = FileProvider.getUriForFile(this, getFileProvider(), getLatestApk())
|
val contentUri = FileProvider.getUriForFile(this, getFileProvider(), getLatestApk())
|
||||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
@@ -96,7 +96,7 @@ object Updater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteOutdatedApk(
|
suspend fun deleteOutdatedApk(
|
||||||
context: Context = App.context,
|
context: Context,
|
||||||
) = context.runCatching {
|
) = context.runCatching {
|
||||||
val apkFile = getLatestApk()
|
val apkFile = getLatestApk()
|
||||||
if (apkFile.exists()) {
|
if (apkFile.exists()) {
|
||||||
@@ -110,7 +110,7 @@ object Updater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun downloadApk(
|
suspend fun downloadApk(
|
||||||
context: Context = App.context,
|
context: Context,
|
||||||
latestRelease: LatestRelease
|
latestRelease: LatestRelease
|
||||||
): Flow<DownloadStatus> = withContext(Dispatchers.IO) {
|
): Flow<DownloadStatus> = withContext(Dispatchers.IO) {
|
||||||
val apkVersion = context.packageManager.getPackageArchiveInfo(
|
val apkVersion = context.packageManager.getPackageArchiveInfo(
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package org.xtimms.tokusho.sections.settings.about
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Description
|
||||||
import androidx.compose.material.icons.outlined.Info
|
import androidx.compose.material.icons.outlined.Info
|
||||||
import androidx.compose.material.icons.outlined.Update
|
import androidx.compose.material.icons.outlined.Update
|
||||||
|
import androidx.compose.material.icons.outlined.UpdateDisabled
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -13,6 +15,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import org.xtimms.tokusho.App
|
import org.xtimms.tokusho.App
|
||||||
@@ -27,6 +30,7 @@ import org.xtimms.tokusho.utils.system.toast
|
|||||||
|
|
||||||
const val ABOUT_DESTINATION = "about"
|
const val ABOUT_DESTINATION = "about"
|
||||||
|
|
||||||
|
private const val repoUrl = "https://git.kotatsu.app/Xtimms/Tokusho"
|
||||||
const val weblate = "https://hosted.weblate.org/engage/tokusho/"
|
const val weblate = "https://hosted.weblate.org/engage/tokusho/"
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -42,6 +46,11 @@ fun AboutView(
|
|||||||
val info = App.getVersionReport()
|
val info = App.getVersionReport()
|
||||||
val versionName = packageInfo.versionName
|
val versionName = packageInfo.versionName
|
||||||
|
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
fun openUrl(url: String) {
|
||||||
|
uriHandler.openUri(url)
|
||||||
|
}
|
||||||
|
|
||||||
ScaffoldWithTopAppBar(
|
ScaffoldWithTopAppBar(
|
||||||
title = stringResource(R.string.about),
|
title = stringResource(R.string.about),
|
||||||
navigateBack = navigateBack
|
navigateBack = navigateBack
|
||||||
@@ -50,13 +59,19 @@ fun AboutView(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
) {
|
) {
|
||||||
|
item {
|
||||||
|
PreferenceItem(
|
||||||
|
title = stringResource(R.string.readme),
|
||||||
|
description = stringResource(R.string.readme_desc),
|
||||||
|
icon = Icons.Outlined.Description,
|
||||||
|
) { openUrl(repoUrl) }
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
PreferenceSwitchWithDivider(
|
PreferenceSwitchWithDivider(
|
||||||
title = stringResource(R.string.auto_update),
|
title = stringResource(R.string.auto_update),
|
||||||
description = stringResource(R.string.check_for_updates_desc),
|
description = stringResource(R.string.check_for_updates_desc),
|
||||||
icon = Icons.Outlined.Update,
|
icon = if (isAutoUpdateEnabled) Icons.Outlined.Update else Icons.Outlined.UpdateDisabled,
|
||||||
isChecked = isAutoUpdateEnabled,
|
isChecked = isAutoUpdateEnabled,
|
||||||
isSwitchEnabled = true,
|
|
||||||
onClick = navigateToUpdatePage,
|
onClick = navigateToUpdatePage,
|
||||||
onChecked = {
|
onChecked = {
|
||||||
isAutoUpdateEnabled = !isAutoUpdateEnabled
|
isAutoUpdateEnabled = !isAutoUpdateEnabled
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ fun UpdateView(
|
|||||||
runCatching {
|
runCatching {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
Updater.checkForUpdate()?.let {
|
Updater.checkForUpdate(context)?.let {
|
||||||
latestRelease = it
|
latestRelease = it
|
||||||
showUpdateDialog = true
|
showUpdateDialog = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.widget.Toast
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.xtimms.tokusho.App.Companion.applicationScope
|
import org.xtimms.tokusho.utils.lang.processLifecycleScope
|
||||||
|
|
||||||
fun Context.toast(message: String) {
|
fun Context.toast(message: String) {
|
||||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||||
@@ -16,7 +16,7 @@ fun Context.toast(@StringRes stringRes: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Context.suspendToast(@StringRes stringRes: Int) {
|
fun Context.suspendToast(@StringRes stringRes: Int) {
|
||||||
applicationScope.launch(Dispatchers.Main) {
|
processLifecycleScope.launch(Dispatchers.Main) {
|
||||||
toast(getString(stringRes))
|
toast(getString(stringRes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<string name="off">Off</string>
|
<string name="off">Off</string>
|
||||||
<string name="open_settings">Open settings</string>
|
<string name="open_settings">Open settings</string>
|
||||||
<string name="auto_update">Automatic updates</string>
|
<string name="auto_update">Automatic updates</string>
|
||||||
<string name="check_for_updates_desc">Automatically check for the latest version on GitHub</string>
|
<string name="check_for_updates_desc">Automatically check for the latest version on Gitea</string>
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
<string name="info_copied">Info copied to clipboard</string>
|
<string name="info_copied">Info copied to clipboard</string>
|
||||||
<string name="enable_auto_update">Enable auto update</string>
|
<string name="enable_auto_update">Enable auto update</string>
|
||||||
@@ -57,4 +57,6 @@
|
|||||||
<string name="no_results_found">No results found</string>
|
<string name="no_results_found">No results found</string>
|
||||||
<string name="error_no_match">No matches</string>
|
<string name="error_no_match">No matches</string>
|
||||||
<string name="information_no_manga_category">No manga in this category</string>
|
<string name="information_no_manga_category">No manga in this category</string>
|
||||||
|
<string name="readme">README</string>
|
||||||
|
<string name="readme_desc">Check the Gitea repository and the README</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user