diff --git a/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayAppUpdateController.kt b/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayAppUpdateController.kt index fb18738745..4df0455a53 100644 --- a/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayAppUpdateController.kt +++ b/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayAppUpdateController.kt @@ -27,6 +27,9 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -58,6 +61,20 @@ class GooglePlayAppUpdateController internal constructor( update?.takeIf { it.isToastEligible(lastShown) } }.stateIn(scope, SharingStarted.Eagerly, null) + init { + // The resume check reads the minimum version cached from the last fetch, and the fetch that + // runs on the same resume lands after it. Checking again when the minimum moves is what + // lets a minimum raised while the app sat in the background block it on return. The first + // value is left to the resume check. + scope.launch { + userFlags.resolvedFlags + .map { it.minimumVersion.effectiveValue } + .distinctUntilChanged() + .drop(1) + .collect { checkForUpdate() } + } + } + override val updateDownloaded: StateFlow get() = flexibleUpdates.downloaded diff --git a/apps/flipcash/shared/appupdates/src/test/kotlin/com/flipcash/app/updates/internal/ForcedUpdateTest.kt b/apps/flipcash/shared/appupdates/src/test/kotlin/com/flipcash/app/updates/internal/ForcedUpdateTest.kt index 232eb34654..7b7f73e0fd 100644 --- a/apps/flipcash/shared/appupdates/src/test/kotlin/com/flipcash/app/updates/internal/ForcedUpdateTest.kt +++ b/apps/flipcash/shared/appupdates/src/test/kotlin/com/flipcash/app/updates/internal/ForcedUpdateTest.kt @@ -45,18 +45,23 @@ class ForcedUpdateTest { private fun idleMain() = shadowOf(Looper.getMainLooper()).idle() - private fun flags( + private fun resolved( minimumVersion: Int?, override: FieldOverride = FieldOverride.None, isStaff: Boolean = false, - ) { - val resolved = mockk { - every { this@mockk.minimumVersion } returns ResolvedFlag(minimumVersion, override) - every { this@mockk.isStaff } returns ResolvedFlag(isStaff, FieldOverride.None) - } - every { userFlags.resolvedFlags } returns MutableStateFlow(resolved) + ): ResolvedUserFlags = mockk { + every { this@mockk.minimumVersion } returns ResolvedFlag(minimumVersion, override) + every { this@mockk.isStaff } returns ResolvedFlag(isStaff, FieldOverride.None) } + private fun flags( + minimumVersion: Int?, + override: FieldOverride = FieldOverride.None, + isStaff: Boolean = false, + ): MutableStateFlow = + MutableStateFlow(resolved(minimumVersion, override, isStaff)) + .also { every { userFlags.resolvedFlags } returns it } + private fun TestScope.controller() = GooglePlayAppUpdateController( context = activity, versionInfo = VersionInfo(versionCode = currentVersion), @@ -128,6 +133,30 @@ class ForcedUpdateTest { assertIs(start(controller).exceptionOrNull()) } + @Test + fun `a minimum raised after the check shows the gate without another resume`() = runTest { + fake.setUpdateAvailable(currentVersion + 1) + val flags = flags(minimumVersion = currentVersion) + val controller = controller() + controller.check() + assertNull(controller.availableUpdate.value) + + flags.value = resolved(minimumVersion = currentVersion + 1) + idleMain() + + assertEquals(UpdateAvailability.UPDATE_AVAILABLE, controller.availableUpdate.value?.updateAvailability) + } + + @Test + fun `building the controller checks nothing on its own`() = runTest { + fake.setUpdateAvailable(currentVersion + 1) + flags(minimumVersion = currentVersion + 1) + val controller = controller() + idleMain() + + assertNull(controller.availableUpdate.value) + } + @Test fun `starting the update opens Play's immediate flow`() = runTest { fake.setUpdateAvailable(currentVersion + 1)