Check for app and extension update on every cold start (#3658)

This commit is contained in:
AntsyLich
2026-07-30 01:17:10 +06:00
committed by GitHub
parent fbf5ae82f7
commit d15a05602a
5 changed files with 5 additions and 97 deletions
+1
View File
@@ -19,6 +19,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
### Changed ### Changed
- Detect Shizuku with permission check ([@Small-Ku](https://github.com/Small-Ku)) ([#3565](https://github.com/mihonapp/mihon/pull/3565)) - Detect Shizuku with permission check ([@Small-Ku](https://github.com/Small-Ku)) ([#3565](https://github.com/mihonapp/mihon/pull/3565))
- Check for app and extension update on every cold start ([@AntsyLich](https://github.com/AntsyLich)) ([#3658](https://github.com/mihonapp/mihon/pull/3658))
### Improved ### Improved
- Library search ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#3554](https://github.com/mihonapp/mihon/pull/3554)) - Library search ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#3554](https://github.com/mihonapp/mihon/pull/3554))
@@ -143,7 +143,7 @@ class DomainModule : InjektModule {
} }
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) } addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
addFactory { GetApplicationRelease(get(), get()) } addFactory { GetApplicationRelease(get()) }
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) } addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
addFactory { TrackChapter(get(), get(), get(), get()) } addFactory { TrackChapter(get(), get(), get(), get()) }
@@ -1,53 +1,28 @@
package eu.kanade.tachiyomi.extension.api package eu.kanade.tachiyomi.extension.api
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.LoadResult import eu.kanade.tachiyomi.extension.model.LoadResult
import eu.kanade.tachiyomi.extension.util.ExtensionLoader import eu.kanade.tachiyomi.extension.util.ExtensionLoader
import mihon.domain.extension.interactor.UpdateExtensionStores import mihon.domain.extension.interactor.UpdateExtensionStores
import mihon.domain.extension.repository.ExtensionStoreRepository import mihon.domain.extension.repository.ExtensionStoreRepository
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.time.Instant
import kotlin.time.Duration.Companion.days
internal class ExtensionApi { internal class ExtensionApi {
private val repository: ExtensionStoreRepository by injectLazy() private val repository: ExtensionStoreRepository by injectLazy()
private val preferenceStore: PreferenceStore by injectLazy()
private val updateExtensionStores: UpdateExtensionStores by injectLazy() private val updateExtensionStores: UpdateExtensionStores by injectLazy()
private val extensionManager: ExtensionManager by injectLazy()
private val lastExtCheck: Preference<Long> by lazy {
preferenceStore.getLong(Preference.appStateKey("last_ext_check"), 0)
}
suspend fun findExtensions(): List<Extension.Available> { suspend fun findExtensions(): List<Extension.Available> {
return withIOContext { repository.fetchExtensions() } return withIOContext { repository.fetchExtensions() }
} }
suspend fun checkForUpdates( suspend fun checkForUpdates(context: Context) {
context: Context,
fromAvailableExtensionList: Boolean = false,
): List<Extension.Installed>? {
// Limit checks to once a day at most
if (!fromAvailableExtensionList &&
Instant.now().toEpochMilli() < lastExtCheck.get() + 1.days.inWholeMilliseconds
) {
return null
}
updateExtensionStores() updateExtensionStores()
val extensions = if (fromAvailableExtensionList) { val extensions = findExtensions()
extensionManager.availableExtensionsFlow.value
} else {
findExtensions().also { lastExtCheck.set(Instant.now().toEpochMilli()) }
}
val installedExtensions = ExtensionLoader.loadExtensions(context) val installedExtensions = ExtensionLoader.loadExtensions(context)
.filterIsInstance<LoadResult.Success>() .filterIsInstance<LoadResult.Success>()
@@ -68,7 +43,5 @@ internal class ExtensionApi {
if (extensionsWithUpdate.isNotEmpty()) { if (extensionsWithUpdate.isNotEmpty()) {
ExtensionUpdateNotifier(context).promptUpdates(extensionsWithUpdate.map { it.name }) ExtensionUpdateNotifier(context).promptUpdates(extensionsWithUpdate.map { it.name })
} }
return extensionsWithUpdate
} }
} }
@@ -1,34 +1,14 @@
package tachiyomi.domain.release.interactor package tachiyomi.domain.release.interactor
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.model.Release
import tachiyomi.domain.release.service.ReleaseService import tachiyomi.domain.release.service.ReleaseService
import java.time.Instant
import java.time.temporal.ChronoUnit
class GetApplicationRelease( class GetApplicationRelease(
private val service: ReleaseService, private val service: ReleaseService,
private val preferenceStore: PreferenceStore,
) { ) {
private val lastChecked: Preference<Long> by lazy {
preferenceStore.getLong(Preference.appStateKey("last_app_check"), 0)
}
suspend fun await(arguments: Arguments): Result { suspend fun await(arguments: Arguments): Result {
val now = Instant.now()
// Limit checks to once every 3 days at most
val nextCheckTime = Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS)
if (!arguments.forceCheck && now.isBefore(nextCheckTime)) {
return Result.NoNewUpdate
}
val release = service.latest(arguments) ?: return Result.NoNewUpdate val release = service.latest(arguments) ?: return Result.NoNewUpdate
lastChecked.set(now.toEpochMilli())
// Check if latest version is different from current version // Check if latest version is different from current version
val isNewVersion = isNewVersion( val isNewVersion = isNewVersion(
arguments.isPreview, arguments.isPreview,
@@ -2,39 +2,27 @@ package tachiyomi.domain.release.interactor
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.model.Release
import tachiyomi.domain.release.service.ReleaseService import tachiyomi.domain.release.service.ReleaseService
import java.time.Instant
class GetApplicationReleaseTest { class GetApplicationReleaseTest {
private lateinit var getApplicationRelease: GetApplicationRelease private lateinit var getApplicationRelease: GetApplicationRelease
private lateinit var releaseService: ReleaseService private lateinit var releaseService: ReleaseService
private lateinit var preference: Preference<Long>
@BeforeEach @BeforeEach
fun beforeEach() { fun beforeEach() {
val preferenceStore = mockk<PreferenceStore>()
preference = mockk()
every { preferenceStore.getLong(any(), any()) } returns preference
releaseService = mockk() releaseService = mockk()
getApplicationRelease = GetApplicationRelease(releaseService, preferenceStore) getApplicationRelease = GetApplicationRelease(releaseService)
} }
@Test @Test
fun `When has update but is preview expect new update`() = runTest { fun `When has update but is preview expect new update`() = runTest {
every { preference.get() } returns 0
every { preference.set(any()) }.answers { }
val release = Release( val release = Release(
"r2000", "r2000",
"info", "info",
@@ -61,9 +49,6 @@ class GetApplicationReleaseTest {
@Test @Test
fun `When has update expect new update`() = runTest { fun `When has update expect new update`() = runTest {
every { preference.get() } returns 0
every { preference.set(any()) }.answers { }
val release = Release( val release = Release(
"v2.0.0", "v2.0.0",
"info", "info",
@@ -90,9 +75,6 @@ class GetApplicationReleaseTest {
@Test @Test
fun `When has no update expect no new update`() = runTest { fun `When has no update expect no new update`() = runTest {
every { preference.get() } returns 0
every { preference.set(any()) }.answers { }
val release = Release( val release = Release(
"v1.0.0", "v1.0.0",
"info", "info",
@@ -114,32 +96,4 @@ class GetApplicationReleaseTest {
result shouldBe GetApplicationRelease.Result.NoNewUpdate result shouldBe GetApplicationRelease.Result.NoNewUpdate
} }
@Test
fun `When now is before three days expect no new update`() = runTest {
every { preference.get() } returns Instant.now().toEpochMilli()
every { preference.set(any()) }.answers { }
val release = Release(
"v1.0.0",
"info",
"http://example.com/release_link",
"http://example.com/release_link.apk",
)
coEvery { releaseService.latest(any()) } returns release
val result = getApplicationRelease.await(
GetApplicationRelease.Arguments(
isFoss = false,
isPreview = false,
commitCount = 0,
versionName = "v2.0.0",
repository = "test",
),
)
coVerify(exactly = 0) { releaseService.latest(any()) }
result shouldBe GetApplicationRelease.Result.NoNewUpdate
}
} }