Bungles
Bungles

Reputation: 2257

How can I use Android AppUpdateManager appUpdateInfoTask synchronously?

Using Google's instructions, I wrote this function to determine if an app update is available in the play store.

public fun isAppUpdateAvailable(): Boolean
{
    var isAvailable = false
    val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(applicationContext)
    val appUpdateInfoTask: Task<AppUpdateInfo> = appUpdateManager.appUpdateInfo

    appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
        if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
            isAvailable = true
        }
    }

    return isAvailable
}

I call this from my MainActivity.onCreate() because I need to launch a different activity instead before starting the update flow. That other activity starts the update flow after some user interaction.

This doesn't work of course, because addOnSuccessListener executes asynchronously so isAppUpdateAvailable() always returns false. Is there a way to implement this using co-routines or something?

Upvotes: 0

Views: 51

Answers (2)

hasan.z
hasan.z

Reputation: 349

I’m not sure if the Google Update API itself supports coroutines, like if it provides a method similar to this:

appUpdateManager.appUpdateInfo.await()

However, using callbackFlow in coroutines, you can easily convert the callback to a flow and work with it. Here’s an example:

fun isAppUpdateAvailableFlow() = callbackFlow<Boolean> {
    val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(applicationContext)
    val appUpdateInfoTask = appUpdateManager.appUpdateInfo

    val listener = { appUpdateInfo: AppUpdateInfo ->
        val isAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
        trySend(isAvailable) // Send the result to the flow
        close() // Close the flow after emitting the result
    }

    appUpdateInfoTask.addOnSuccessListener(listener).addOnFailureListener { exception ->
        close(exception) // Close the flow with an error if the task fails
    }

    // Suspend the flow until it's closed
    awaitClose()
}

You can then easily collect or call first on it, like this:

lifecycleScope.launch {
    val isUpdateAvailable = isAppUpdateAvailableFlow().first()
    if (isUpdateAvailable) {
        // Handle the case when an update is available
    } else {
        // Proceed with normal flow
    }
}

Upvotes: 0

Gabe Sechan
Gabe Sechan

Reputation: 93708

You can make a suspend function that returns the result, but its' still going to run asynchronously, because the code is asynchronous. And assuming you call this in onCreate, onCreate is not a suspend function and cannot call suspend functions. Nor can it use runBlocking to launch it- doing so would stop onCreate from returning and cause an ANR.

The answer to this is to show a loading screen or spinner until this function returns, then display the correct UI for the result. This can either be done via fragments, by loading completely different UIs in the same Activity without fragments, or by using a launcher Activity that checks this state and then launches one of the two other Activities based on the result.

Upvotes: 0

Related Questions