Activity result
AndroidX library contains Activity Result API to launch activity and handle its result.
implementation ("androidx.activity:activity-ktx:1.2.3")
implementation ("androidx.fragment:fragment-ktx:1.3.4")
The ActivityResultContract interface allows to specify that an activity can be called with an input of type I and produce an output of type O.
class CustomContract : ActivityResultContract<Int, String?>() {
override fun createIntent(context: Context, input: Int?): Intent {
var intent = Intent(context, ActivityForResult::class.java)
intent.putExtra("key", input)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): String? = when {
resultCode != Activity.RESULT_OK -> null // action is cancelled
else -> intent?.getStringExtra("resultData") // return the data
}
}
Then you must register contract in your activity or fragment.
private val contractReg = registerForActivityResult(CustomContract()) {resultStr ->
if (resultStr == null)
// ... do something when action canceled
else
// ... do something with result
}
And last step is calling contract where you want
button.setOnClickListener {
contractReg.launch(inValue)
}
Activity that produces the result must set the result before finishing
class SecondActivity: AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
buttonApply.setOnClickListener(object : OnClickListener() {
fun onClick(arg0: View?) {
val message: String = editText1.getText().toString()
val intent = Intent()
intent.putExtra("resultData", message)
setResult(Activity.RESULT_OK,intent)
finish() //finishing activity
}
})
}
//...
}
built-in contracts
There are some built-in contracts defined in ActivityResultContracts class. For example
- CaptureVideo - contract to take a video saving it into the provided content-Uri.
- CreateDocument - contract to prompt the user to select a path for creating a new document, returning the content: Uri of the item that was created.
- TakePicture - contract to take a picture saving it into the provided content-Uri.
- TakePicturePreview - contract to take small a picture preview, returning it as a Bitmap.
- PickContact - contract to pick a contact from the contacts app.
- RequestPermission - contract to request a permission ( example in permission article ).
- RequestMultiplePermissions - contract to request permissions.
private val takePicture =
registerForActivityResult(ActivityResultContracts.TakePicture()) {bitmap ->
bitmap?.apply {
imageView.setImageBitmap(this)
}
}
button.setOnClickListener {
var imageUri: Uri? = null
takePicture.launch(imageUri)
}
ActivityResultRegistry
You can also receive the activity result in a separate class that does not implement ActivityResultCaller by using ActivityResultRegistry directly.
You can use specific ActivityResultRegistry for testing. In this case you should implement the onLaunch() method replacing startActivityForResult() call with dispatchResult(), providing the exact results you want to use in your test.
@Test
fun activityResultTest {
// Create an expected result Bitmap
val expectedResult = Bitmap.createBitmap(1, 1, Bitmap.Config.RGBA_F16)
// Create the test ActivityResultRegistry
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
// Use the launchFragmentInContainer method that takes a
// lambda to construct the Fragment with the testRegistry
with(launchFragmentInContainer { MyFragment(testRegistry) }) {
onFragment { fragment ->
// Trigger the ActivityResultLauncher
fragment.takePicture()
// Verify the result is set
assertThat(fragment.thumbnailLiveData.value)
.isSameInstanceAs(expectedResult)
}
}
}
old way
In old way you must start activity with the startActivityForResult() method and override the onActivityResult() method to handle result.