I use the following codes for capturing video. Everything is ok but after capturing video, the registerForActivityResult(ActivityResultContracts.TakeVideo())
is always null. The captured video exists in the given path. In what ways I can resolve this issue?
private fun openCameraForMovie() {
val packageManager = requireContext().packageManager
Intent(MediaStore.ACTION_VIDEO_CAPTURE).also { takeVideoIntent ->
takeVideoIntent.resolveActivity(packageManager)?.also {
startRecording()
}
}
}
private fun startRecording() {
try {
val root = File(
requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
.toString() + "/media/"
)
if (!root.exists()) {
root.mkdir()
}
val videoName = "vid_" + System.currentTimeMillis() + ".mp4"
val sdImageMainDirectory = File(root, videoName)
var fileUri: Uri? = FileProvider.getUriForFile(
requireContext(),
context?.applicationContext?.packageName + ".provider",
sdImageMainDirectory
) ?: return
mViewModel.videoUri.postValue(fileUri)
} catch (ex: Exception) {
Timber.d("startRecording ${ex.localizedMessage}")
}
}
This code is always null:
private val recordVideoResult =
registerForActivityResult(ActivityResultContracts.TakeVideo()) { it ->
try {
if (it != null) {
Timber.d(">>>> Video Bitmap: $it")
}
} catch (ex: Exception) {
Timber.d(">>>> Video Bitmap: ${ex.localizedMessage}")
}
}
An
ActivityResultContract
to take a video saving it into the provided content-Uri
.Returns a thumbnail.
Source TakeVideo docs, emphasis mine.
You don't want a thumbnail (which is optional anyway), you want the Uri
that points to the actual video. But you already know the Uri
, because you supplied it as the input.
There are several steps required to make this work:
Store the input Uri
in a variable and correctly handle saved state, so your app knows you've been recording video when you get back from the Camera app.
Write custom TakeVideo
contract which returns if video capture was successful. Checking for non-null preview Bitmap
cannot be used, because preview is optional.
It may look something like this:
private AtomicReference<Uri> recordingUri = new AtomicReference<>();
private ActivityResultLauncher<Uri> recordVideoResult =
registerForActivityResult(new TakeVideo2(), success -> {
final Uri uri = recordingUri.getAndSet(null);
if (success) {
// Do something with the video.
}
});
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
recordingUri.set(savedInstanceState.getParcelable("recordingUri"));
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
// ...
outState.putParcelable("recordingUri", recordingUri.get());
}
private void startRecording() {
final Uri uri = // ...;
recordingUri.set(uri);
recordVideoResult.launch(uri);
}
public static class TakeVideo2 extends ActivityResultContract<Uri, Boolean> {
@CallSuper
@NonNull
@Override
public Intent createIntent(@NonNull Context context, @NonNull Uri input) {
return new Intent(MediaStore.ACTION_VIDEO_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, input);
}
@Nullable
@Override
public final SynchronousResult<Boolean> getSynchronousResult(@NonNull Context context,
@NonNull Uri input) {
return null;
}
@Override
public final Boolean parseResult(int resultCode, @Nullable Intent intent) {
return resultCode == Activity.RESULT_OK;
}
}