How do I capture the URI from a registerActivityResult?

Issue

I’m currently attempting to write a view in an android app that will open a camera on a button press, let the user record a video, and stash the URI in a member attached to said view. I’m very hopeful this is possible, but unsure because I can’t find many docs regarding the issue.

I’m using the top answer here as a reference for capturing the URI, and it’s very comprehensive, but not working in my case for reasons of which I am unsure.

I declare my variable at the top of the file as so

private var videoUri: Uri? = null

The rest of the relevant code happens here:

val recordVideoResult = registerForActivityResult(ActivityResultContracts.CaptureVideo()) { uri: Uri? ->
    uri?.let {
        this.videoUri = uri
    }
}

val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
    if (isGranted) {
        launchCamera()
    } else {
        Toast.makeText(this, "Can't open camera because permission is denied. Please provide access in settings.", Toast.LENGTH_SHORT).show()
    }
}


fun launchCamera() {
    if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) { // First check if camera is available in the device
        when {
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> {
                recordVideoResult.launch(this.uri)
            }
            else -> {
                // You can directly ask for the permission.
                // The registered ActivityResultCallback gets the result of this request.
                requestPermissionLauncher.launch(
                    Manifest.permission.CAMERA
                )
            }
        }
    }
}

My IDE and compiler yell at me with the below error, which looks like a bug to me, but will not go away.
enter image description here

I’m very unsure what I’m doing wrong. Forgive me if it’s incredibly obvious, I’ve been writing Kotlin for less than a week.

Solution

You’ve got the input and output of CaptureVideo mixed up. The CaptureVideo contract says:

An ActivityResultContract to take a video saving it into the provided content-Uri.

Returns true if the video was saved into the given Uri.

So the input is a Uri – that’s what you need to pass to launch. The output is a Boolean. This can be confirmed by looking at the class itself, which shows the complete type – ActivityResultContract<Uri, Boolean>.

This means what you actually need to do is:

  1. Setup a FileProvider. This is how you generate a Uri from a File that your app owns that is suitable to send to the launch of a CaptureVideo Intent. You’re the one who picks where the video is stored, not the camera app.

  2. Generate the appropriate File associated with where you want to save your video and store it as a variable in your class like you were previously doing with your videoUri, e.g., in a variable named file (as per the note on the Launching an Intent documentation, you’ll probably also want to store that in onSaveInstanceState() in case your process is destroyed while the camera is up).

  3. Use the FileProvider APIs to generate a Uri from your File and pass that to your call to launch.

val contentUri = FileProvider.getUriForFile(
  this, // your Activity or 
  "com.example.myapp.fileprovider", // The android:authorities value from your manifest 
  file) // The File you want to store the video into
recordVideoResult.launch(contentUri)
  1. Change your call to registerForActivityResult to account for the success Boolean result:
val recordVideoResult = registerForActivityResult(ActivityResultContracts.CaptureVideo()) { success ->
    if (success) {
      // Now your file contains the fully captured video
    } else {
      // The user cancelled taking a video
    }
}

Answered By – ianhanniballake

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published