Skip to content

Instantly share code, notes, and snippets.

@RicardAparicio
Last active November 11, 2022 12:04
Show Gist options
  • Save RicardAparicio/2cd3f633e5f706268a22b8c42312f6eb to your computer and use it in GitHub Desktop.
Save RicardAparicio/2cd3f633e5f706268a22b8c42312f6eb to your computer and use it in GitHub Desktop.
Class to get result from an Activity / Fragment with Kotlin Coroutines using Result API's
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.launch
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.CompletableDeferred
sealed class IntentResult(open val intent: Intent?) {
data class Ok(override val intent: Intent?) : IntentResult(intent)
data class Cancelled(override val intent: Intent?) : IntentResult(intent)
}
class IntentResultLauncher {
private val observer: DefaultLifecycleObserver = Observer(
create = { owner ->
launcher = registry?.register(REGISTRY_KEY, owner, Contract { requireNotNull(intent) }) { result: IntentResult ->
completableDeferred.complete(result)
}
},
destroy = {
completableDeferred.cancel()
launcher?.unregister()
}
)
private var completableDeferred: CompletableDeferred<IntentResult> = CompletableDeferred()
private var registry: ActivityResultRegistry? = null
private var intent: Intent? = null
private var launcher: ActivityResultLauncher<Unit>? = null
fun register(registry: ActivityResultRegistry): DefaultLifecycleObserver {
this.registry = registry
return observer
}
suspend fun launch(intent: Intent): IntentResult {
this.intent = intent
requireNotNull(launcher) {
"You must call #register(ActivityResultRegistry) before launch!"
}.launch()
return with(completableDeferred) {
cancel()
completableDeferred = CompletableDeferred()
await()
}
}
private class Observer(private val create: (LifecycleOwner) -> Unit, private val destroy: (LifecycleOwner) -> Unit) : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
create(owner)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
destroy(owner)
}
}
private class Contract(private val intent: () -> Intent) : ActivityResultContract<Unit, IntentResult>() {
override fun createIntent(context: Context, input: Unit): Intent = intent()
override fun parseResult(resultCode: Int, intent: Intent?): IntentResult =
if (resultCode == Activity.RESULT_OK) IntentResult.Ok(intent) else IntentResult.Cancelled(intent)
}
companion object {
private const val REGISTRY_KEY = "resultRegistry-key"
}
}
@RicardAparicio
Copy link
Author

RicardAparicio commented Oct 29, 2022

You have to register the IntentResultLauncher before lifecycle event ON_START (#onCreate should be a good place) because new Result API's require it.

val resultLauncher = IntentResultLauncher()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    lifecycle.addObserver(
        resultLauncher.register(requireActivity().activityResultRegistry)
    )
}

And then just call

suspend fun getResultForIntent(intent: Intent) {
    when (val result = resultLauncher.launch(intent)) {
        is IntentResult.Cancelled -> // do whatever...
        is IntentResult.Ok -> // do whatever...
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment