handle storage location
- add MediaOutputHelper - to create OutputStream for photos - to create FileDescriptor for videos (currently requires API 26+)
This commit is contained in:
parent
e691fc1e8c
commit
79f9267383
|
@ -23,6 +23,7 @@ import com.simplemobiletools.camera.R
|
||||||
import com.simplemobiletools.camera.extensions.config
|
import com.simplemobiletools.camera.extensions.config
|
||||||
import com.simplemobiletools.camera.helpers.FLASH_OFF
|
import com.simplemobiletools.camera.helpers.FLASH_OFF
|
||||||
import com.simplemobiletools.camera.helpers.FLASH_ON
|
import com.simplemobiletools.camera.helpers.FLASH_ON
|
||||||
|
import com.simplemobiletools.camera.helpers.MediaOutputHelper
|
||||||
import com.simplemobiletools.camera.helpers.ORIENT_LANDSCAPE_LEFT
|
import com.simplemobiletools.camera.helpers.ORIENT_LANDSCAPE_LEFT
|
||||||
import com.simplemobiletools.camera.helpers.ORIENT_LANDSCAPE_RIGHT
|
import com.simplemobiletools.camera.helpers.ORIENT_LANDSCAPE_RIGHT
|
||||||
import com.simplemobiletools.camera.helpers.ORIENT_PORTRAIT
|
import com.simplemobiletools.camera.helpers.ORIENT_PORTRAIT
|
||||||
|
@ -220,7 +221,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
|
||||||
)
|
)
|
||||||
|
|
||||||
checkVideoCaptureIntent()
|
checkVideoCaptureIntent()
|
||||||
mPreview = CameraXPreview(this, view_finder, this)
|
mPreview = CameraXPreview(this, view_finder, MediaOutputHelper(this), this)
|
||||||
checkImageCaptureIntent()
|
checkImageCaptureIntent()
|
||||||
mPreview?.setIsImageCaptureIntent(isImageCaptureIntent())
|
mPreview?.setIsImageCaptureIntent(isImageCaptureIntent())
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
package com.simplemobiletools.camera.helpers
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.util.Log
|
||||||
|
import com.simplemobiletools.camera.extensions.config
|
||||||
|
import com.simplemobiletools.camera.extensions.getOutputMediaFile
|
||||||
|
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||||
|
import com.simplemobiletools.commons.extensions.createAndroidSAFFile
|
||||||
|
import com.simplemobiletools.commons.extensions.createDocumentUriFromRootTree
|
||||||
|
import com.simplemobiletools.commons.extensions.createDocumentUriUsingFirstParentTreeUri
|
||||||
|
import com.simplemobiletools.commons.extensions.createSAFFileSdk30
|
||||||
|
import com.simplemobiletools.commons.extensions.getAndroidSAFUri
|
||||||
|
import com.simplemobiletools.commons.extensions.getDocumentFile
|
||||||
|
import com.simplemobiletools.commons.extensions.getDoesFilePathExist
|
||||||
|
import com.simplemobiletools.commons.extensions.getFileOutputStreamSync
|
||||||
|
import com.simplemobiletools.commons.extensions.getFilenameFromPath
|
||||||
|
import com.simplemobiletools.commons.extensions.getMimeType
|
||||||
|
import com.simplemobiletools.commons.extensions.hasProperStoredAndroidTreeUri
|
||||||
|
import com.simplemobiletools.commons.extensions.hasProperStoredFirstParentUri
|
||||||
|
import com.simplemobiletools.commons.extensions.hasProperStoredTreeUri
|
||||||
|
import com.simplemobiletools.commons.extensions.isAccessibleWithSAFSdk30
|
||||||
|
import com.simplemobiletools.commons.extensions.isRestrictedSAFOnlyRoot
|
||||||
|
import com.simplemobiletools.commons.extensions.needsStupidWritePermissions
|
||||||
|
import com.simplemobiletools.commons.extensions.showFileCreateError
|
||||||
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
class MediaOutputHelper(private val activity: BaseSimpleActivity) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MediaOutputHelper"
|
||||||
|
private const val MODE = "rw"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mediaStorageDir = activity.config.savePhotosFolder
|
||||||
|
|
||||||
|
fun getOutputStreamMediaOutput(): MediaOutput.OutputStreamMediaOutput? {
|
||||||
|
val canWrite = activity.canWrite(mediaStorageDir)
|
||||||
|
Log.i(TAG, "getMediaOutput: canWrite=${canWrite}")
|
||||||
|
return if (canWrite) {
|
||||||
|
val path = activity.getOutputMediaFile(true)
|
||||||
|
val uri = activity.getUri(path)
|
||||||
|
uri?.let {
|
||||||
|
activity.getFileOutputStreamSync(path, path.getMimeType())?.let {
|
||||||
|
MediaOutput.OutputStreamMediaOutput(it, uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}.also {
|
||||||
|
Log.i(TAG, "output stream: $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFileDescriptorMediaOutput(): MediaOutput.FileDescriptorMediaOutput? {
|
||||||
|
val canWrite = activity.canWrite(mediaStorageDir)
|
||||||
|
Log.i(TAG, "getMediaOutput: canWrite=${canWrite}")
|
||||||
|
return if (canWrite) {
|
||||||
|
val path = activity.getOutputMediaFile(false)
|
||||||
|
val uri = activity.getUri(path)
|
||||||
|
uri?.let {
|
||||||
|
activity.getFileDescriptorSync(path, path.getMimeType())?.let {
|
||||||
|
MediaOutput.FileDescriptorMediaOutput(it, uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}.also {
|
||||||
|
Log.i(TAG, "descriptor: $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BaseSimpleActivity.canWrite(path: String): Boolean {
|
||||||
|
return when {
|
||||||
|
isRestrictedSAFOnlyRoot(path) -> hasProperStoredAndroidTreeUri(path)
|
||||||
|
needsStupidWritePermissions(path) -> hasProperStoredTreeUri(false)
|
||||||
|
isAccessibleWithSAFSdk30(path) -> hasProperStoredFirstParentUri(path)
|
||||||
|
else -> File(path).canWrite()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BaseSimpleActivity.getUri(path: String): Uri? {
|
||||||
|
val targetFile = File(path)
|
||||||
|
return when {
|
||||||
|
isRestrictedSAFOnlyRoot(path) -> {
|
||||||
|
getAndroidSAFUri(path)
|
||||||
|
}
|
||||||
|
needsStupidWritePermissions(path) -> {
|
||||||
|
val parentFile = targetFile.parentFile ?: return null
|
||||||
|
val documentFile =
|
||||||
|
if (getDoesFilePathExist(parentFile.absolutePath ?: return null)) {
|
||||||
|
getDocumentFile(parentFile.path)
|
||||||
|
} else {
|
||||||
|
val parentDocumentFile = parentFile.parent?.let { getDocumentFile(it) }
|
||||||
|
parentDocumentFile?.createDirectory(parentFile.name) ?: getDocumentFile(parentFile.absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentFile == null) {
|
||||||
|
return Uri.fromFile(targetFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (getDoesFilePathExist(path)) {
|
||||||
|
createDocumentUriFromRootTree(path)
|
||||||
|
} else {
|
||||||
|
documentFile.createFile(path.getMimeType(), path.getFilenameFromPath())!!.uri
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isAccessibleWithSAFSdk30(path) -> {
|
||||||
|
try {
|
||||||
|
createDocumentUriUsingFirstParentTreeUri(path)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
} ?: Uri.fromFile(targetFile)
|
||||||
|
}
|
||||||
|
else -> return Uri.fromFile(targetFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BaseSimpleActivity.getFileDescriptorSync(path: String, mimeType: String): ParcelFileDescriptor? {
|
||||||
|
val targetFile = File(path)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
isRestrictedSAFOnlyRoot(path) -> {
|
||||||
|
val uri = getAndroidSAFUri(path)
|
||||||
|
if (!getDoesFilePathExist(path)) {
|
||||||
|
createAndroidSAFFile(path)
|
||||||
|
}
|
||||||
|
applicationContext.contentResolver.openFileDescriptor(uri, MODE)
|
||||||
|
}
|
||||||
|
needsStupidWritePermissions(path) -> {
|
||||||
|
val parentFile = targetFile.parentFile ?: return null
|
||||||
|
val documentFile =
|
||||||
|
if (getDoesFilePathExist(parentFile.absolutePath ?: return null)) {
|
||||||
|
getDocumentFile(parentFile.path)
|
||||||
|
} else {
|
||||||
|
val parentDocumentFile = parentFile.parent?.let { getDocumentFile(it) }
|
||||||
|
parentDocumentFile?.createDirectory(parentFile.name) ?: getDocumentFile(parentFile.absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (documentFile == null) {
|
||||||
|
val casualOutputStream = createCasualFileDescriptor(targetFile)
|
||||||
|
return if (casualOutputStream == null) {
|
||||||
|
showFileCreateError(parentFile.path)
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
casualOutputStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val uri = if (getDoesFilePathExist(path)) {
|
||||||
|
createDocumentUriFromRootTree(path)
|
||||||
|
} else {
|
||||||
|
documentFile.createFile(mimeType, path.getFilenameFromPath())!!.uri
|
||||||
|
}
|
||||||
|
applicationContext.contentResolver.openFileDescriptor(uri, MODE)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isAccessibleWithSAFSdk30(path) -> {
|
||||||
|
try {
|
||||||
|
val uri = createDocumentUriUsingFirstParentTreeUri(path)
|
||||||
|
if (!getDoesFilePathExist(path)) {
|
||||||
|
createSAFFileSdk30(path)
|
||||||
|
}
|
||||||
|
applicationContext.contentResolver.openFileDescriptor(uri, MODE)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
} ?: createCasualFileDescriptor(targetFile)
|
||||||
|
}
|
||||||
|
else -> return createCasualFileDescriptor(targetFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BaseSimpleActivity.createCasualFileDescriptor(targetFile: File): ParcelFileDescriptor? {
|
||||||
|
if (targetFile.parentFile?.exists() == false) {
|
||||||
|
targetFile.parentFile?.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
contentResolver.openFileDescriptor(Uri.fromFile(targetFile), MODE)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class MediaOutput(
|
||||||
|
open val uri: Uri,
|
||||||
|
) {
|
||||||
|
data class OutputStreamMediaOutput(
|
||||||
|
val outputStream: OutputStream,
|
||||||
|
override val uri: Uri,
|
||||||
|
) : MediaOutput(uri)
|
||||||
|
|
||||||
|
data class FileDescriptorMediaOutput(
|
||||||
|
val fileDescriptor: ParcelFileDescriptor,
|
||||||
|
override val uri: Uri,
|
||||||
|
) : MediaOutput(uri)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,13 @@ import android.view.OrientationEventListener
|
||||||
import android.view.ScaleGestureDetector
|
import android.view.ScaleGestureDetector
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.camera.core.*
|
import androidx.camera.core.AspectRatio
|
||||||
|
import androidx.camera.core.Camera
|
||||||
|
import androidx.camera.core.CameraSelector
|
||||||
|
import androidx.camera.core.CameraState
|
||||||
|
import androidx.camera.core.DisplayOrientedMeteringPointFactory
|
||||||
|
import androidx.camera.core.FocusMeteringAction
|
||||||
|
import androidx.camera.core.ImageCapture
|
||||||
import androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
|
import androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
|
||||||
import androidx.camera.core.ImageCapture.FLASH_MODE_AUTO
|
import androidx.camera.core.ImageCapture.FLASH_MODE_AUTO
|
||||||
import androidx.camera.core.ImageCapture.FLASH_MODE_OFF
|
import androidx.camera.core.ImageCapture.FLASH_MODE_OFF
|
||||||
|
@ -26,7 +32,11 @@ import androidx.camera.core.ImageCapture.Metadata
|
||||||
import androidx.camera.core.ImageCapture.OnImageSavedCallback
|
import androidx.camera.core.ImageCapture.OnImageSavedCallback
|
||||||
import androidx.camera.core.ImageCapture.OutputFileOptions
|
import androidx.camera.core.ImageCapture.OutputFileOptions
|
||||||
import androidx.camera.core.ImageCapture.OutputFileResults
|
import androidx.camera.core.ImageCapture.OutputFileResults
|
||||||
|
import androidx.camera.core.ImageCaptureException
|
||||||
|
import androidx.camera.core.Preview
|
||||||
|
import androidx.camera.core.UseCase
|
||||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||||
|
import androidx.camera.video.FileDescriptorOutputOptions
|
||||||
import androidx.camera.video.MediaStoreOutputOptions
|
import androidx.camera.video.MediaStoreOutputOptions
|
||||||
import androidx.camera.video.Quality
|
import androidx.camera.video.Quality
|
||||||
import androidx.camera.video.QualitySelector
|
import androidx.camera.video.QualitySelector
|
||||||
|
@ -35,6 +45,7 @@ import androidx.camera.video.Recording
|
||||||
import androidx.camera.video.VideoCapture
|
import androidx.camera.video.VideoCapture
|
||||||
import androidx.camera.video.VideoRecordEvent
|
import androidx.camera.video.VideoRecordEvent
|
||||||
import androidx.camera.view.PreviewView
|
import androidx.camera.view.PreviewView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.doOnLayout
|
import androidx.core.view.doOnLayout
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
@ -46,6 +57,7 @@ import com.simplemobiletools.camera.extensions.toAppFlashMode
|
||||||
import com.simplemobiletools.camera.extensions.toCameraSelector
|
import com.simplemobiletools.camera.extensions.toCameraSelector
|
||||||
import com.simplemobiletools.camera.extensions.toCameraXFlashMode
|
import com.simplemobiletools.camera.extensions.toCameraXFlashMode
|
||||||
import com.simplemobiletools.camera.extensions.toLensFacing
|
import com.simplemobiletools.camera.extensions.toLensFacing
|
||||||
|
import com.simplemobiletools.camera.helpers.MediaOutputHelper
|
||||||
import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
||||||
import com.simplemobiletools.camera.helpers.PinchToZoomOnScaleGestureListener
|
import com.simplemobiletools.camera.helpers.PinchToZoomOnScaleGestureListener
|
||||||
import com.simplemobiletools.camera.interfaces.MyPreview
|
import com.simplemobiletools.camera.interfaces.MyPreview
|
||||||
|
@ -61,6 +73,7 @@ import kotlin.math.min
|
||||||
class CameraXPreview(
|
class CameraXPreview(
|
||||||
private val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
private val previewView: PreviewView,
|
private val previewView: PreviewView,
|
||||||
|
private val mediaOutputHelper: MediaOutputHelper,
|
||||||
private val listener: CameraXPreviewListener,
|
private val listener: CameraXPreviewListener,
|
||||||
) : MyPreview, DefaultLifecycleObserver {
|
) : MyPreview, DefaultLifecycleObserver {
|
||||||
|
|
||||||
|
@ -76,7 +89,7 @@ class CameraXPreview(
|
||||||
|
|
||||||
private val config = activity.config
|
private val config = activity.config
|
||||||
private val contentResolver = activity.contentResolver
|
private val contentResolver = activity.contentResolver
|
||||||
private val mainExecutor = activity.mainExecutor
|
private val mainExecutor = ContextCompat.getMainExecutor(activity)
|
||||||
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
private val mediaSoundHelper = MediaSoundHelper()
|
private val mediaSoundHelper = MediaSoundHelper()
|
||||||
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
||||||
|
@ -363,24 +376,29 @@ class CameraXPreview(
|
||||||
val imageCapture = imageCapture ?: throw IllegalStateException("Camera initialization failed.")
|
val imageCapture = imageCapture ?: throw IllegalStateException("Camera initialization failed.")
|
||||||
|
|
||||||
val metadata = Metadata().apply {
|
val metadata = Metadata().apply {
|
||||||
isReversedHorizontal = config.flipPhotos
|
isReversedHorizontal = isFrontCameraInUse() && config.flipPhotos
|
||||||
}
|
}
|
||||||
|
|
||||||
val contentValues = ContentValues().apply {
|
val mediaOutput = mediaOutputHelper.getOutputStreamMediaOutput()
|
||||||
put(MediaStore.MediaColumns.DISPLAY_NAME, getRandomMediaName(true))
|
|
||||||
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
|
|
||||||
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
|
|
||||||
}
|
|
||||||
val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
|
||||||
val outputOptions = OutputFileOptions.Builder(contentResolver, contentUri, contentValues)
|
|
||||||
.setMetadata(metadata)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
|
val outputOptionsBuilder = if (mediaOutput != null) {
|
||||||
|
OutputFileOptions.Builder(mediaOutput.outputStream)
|
||||||
|
} else {
|
||||||
|
val contentValues = ContentValues().apply {
|
||||||
|
put(MediaStore.MediaColumns.DISPLAY_NAME, getRandomMediaName(true))
|
||||||
|
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
|
||||||
|
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
|
||||||
|
}
|
||||||
|
val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
OutputFileOptions.Builder(contentResolver, contentUri, contentValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
val outputOptions = outputOptionsBuilder.setMetadata(metadata).build()
|
||||||
|
|
||||||
imageCapture.takePicture(outputOptions, mainExecutor, object : OnImageSavedCallback {
|
imageCapture.takePicture(outputOptions, mainExecutor, object : OnImageSavedCallback {
|
||||||
override fun onImageSaved(outputFileResults: OutputFileResults) {
|
override fun onImageSaved(outputFileResults: OutputFileResults) {
|
||||||
listener.toggleBottomButtons(false)
|
listener.toggleBottomButtons(false)
|
||||||
listener.onMediaCaptured(outputFileResults.savedUri!!)
|
listener.onMediaCaptured(mediaOutput?.uri ?: outputFileResults.savedUri!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(exception: ImageCaptureException) {
|
override fun onError(exception: ImageCaptureException) {
|
||||||
|
@ -416,19 +434,23 @@ class CameraXPreview(
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
private fun startRecording() {
|
private fun startRecording() {
|
||||||
val videoCapture = videoCapture ?: throw IllegalStateException("Camera initialization failed.")
|
val videoCapture = videoCapture ?: throw IllegalStateException("Camera initialization failed.")
|
||||||
val contentValues = ContentValues().apply {
|
|
||||||
put(MediaStore.MediaColumns.DISPLAY_NAME, getRandomMediaName(false))
|
|
||||||
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
|
|
||||||
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
|
|
||||||
}
|
|
||||||
val contentUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
|
||||||
val outputOptions = MediaStoreOutputOptions.Builder(contentResolver, contentUri)
|
|
||||||
.setContentValues(contentValues)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
currentRecording = videoCapture.output
|
val mediaOutput = mediaOutputHelper.getFileDescriptorMediaOutput()
|
||||||
.prepareRecording(activity, outputOptions)
|
val recording = if (mediaOutput != null) {
|
||||||
.withAudioEnabled()
|
FileDescriptorOutputOptions.Builder(mediaOutput.fileDescriptor).build()
|
||||||
|
.let { videoCapture.output.prepareRecording(activity, it) }
|
||||||
|
} else {
|
||||||
|
val contentValues = ContentValues().apply {
|
||||||
|
put(MediaStore.MediaColumns.DISPLAY_NAME, getRandomMediaName(false))
|
||||||
|
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
|
||||||
|
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
|
||||||
|
}
|
||||||
|
val contentUri = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
MediaStoreOutputOptions.Builder(contentResolver, contentUri).setContentValues(contentValues).build()
|
||||||
|
.let { videoCapture.output.prepareRecording(activity, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRecording = recording.withAudioEnabled()
|
||||||
.start(mainExecutor) { recordEvent ->
|
.start(mainExecutor) { recordEvent ->
|
||||||
Log.d(TAG, "recordEvent=$recordEvent ")
|
Log.d(TAG, "recordEvent=$recordEvent ")
|
||||||
recordingState = recordEvent
|
recordingState = recordEvent
|
||||||
|
@ -448,7 +470,7 @@ class CameraXPreview(
|
||||||
if (recordEvent.hasError()) {
|
if (recordEvent.hasError()) {
|
||||||
// TODO: Handle errors
|
// TODO: Handle errors
|
||||||
} else {
|
} else {
|
||||||
listener.onMediaCaptured(recordEvent.outputResults.outputUri)
|
listener.onMediaCaptured(mediaOutput?.uri ?: recordEvent.outputResults.outputUri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue