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.
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.
CodePudding user response:
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:
Setup a
FileProvider
. This is how you generate aUri
from aFile
that your app owns that is suitable to send to thelaunch
of aCaptureVideo
Intent. You're the one who picks where the video is stored, not the camera app.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 yourvideoUri
, e.g., in a variable namedfile
(as per the note on the Launching an Intent documentation, you'll probably also want to store that inonSaveInstanceState()
in case your process is destroyed while the camera is up).Use the
FileProvider
APIs to generate a Uri from yourFile
and pass that to your call tolaunch
.
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)
- Change your call to
registerForActivityResult
to account for thesuccess
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
}
}