This is the second post in a two-part series on how we can convert and consume active results using corotins.
In the first part, we were able to create a component Which allowed us to convert the contract of the result of the activity RequestMultiplePermissions To Coroutine, while still being able to handle entertainment and death-processing activity scenarios.
While reviewing the code we had for ourselves PermissionManager
Class, the only specific parts related to permissions were:
- The show of
RequestMultiplePermissions
. - Mapping the result to our defined opaque class.
- Keeping the list of permissions for the saved state and using it as a flag to decide if there is a pending action.
So in order to design our component that can be used to consume all the results of the activity, we just need to generalize or remove the points above.
The first two points are straight forward, since we’re aiming for a generic component here, we’ll just remove them, so we’ll move the instance of ActivityResultContract
To our function, and we will make it return the same type as defined in the contract. Which means there is a signature for our main function like this:
suspend fun <I, O, C : ActivityResultContract<I, O>> requestResult( contract: C, input: I ): O?
Regarding the third point, we have many ways to address it:
- Use a simple boolean to tell us if there is a pending action. This will work in most cases, but it has one drawback, if death occurs in the process, and on the ViewModel side we do not handle it, our component will still think there is a pending action, i.e. if we ask for another type of results, it will get stuck.
- By keeping the class name of the contract, and comparing with it, this is a simple way to do it, and it will work for most cases, although it may still break in a very specific case: if the ViewModel does not handle the process of death, and we seek another result using the same type Of a contract, but using a different input, we will probably return the wrong result.
- By keeping the class name of both the contract and the input, and although this is the ultimate solution, comparing the input to a saved value will not be easy, as it is a generic type, and we do not know if it is valid
equals
Application or not.
I chose the second option, because the failure scenario is a very remote corner case, and when using SavedStateHandle
True to keep the situation, it will not happen at all, tell me what you think about this choice in the comments.
With all this, we can get our component now:
After I started the article, I decided to publish this component as a library for anyone interested, it is available at the same Repo.
You can request the result of the activity using a low-level API:
Or using extensions for the built-in ActivityResult Contracts
God Example The app has examples of its use for permissions and other activity results, and it has an example of its use for custom ActivityResultContract
.
If you have any comments or questions regarding the code or API, please release them in the comments section, I hope the directory can be useful to some people.