mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-06-28 07:46:04 +02:00
![arkon](/assets/img/avatar_default.png)
Also renaming the helper composables so it's a bit easier to find/replace everything in forks.
296 lines
12 KiB
Kotlin
296 lines
12 KiB
Kotlin
package eu.kanade.presentation.more.settings.screen.about
|
|
|
|
import android.content.Context
|
|
import androidx.compose.animation.AnimatedVisibility
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
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.material.icons.Icons
|
|
import androidx.compose.material.icons.outlined.Public
|
|
import androidx.compose.material3.CircularProgressIndicator
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
import androidx.compose.runtime.setValue
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.platform.LocalUriHandler
|
|
import androidx.compose.ui.unit.dp
|
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
|
import eu.kanade.domain.ui.UiPreferences
|
|
import eu.kanade.presentation.components.AppBar
|
|
import eu.kanade.presentation.more.LogoHeader
|
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
|
import eu.kanade.presentation.util.LocalBackPress
|
|
import eu.kanade.presentation.util.Screen
|
|
import eu.kanade.tachiyomi.BuildConfig
|
|
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
|
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
|
|
import eu.kanade.tachiyomi.ui.more.NewUpdateScreen
|
|
import eu.kanade.tachiyomi.util.CrashLogUtil
|
|
import eu.kanade.tachiyomi.util.lang.toDateTimestampString
|
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
|
import eu.kanade.tachiyomi.util.system.toast
|
|
import kotlinx.coroutines.launch
|
|
import logcat.LogPriority
|
|
import tachiyomi.core.util.lang.withIOContext
|
|
import tachiyomi.core.util.lang.withUIContext
|
|
import tachiyomi.core.util.system.logcat
|
|
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
|
import tachiyomi.i18n.MR
|
|
import tachiyomi.presentation.core.components.LinkIcon
|
|
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
|
import tachiyomi.presentation.core.components.material.Scaffold
|
|
import tachiyomi.presentation.core.i18n.stringResource
|
|
import tachiyomi.presentation.core.icons.CustomIcons
|
|
import tachiyomi.presentation.core.icons.Discord
|
|
import tachiyomi.presentation.core.icons.Facebook
|
|
import tachiyomi.presentation.core.icons.Github
|
|
import tachiyomi.presentation.core.icons.Reddit
|
|
import tachiyomi.presentation.core.icons.X
|
|
import uy.kohesive.injekt.Injekt
|
|
import uy.kohesive.injekt.api.get
|
|
import java.text.DateFormat
|
|
import java.text.SimpleDateFormat
|
|
import java.util.Locale
|
|
import java.util.TimeZone
|
|
|
|
object AboutScreen : Screen() {
|
|
|
|
@Composable
|
|
override fun Content() {
|
|
val scope = rememberCoroutineScope()
|
|
val context = LocalContext.current
|
|
val uriHandler = LocalUriHandler.current
|
|
val handleBack = LocalBackPress.current
|
|
val navigator = LocalNavigator.currentOrThrow
|
|
var isCheckingUpdates by remember { mutableStateOf(false) }
|
|
|
|
Scaffold(
|
|
topBar = { scrollBehavior ->
|
|
AppBar(
|
|
title = stringResource(MR.strings.pref_category_about),
|
|
navigateUp = if (handleBack != null) handleBack::invoke else null,
|
|
scrollBehavior = scrollBehavior,
|
|
)
|
|
},
|
|
) { contentPadding ->
|
|
ScrollbarLazyColumn(
|
|
contentPadding = contentPadding,
|
|
) {
|
|
item {
|
|
LogoHeader()
|
|
}
|
|
|
|
item {
|
|
TextPreferenceWidget(
|
|
title = stringResource(MR.strings.version),
|
|
subtitle = getVersionName(withBuildDate = true),
|
|
onPreferenceClick = {
|
|
val deviceInfo = CrashLogUtil(context).getDebugInfo()
|
|
context.copyToClipboard("Debug information", deviceInfo)
|
|
},
|
|
)
|
|
}
|
|
|
|
if (BuildConfig.INCLUDE_UPDATER) {
|
|
item {
|
|
TextPreferenceWidget(
|
|
title = stringResource(MR.strings.check_for_updates),
|
|
widget = {
|
|
AnimatedVisibility(visible = isCheckingUpdates) {
|
|
CircularProgressIndicator(
|
|
modifier = Modifier.size(28.dp),
|
|
strokeWidth = 3.dp,
|
|
)
|
|
}
|
|
},
|
|
onPreferenceClick = {
|
|
if (!isCheckingUpdates) {
|
|
scope.launch {
|
|
isCheckingUpdates = true
|
|
|
|
checkVersion(
|
|
context = context,
|
|
onAvailableUpdate = { result ->
|
|
val updateScreen = NewUpdateScreen(
|
|
versionName = result.release.version,
|
|
changelogInfo = result.release.info,
|
|
releaseLink = result.release.releaseLink,
|
|
downloadLink = result.release.getDownloadLink(),
|
|
)
|
|
navigator.push(updateScreen)
|
|
},
|
|
onFinish = {
|
|
isCheckingUpdates = false
|
|
},
|
|
)
|
|
}
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
if (!BuildConfig.DEBUG) {
|
|
item {
|
|
TextPreferenceWidget(
|
|
title = stringResource(MR.strings.whats_new),
|
|
onPreferenceClick = { uriHandler.openUri(RELEASE_URL) },
|
|
)
|
|
}
|
|
}
|
|
|
|
item {
|
|
TextPreferenceWidget(
|
|
title = stringResource(MR.strings.help_translate),
|
|
onPreferenceClick = { uriHandler.openUri("https://tachiyomi.org/docs/contribute#translation") },
|
|
)
|
|
}
|
|
|
|
item {
|
|
TextPreferenceWidget(
|
|
title = stringResource(MR.strings.licenses),
|
|
onPreferenceClick = { navigator.push(OpenSourceLicensesScreen()) },
|
|
)
|
|
}
|
|
|
|
item {
|
|
TextPreferenceWidget(
|
|
title = stringResource(MR.strings.privacy_policy),
|
|
onPreferenceClick = { uriHandler.openUri("https://tachiyomi.org/privacy/") },
|
|
)
|
|
}
|
|
|
|
item {
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = 8.dp),
|
|
horizontalArrangement = Arrangement.Center,
|
|
) {
|
|
LinkIcon(
|
|
label = stringResource(MR.strings.website),
|
|
icon = Icons.Outlined.Public,
|
|
url = "https://tachiyomi.org",
|
|
)
|
|
LinkIcon(
|
|
label = "Discord",
|
|
icon = CustomIcons.Discord,
|
|
url = "https://discord.gg/tachiyomi",
|
|
)
|
|
LinkIcon(
|
|
label = "X",
|
|
icon = CustomIcons.X,
|
|
url = "https://x.com/tachiyomiorg",
|
|
)
|
|
LinkIcon(
|
|
label = "Facebook",
|
|
icon = CustomIcons.Facebook,
|
|
url = "https://facebook.com/tachiyomiorg",
|
|
)
|
|
LinkIcon(
|
|
label = "Reddit",
|
|
icon = CustomIcons.Reddit,
|
|
url = "https://www.reddit.com/r/Tachiyomi",
|
|
)
|
|
LinkIcon(
|
|
label = "GitHub",
|
|
icon = CustomIcons.Github,
|
|
url = "https://github.com/tachiyomiorg",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks version and shows a user prompt if an update is available.
|
|
*/
|
|
private suspend fun checkVersion(
|
|
context: Context,
|
|
onAvailableUpdate: (GetApplicationRelease.Result.NewUpdate) -> Unit,
|
|
onFinish: () -> Unit,
|
|
) {
|
|
val updateChecker = AppUpdateChecker()
|
|
withUIContext {
|
|
try {
|
|
when (val result = withIOContext { updateChecker.checkForUpdate(context, forceCheck = true) }) {
|
|
is GetApplicationRelease.Result.NewUpdate -> {
|
|
onAvailableUpdate(result)
|
|
}
|
|
is GetApplicationRelease.Result.NoNewUpdate -> {
|
|
context.toast(MR.strings.update_check_no_new_updates)
|
|
}
|
|
is GetApplicationRelease.Result.OsTooOld -> {
|
|
context.toast(MR.strings.update_check_eol)
|
|
}
|
|
else -> {}
|
|
}
|
|
} catch (e: Exception) {
|
|
context.toast(e.message)
|
|
logcat(LogPriority.ERROR, e)
|
|
} finally {
|
|
onFinish()
|
|
}
|
|
}
|
|
}
|
|
|
|
fun getVersionName(withBuildDate: Boolean): String {
|
|
return when {
|
|
BuildConfig.DEBUG -> {
|
|
"Debug ${BuildConfig.COMMIT_SHA}".let {
|
|
if (withBuildDate) {
|
|
"$it (${getFormattedBuildTime()})"
|
|
} else {
|
|
it
|
|
}
|
|
}
|
|
}
|
|
BuildConfig.PREVIEW -> {
|
|
"Preview r${BuildConfig.COMMIT_COUNT}".let {
|
|
if (withBuildDate) {
|
|
"$it (${BuildConfig.COMMIT_SHA}, ${getFormattedBuildTime()})"
|
|
} else {
|
|
"$it (${BuildConfig.COMMIT_SHA})"
|
|
}
|
|
}
|
|
}
|
|
else -> {
|
|
"Stable ${BuildConfig.VERSION_NAME}".let {
|
|
if (withBuildDate) {
|
|
"$it (${getFormattedBuildTime()})"
|
|
} else {
|
|
it
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal fun getFormattedBuildTime(): String {
|
|
return try {
|
|
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)
|
|
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
|
val buildTime = inputDf.parse(BuildConfig.BUILD_TIME)
|
|
|
|
val outputDf = DateFormat.getDateTimeInstance(
|
|
DateFormat.MEDIUM,
|
|
DateFormat.SHORT,
|
|
Locale.getDefault(),
|
|
)
|
|
outputDf.timeZone = TimeZone.getDefault()
|
|
|
|
buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
|
|
} catch (e: Exception) {
|
|
BuildConfig.BUILD_TIME
|
|
}
|
|
}
|
|
}
|