Created
February 14, 2018 17:59
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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