Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Boolean>
get() = flexibleUpdates.downloaded

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,23 @@ class ForcedUpdateTest {

private fun idleMain() = shadowOf(Looper.getMainLooper()).idle()

private fun flags(
private fun resolved(
minimumVersion: Int?,
override: FieldOverride<Int?> = FieldOverride.None,
isStaff: Boolean = false,
) {
val resolved = mockk<ResolvedUserFlags> {
every { [email protected] } returns ResolvedFlag(minimumVersion, override)
every { [email protected] } returns ResolvedFlag(isStaff, FieldOverride.None)
}
every { userFlags.resolvedFlags } returns MutableStateFlow(resolved)
): ResolvedUserFlags = mockk {
every { [email protected] } returns ResolvedFlag(minimumVersion, override)
every { [email protected] } returns ResolvedFlag(isStaff, FieldOverride.None)
}

private fun flags(
minimumVersion: Int?,
override: FieldOverride<Int?> = FieldOverride.None,
isStaff: Boolean = false,
): MutableStateFlow<ResolvedUserFlags> =
MutableStateFlow(resolved(minimumVersion, override, isStaff))
.also { every { userFlags.resolvedFlags } returns it }

private fun TestScope.controller() = GooglePlayAppUpdateController(
context = activity,
versionInfo = VersionInfo(versionCode = currentVersion),
Expand Down Expand Up @@ -128,6 +133,30 @@ class ForcedUpdateTest {
assertIs<NoUpdateAvailableException>(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)
Expand Down
Loading