Skip to content

Instantly share code, notes, and snippets.

@dpmedeiros
Created February 14, 2018 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dpmedeiros/74d3bd3a50dddad4378bd03d38ad7a62 to your computer and use it in GitHub Desktop.
Save dpmedeiros/74d3bd3a50dddad4378bd03d38ad7a62 to your computer and use it in GitHub Desktop.
Extends Activity class to allow a caller to request a result from another activity and get notified via a callback.
package com.dpmedeiros.android.util
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.IntentSender
import android.support.annotation.MainThread
/**
* Extensions to Android's Activity class allowing a caller to request a result from another activity and get notified
* via a callback instead of having to implement logic in onActivityResult to handle the returned result.
*/
/**
* Callback type to receive a result code and intent.
*/
private typealias ResultCallback = (Int, Intent?) -> Unit
private data class Callbacks(val requestCode: Int,
val resultCallback: ResultCallback,
val lifecycleCallbacks: ActivityLifecycleCallbacks)
private val callbackMap = mutableMapOf<Activity, Callbacks>()
/**
* Request a result from an activity, via standard [Activity.startIntentSenderForResult] means.
* The activity must invoke [notifyResultCodeToListeners] from its [Activity.onActivityResult] callback to notify the
* awaiting callback passed into this function.
*
* If calling activity stops before the result is returned, the newly launched activity will be finished and
* [Activity.RESULT_CANCELED] will be returned in the [onResult] callback.
*
* If [Activity.startIntentSenderForResult] throws a [IntentSender.SendIntentException] this will invoke the
* [onResult] callback immediately with [Activity.RESULT_CANCELED]
*
* @param intentSender an intent sender describing the intent to request a result for
* @param requestCode request code to associate with the request
* @param flags additional flags to pass in the resolved intent. These may override flags in the [intentSender]
* @param onResult lambda to notify with the result. First parameter will be the result code and the second
* parameter will be the intent sent back (null if no intent available due to error)
*/
@JvmOverloads
@MainThread
fun Activity.requestResult(intentSender: IntentSender, requestCode: Int, flags: Int = 0, onResult: ResultCallback) {
try {
addRequestMapping(requestCode, onResult)
startIntentSenderForResult(
intentSender,
requestCode,
null,
flags,
flags,
0)
} catch (e: IntentSender.SendIntentException) {
onResult(Activity.RESULT_CANCELED, null)
}
}
/**
* Same as [requestResult] but for use with an intent. The activity will be started via
* [Activity.startActivityForResult]
*
* If [ActivityNotFoundException] is thrown this will invoke the [onResult] callback immediately with
* [Activity.RESULT_CANCELED]
*/
@MainThread
fun Activity.requestResult(intent: Intent, requestCode: Int, onResult: ResultCallback) {
addRequestMapping(requestCode, onResult)
try {
startActivityForResult(intent, requestCode)
} catch (e: ActivityNotFoundException) {
onResult(Activity.RESULT_CANCELED, null)
}
}
private fun Activity.addRequestMapping(requestCode: Int, onResult: ResultCallback) {
if (callbackMap[this] != null) throw IllegalStateException("Already requesting a result for activity $this")
val lifecycleCallback = object : ActivityLifecycleCallbacks() {
override fun onActivityStopped(activity: Activity) {
if (activity === this@addRequestMapping) {
finishActivity(requestCode)
removeRequest()
onResult(Activity.RESULT_CANCELED, null)
}
}
}
this.application.registerActivityLifecycleCallbacks(lifecycleCallback)
callbackMap[this] = Callbacks(requestCode, onResult, lifecycleCallback)
}
/**
* Call this from your Activity's [Activity.onActivityResult]. This is necessary to invoke waiting callbacks with the
* result.
*
* @param requestCode request code passed to [Activity.onActivityResult]
* @param resultCode result code passed to [Activity.onActivityResult]
* @param data intent passed to [Activity.onActivityResult]
*/
@MainThread
fun Activity.notifyResultCodeToListeners(requestCode: Int, resultCode: Int, data: Intent?) {
val callbacks = callbackMap[this]
if (callbacks?.requestCode == requestCode) {
callbacks.resultCallback(resultCode, data)
}
removeRequest()
}
private fun Activity.removeRequest() {
val callbacks = callbackMap.remove(this)
if (callbacks != null) {
application.unregisterActivityLifecycleCallbacks(callbacks.lifecycleCallbacks)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment