Activity result

AndroidX library contains Activity Result API to launch activity and handle its result.

Add dependency
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.

Custom contract
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.

Contract registration
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

Call contract
button.setOnClickListener {
    contractReg.launch(inValue)
}

Activity that produces the result must set the result before finishing

Set result example
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.
Take picture contract
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.

ActivityResultRegistry example

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 example
@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.

Old way example