Add in-app app update check

This commit is contained in:
arkon 2021-08-13 18:18:53 -04:00
parent 96a64c7bd2
commit f23f22ab01
8 changed files with 97 additions and 68 deletions

View file

@ -269,6 +269,7 @@ class PreferencesHelper(val context: Context) {
fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0) fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0)
fun lastAppCheck() = flowPrefs.getLong("last_app_check", 0)
fun lastExtCheck() = flowPrefs.getLong("last_ext_check", 0) fun lastExtCheck() = flowPrefs.getLong("last_ext_check", 0)
fun searchPinnedSourcesOnly() = prefs.getBoolean(Keys.searchPinnedSourcesOnly, false) fun searchPinnedSourcesOnly() = prefs.getBoolean(Keys.searchPinnedSourcesOnly, false)

View file

@ -1,16 +1,19 @@
package eu.kanade.tachiyomi.data.updater package eu.kanade.tachiyomi.data.updater
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withIOContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date
class GithubUpdateChecker { class AppUpdateChecker {
private val networkService: NetworkHelper by injectLazy() private val networkService: NetworkHelper by injectLazy()
private val preferences: PreferencesHelper by injectLazy()
private val repo: String by lazy { private val repo: String by lazy {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
@ -20,18 +23,20 @@ class GithubUpdateChecker {
} }
} }
suspend fun checkForUpdate(): GithubUpdateResult { suspend fun checkForUpdate(): AppUpdateResult {
return withIOContext { return withIOContext {
networkService.client networkService.client
.newCall(GET("https://api.github.com/repos/$repo/releases/latest")) .newCall(GET("https://api.github.com/repos/$repo/releases/latest"))
.await() .await()
.parseAs<GithubRelease>() .parseAs<GithubRelease>()
.let { .let {
preferences.lastAppCheck().set(Date().time)
// Check if latest version is different from current version // Check if latest version is different from current version
if (isNewVersion(it.version)) { if (isNewVersion(it.version)) {
GithubUpdateResult.NewUpdate(it) AppUpdateResult.NewUpdate(it)
} else { } else {
GithubUpdateResult.NoNewUpdate AppUpdateResult.NoNewUpdate
} }
} }
} }

View file

@ -0,0 +1,6 @@
package eu.kanade.tachiyomi.data.updater
sealed class AppUpdateResult {
class NewUpdate(val release: GithubRelease) : AppUpdateResult()
object NoNewUpdate : AppUpdateResult()
}

View file

@ -1,6 +0,0 @@
package eu.kanade.tachiyomi.data.updater
sealed class GithubUpdateResult {
class NewUpdate(val release: GithubRelease) : GithubUpdateResult()
object NoNewUpdate : GithubUpdateResult()
}

View file

@ -16,9 +16,9 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
override fun doWork() = runBlocking { override fun doWork() = runBlocking {
try { try {
val result = GithubUpdateChecker().checkForUpdate() val result = AppUpdateChecker().checkForUpdate()
if (result is GithubUpdateResult.NewUpdate) { if (result is AppUpdateResult.NewUpdate) {
UpdaterNotifier(context).promptUpdate(result.release.getDownloadLink()) UpdaterNotifier(context).promptUpdate(result.release.getDownloadLink())
} }
Result.success() Result.success()

View file

@ -35,6 +35,8 @@ import eu.kanade.tachiyomi.Migrations
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.preference.asImmediateFlow import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
import eu.kanade.tachiyomi.databinding.MainActivityBinding import eu.kanade.tachiyomi.databinding.MainActivityBinding
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.ui.base.activity.BaseViewBindingActivity import eu.kanade.tachiyomi.ui.base.activity.BaseViewBindingActivity
@ -52,6 +54,7 @@ import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.more.MoreController import eu.kanade.tachiyomi.ui.more.MoreController
import eu.kanade.tachiyomi.ui.more.NewUpdateDialogController
import eu.kanade.tachiyomi.ui.recent.history.HistoryController import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.ui.setting.SettingsMainController import eu.kanade.tachiyomi.ui.setting.SettingsMainController
@ -334,19 +337,32 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
getExtensionUpdates()
}
private fun setExtensionsBadge() { checkForExtensionUpdates()
val updates = preferences.extensionUpdatesCount().get() if (BuildConfig.INCLUDE_UPDATER) {
if (updates > 0) { checkForAppUpdates()
nav.getOrCreateBadge(R.id.nav_browse).number = updates
} else {
nav.removeBadge(R.id.nav_browse)
} }
} }
private fun getExtensionUpdates() { private fun checkForAppUpdates() {
// Limit checks to once a day at most
if (Date().time < preferences.lastAppCheck().get() + TimeUnit.DAYS.toMillis(1)) {
return
}
lifecycleScope.launchIO {
try {
val result = AppUpdateChecker().checkForUpdate()
if (result is AppUpdateResult.NewUpdate) {
NewUpdateDialogController(result).showDialog(router)
}
} catch (e: Exception) {
Timber.e(e)
}
}
}
private fun checkForExtensionUpdates() {
// Limit checks to once a day at most // Limit checks to once a day at most
if (Date().time < preferences.lastExtCheck().get() + TimeUnit.DAYS.toMillis(1)) { if (Date().time < preferences.lastExtCheck().get() + TimeUnit.DAYS.toMillis(1)) {
return return
@ -362,6 +378,15 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
} }
} }
private fun setExtensionsBadge() {
val updates = preferences.extensionUpdatesCount().get()
if (updates > 0) {
nav.getOrCreateBadge(R.id.nav_browse).number = updates
} else {
nav.removeBadge(R.id.nav_browse)
}
}
private fun handleIntentAction(intent: Intent): Boolean { private fun handleIntentAction(intent: Intent): Boolean {
val notificationId = intent.getIntExtra("notificationId", -1) val notificationId = intent.getIntExtra("notificationId", -1)
if (notificationId > -1) { if (notificationId > -1) {

View file

@ -1,16 +1,10 @@
package eu.kanade.tachiyomi.ui.more package eu.kanade.tachiyomi.ui.more
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
import eu.kanade.tachiyomi.data.updater.GithubUpdateResult import eu.kanade.tachiyomi.data.updater.AppUpdateResult
import eu.kanade.tachiyomi.data.updater.UpdaterService
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
@ -33,12 +27,10 @@ import java.util.TimeZone
class AboutController : SettingsController(), NoToolbarElevationController { class AboutController : SettingsController(), NoToolbarElevationController {
private val updateChecker by lazy { GithubUpdateChecker() } private val updateChecker by lazy { AppUpdateChecker() }
private val dateFormat: DateFormat = preferences.dateFormat() private val dateFormat: DateFormat = preferences.dateFormat()
private val isUpdaterEnabled = BuildConfig.INCLUDE_UPDATER
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_about titleRes = R.string.pref_category_about
@ -60,7 +52,7 @@ class AboutController : SettingsController(), NoToolbarElevationController {
} }
} }
} }
if (isUpdaterEnabled) { if (BuildConfig.INCLUDE_UPDATER) {
preference { preference {
key = "pref_about_check_for_updates" key = "pref_about_check_for_updates"
titleRes = R.string.check_for_updates titleRes = R.string.check_for_updates
@ -103,14 +95,10 @@ class AboutController : SettingsController(), NoToolbarElevationController {
launchNow { launchNow {
try { try {
when (val result = updateChecker.checkForUpdate()) { when (val result = updateChecker.checkForUpdate()) {
is GithubUpdateResult.NewUpdate -> { is AppUpdateResult.NewUpdate -> {
val body = result.release.info NewUpdateDialogController(result).showDialog(router)
val url = result.release.getDownloadLink()
// Create confirmation window
NewUpdateDialogController(body, url).showDialog(router)
} }
is GithubUpdateResult.NoNewUpdate -> { is AppUpdateResult.NoNewUpdate -> {
activity?.toast(R.string.update_check_no_new_updates) activity?.toast(R.string.update_check_no_new_updates)
} }
} }
@ -121,34 +109,6 @@ class AboutController : SettingsController(), NoToolbarElevationController {
} }
} }
class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundle) {
constructor(body: String, url: String) : this(
bundleOf(BODY_KEY to body, URL_KEY to url)
)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.update_check_notification_update_available)
.setMessage(args.getString(BODY_KEY) ?: "")
.setPositiveButton(R.string.update_check_confirm) { _, _ ->
val appContext = applicationContext
if (appContext != null) {
// Start download
val url = args.getString(URL_KEY) ?: ""
UpdaterService.start(appContext, url)
}
}
.setNegativeButton(R.string.update_check_ignore, null)
.create()
}
private companion object {
const val BODY_KEY = "NewUpdateDialogController.body"
const val URL_KEY = "NewUpdateDialogController.key"
}
}
private fun getFormattedBuildTime(): String { private fun getFormattedBuildTime(): String {
return try { return try {
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)

View file

@ -0,0 +1,38 @@
package eu.kanade.tachiyomi.ui.more
import android.app.Dialog
import android.os.Bundle
import androidx.core.os.bundleOf
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
import eu.kanade.tachiyomi.data.updater.UpdaterService
import eu.kanade.tachiyomi.ui.base.controller.DialogController
class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundle) {
constructor(update: AppUpdateResult.NewUpdate) : this(
bundleOf(BODY_KEY to update.release.info, URL_KEY to update.release.getDownloadLink())
)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.update_check_notification_update_available)
.setMessage(args.getString(BODY_KEY) ?: "")
.setPositiveButton(R.string.update_check_confirm) { _, _ ->
val appContext = applicationContext
if (appContext != null) {
// Start download
val url = args.getString(URL_KEY) ?: ""
UpdaterService.start(appContext, url)
}
}
.setNegativeButton(R.string.update_check_ignore, null)
.create()
}
private companion object {
const val BODY_KEY = "NewUpdateDialogController.body"
const val URL_KEY = "NewUpdateDialogController.key"
}
}