Implement automatic update checks

This commit is contained in:
FooIbar authored and tarsin committed 2023-07-11 06:53:09 +08:00
1 parent a8c96fa7df
commit d9522e4e89
9 files changed
+170 -31

No files matched your search

@@ -74,12 +74,14 @@ object Settings : DataStorePreferences(null) {
var requestNewsTimerHour by intPref("request_news_timer_hour", -1)
var requestNewsTimerMinute by intPref("request_news_timer_minute", -1)
var dataMapNextId by intPref("data_map_next_id", 0)
var updateIntervalDays by intPref("update_interval_days", 0)
var recentDownloadLabel by stringOrNullPref("recent_download_label", null)
var defaultDownloadLabel by stringOrNullPref("default_download_label", null)
var displayName by stringOrNullPref("display_name", null)
var avatar by stringOrNullPref("avatar", null)
var language by stringPref("app_language", "system").observed { updateWhenLocaleChanges() }
var lastDawnDay by longPref("last_dawn_day", 0)
var lastUpdateDay by longPref("last_update_day", 0)
init {
if ("CN" == Locale.getDefault().country) {
@@ -90,7 +90,9 @@ import com.hippo.ehviewer.ui.legacy.GalleryInfoContentHelper
import com.hippo.ehviewer.ui.legacy.ViewTransition
import com.hippo.ehviewer.ui.legacy.WindowInsetsAnimationHelper
import com.hippo.ehviewer.ui.setMD3Content
import com.hippo.ehviewer.ui.settings.showNewVersion
import com.hippo.ehviewer.ui.tools.DialogState
import com.hippo.ehviewer.updater.AppUpdater
import com.hippo.ehviewer.util.getParcelableCompat
import com.hippo.ehviewer.yorozuya.AnimationUtils
import com.hippo.ehviewer.yorozuya.SimpleAnimatorListener
@@ -289,6 +291,17 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, OnClickFabList
private val dialogState = DialogState()
// TODO: Move this out
private fun checkForUpdates() {
lifecycleScope.launchIO {
runSuspendCatching {
AppUpdater.checkForUpdate()?.let {
dialogState.showNewVersion(requireContext(), it)
}
}
}
}
override fun onCreateViewWithToolbar(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -296,6 +309,7 @@ class GalleryListScene : SearchBarScene(), OnDragHandlerListener, OnClickFabList
): View {
_binding = SceneGalleryListBinding.inflate(inflater, container, false)
binding.root.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Handler() } })
checkForUpdates()
requireActivity().onBackPressedDispatcher.addCallback(mCallback)
mHideActionFabSlop = ViewConfiguration.get(requireContext()).scaledTouchSlop
mShowActionFab = true
@@ -1,9 +1,12 @@
package com.hippo.ehviewer.ui.settings
import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Icon
@@ -32,9 +35,11 @@ import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.ui.LICENSE_SCREEN
import com.hippo.ehviewer.ui.LocalNavController
import com.hippo.ehviewer.ui.tools.DialogState
import com.hippo.ehviewer.ui.tools.observed
import com.hippo.ehviewer.ui.tools.rememberDialogState
import com.hippo.ehviewer.ui.tools.toAnnotatedString
import com.hippo.ehviewer.updater.AppUpdater
import com.hippo.ehviewer.updater.Release
import com.hippo.ehviewer.util.ExceptionUtils
import com.hippo.ehviewer.util.installPackage
import eu.kanade.tachiyomi.util.lang.withUIContext
@@ -64,31 +69,6 @@ fun AboutScreen() {
val dialogState = rememberDialogState()
dialogState.Handler()
fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) }
suspend fun DialogState.showNewVersion(
name: String,
changelog: String,
downloadTo: suspend (File) -> Unit,
) {
val download = show(
confirmText = R.string.download,
dismissText = android.R.string.cancel,
title = R.string.new_version_available,
) {
Column {
Text(
text = name,
style = MaterialTheme.typography.headlineSmall,
)
Spacer(modifier = Modifier.height(16.dp))
Text(text = changelog)
}
}
if (download) {
val file = File(AppConfig.tempDir, "update.apk").apply { delete() }
downloadTo(file)
withUIContext { context.installPackage(file) }
}
}
Scaffold(
topBar = {
TopAppBar(
@@ -103,7 +83,7 @@ fun AboutScreen() {
},
snackbarHost = { SnackbarHost(snackbarHostState) },
) { paddingValues ->
Column(modifier = Modifier.padding(paddingValues).nestedScroll(scrollBehavior.nestedScrollConnection)) {
Column(modifier = Modifier.padding(paddingValues).nestedScroll(scrollBehavior.nestedScrollConnection).verticalScroll(rememberScrollState())) {
Preference(
title = stringResource(id = R.string.settings_about_declaration),
summary = stringResource(id = R.string.settings_about_declaration_summary),
@@ -131,12 +111,16 @@ fun AboutScreen() {
title = stringResource(id = R.string.use_ci_update_channel),
value = Settings::useCIUpdateChannel,
)
SimpleMenuPreferenceInt2(
title = stringResource(id = R.string.auto_updates),
entry = R.array.update_frequency,
entryValueRes = R.array.update_frequency_values,
value = Settings::updateIntervalDays.observed,
)
WorkPreference(title = stringResource(id = R.string.settings_about_check_for_updates)) {
runSuspendCatching {
AppUpdater.checkForUpdate(true)?.run {
dialogState.showNewVersion(version, changelog) { file ->
AppUpdater.downloadUpdate(downloadLink, file)
}
AppUpdater.checkForUpdate(true)?.let {
dialogState.showNewVersion(context, it)
} ?: launchSnackBar(context.getString(R.string.already_latest_version))
}.onFailure {
launchSnackBar(ExceptionUtils.getReadableString(it))
@@ -145,3 +129,26 @@ fun AboutScreen() {
}
}
}
suspend fun DialogState.showNewVersion(context: Context, release: Release) {
val download = show(
confirmText = R.string.download,
dismissText = android.R.string.cancel,
title = R.string.new_version_available,
) {
Column {
Text(
text = release.version,
style = MaterialTheme.typography.headlineSmall,
)
Spacer(modifier = Modifier.height(16.dp))
Text(text = release.changelog)
}
}
// TODO: Download in the background and show progress in notification
if (download) {
val file = File(AppConfig.tempDir, "update.apk").apply { delete() }
AppUpdater.downloadUpdate(release.downloadLink, file)
withUIContext { context.installPackage(file) }
}
}
@@ -29,6 +29,7 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.integerArrayResource
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
@@ -40,6 +41,7 @@ import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
import com.hippo.ehviewer.ui.openBrowser
import com.hippo.ehviewer.ui.settings.PreferenceTokens.PreferenceTextPadding
import com.jamal.composeprefs3.ui.prefs.DropDownPref
import com.jamal.composeprefs3.ui.prefs.DropDownPrefInt
import com.jamal.composeprefs3.ui.prefs.SliderPref
import com.jamal.composeprefs3.ui.prefs.SpannedTextPref
import com.jamal.composeprefs3.ui.prefs.SwitchPref
@@ -121,6 +123,20 @@ fun SimpleMenuPreferenceInt(title: String, summary: String? = null, @ArrayRes en
DropDownPref(title = title, summary = summary, defaultValue = v.toString(), onValueChange = ::set, useSelectedAsSummary = summary.isNullOrBlank(), entries = map)
}
@Composable
fun SimpleMenuPreferenceInt2(title: String, summary: String? = null, @ArrayRes entry: Int, @ArrayRes entryValueRes: Int, value: MutableState<Int>) {
val entryArray = stringArrayResource(id = entry)
val valuesArray = integerArrayResource(id = entryValueRes)
val map = remember {
val iter = entryArray.iterator()
valuesArray.associateWith { iter.next() }
}
var v by value
fun set(new: Int) { v = new }
check(entryArray.size == valuesArray.size)
DropDownPrefInt(title = title, summary = summary, defaultValue = v, onValueChange = ::set, useSelectedAsSummary = summary.isNullOrBlank(), entries = map)
}
@Composable
fun SimpleMenuPreference(title: String, @ArrayRes entry: Int, @ArrayRes entryValueRes: Int, value: KMutableProperty0<String>) {
val entryArray = stringArrayResource(id = entry)
@@ -15,6 +15,8 @@ import tachiyomi.data.release.GithubCommitComparison
import tachiyomi.data.release.GithubRelease
import tachiyomi.data.release.GithubWorkflowRuns
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.zip.ZipInputStream
private const val API_URL = "https://api.github.com/repos/${BuildConfig.REPO_NAME}"
@@ -22,7 +24,11 @@ private const val LATEST_RELEASE_URL = "$API_URL/releases/latest"
object AppUpdater {
suspend fun checkForUpdate(forceCheck: Boolean = false): Release? {
if (forceCheck) {
val now = Instant.now()
val lastChecked = Instant.ofEpochSecond(Settings.lastUpdateDay)
val interval = Settings.updateIntervalDays
if (forceCheck || interval != 0 && now.isAfter(lastChecked.plus(interval.toLong(), ChronoUnit.DAYS))) {
Settings.lastUpdateDay = now.epochSecond
if (Settings.useCIUpdateChannel) {
val curSha = BuildConfig.COMMIT_SHA
val branch = ghRequest(API_URL).execute {
@@ -37,6 +43,7 @@ object AppUpdater {
val changelog = runSuspendCatching {
val commitComparisonUrl = "$API_URL/compare/$curSha...$shortSha"
val result = ghRequest(commitComparisonUrl).executeAndParseAs<GithubCommitComparison>()
// TODO: Prettier format, Markdown?
result.commits.joinToString("\n") { "${it.commit.message} (@${it.author.name})" }
}.getOrDefault(workflowRun.title)
return Release(shortSha, changelog, archiveUrl)
@@ -77,3 +77,64 @@ fun DropDownPref(
}
}
}
@Composable
fun DropDownPrefInt(
title: String,
modifier: Modifier = Modifier,
summary: String? = null,
defaultValue: Int? = null,
onValueChange: ((Int) -> Unit)? = null,
useSelectedAsSummary: Boolean = false,
dropdownBackgroundColor: Color? = null,
textColor: Color = MaterialTheme.colorScheme.onBackground,
enabled: Boolean = true,
entries: Map<Int, String> = mapOf(),
) {
var expanded by rememberSaveable { mutableStateOf(false) }
fun edit(item: Map.Entry<Int, String>) {
expanded = false
onValueChange?.invoke(item.key)
}
Column {
TextPref(
title = title,
modifier = modifier,
summary = when {
useSelectedAsSummary && defaultValue != null -> entries[defaultValue]
useSelectedAsSummary && defaultValue == null -> "Not Set"
else -> summary
},
textColor = textColor,
enabled = enabled,
onClick = {
expanded = true
},
)
Box(
modifier = Modifier.padding(start = 16.dp),
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = if (dropdownBackgroundColor != null) Modifier.background(dropdownBackgroundColor) else Modifier,
) {
entries.forEach { item ->
DropdownMenuItem(
onClick = {
edit(item)
},
text = {
Text(
text = item.value,
style = MaterialTheme.typography.bodyLarge,
)
},
)
}
}
}
}
}
@@ -510,6 +510,13 @@
<string name="list_tile_thumb_size">详情模式下缩略图大小</string>
<string name="account_name">账户</string>
<string name="preload_thumb_aggressively">激进地预载缩略图</string>
<string name="auto_updates">自动检查更新</string>
<string name="update_frequency_never">从不</string>
<string name="update_frequency_daily">每天</string>
<string name="update_frequency_3days">每三天</string>
<string name="update_frequency_weekly">每周</string>
<string name="update_frequency_biweekly">每两周</string>
<string name="update_frequency_monthly">每月</string>
<string name="new_version_available">有新版本!</string>
<string name="already_latest_version">已是最新版本</string>
<!-- Tachiyomi Reader Start-->
+18
View File
@@ -320,4 +320,22 @@
<item>20</item>
<item>25</item>
</string-array>
<string-array name="update_frequency">
<item>@string/update_frequency_never</item>
<item>@string/update_frequency_daily</item>
<item>@string/update_frequency_3days</item>
<item>@string/update_frequency_weekly</item>
<item>@string/update_frequency_biweekly</item>
<item>@string/update_frequency_monthly</item>
</string-array>
<integer-array name="update_frequency_values">
<item>0</item>
<item>1</item>
<item>3</item>
<item>7</item>
<item>14</item>
<item>30</item>
</integer-array>
</resources>
+7
View File
@@ -573,6 +573,13 @@
<string name="list_tile_thumb_size">Thumb size in thumb mode</string>
<string name="account_name">Account</string>
<string name="preload_thumb_aggressively">Preload thumbs aggressively</string>
<string name="auto_updates">Automatically check for updates</string>
<string name="update_frequency_never">Never</string>
<string name="update_frequency_daily">Daily</string>
<string name="update_frequency_3days">Every 3 days</string>
<string name="update_frequency_weekly">Weekly</string>
<string name="update_frequency_biweekly">Biweekly</string>
<string name="update_frequency_monthly">Monthly</string>
<string name="new_version_available">New version available!</string>
<string name="already_latest_version">Already the latest version</string>
<!-- Tachiyomi Reader Start-->