I'm following along with some camerax tutorials, and I'm trying to get the image capture & saving working, however the 'onImageSaved' function is never called, and even though I can get a Uri for the image location, it points to nothing. I've spent hours trying to debug this and can't figure out what's gone wrong. I've tried checking file access permissions, that's not the problem.
private fun takePhoto(){
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image
//val photoFile = File(externalMediaDirs[0],"JPEG_${System.currentTimeMillis()}.jpg")
val directory: File = applicationContext.getDir("imageDir", Context.MODE_PRIVATE)
val photoFile = File(directory, "JPEG_${System.currentTimeMillis()}.jpg")
// Create output options object which contains file metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Convert filepath to Uri
val imageUri = Uri.fromFile(photoFile)
// Set up image capture listener, which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
// TODO This never runs
val msg = "Photo capture succeeded: $imageUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
// Send our image Uri back to the demo app & finish this activity
val intent = Intent()
intent.putExtra("ImageUri", imageUri)
runOnUiThread {
setResult(2, intent)
finish()
}
}
CodePudding user response:
First capture image
imageCapture.takePicture(ContextCompat.getMainExecutor(requireContext()),object :
ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
super.onCaptureSuccess(image)
val bitmap =imageProxyToBitmap(image)
val file = bitmap?.let {
AppUtils.saveImage(requireContext(),
it,SimpleDateFormat(CameraFragment.FILENAME_FORMAT,
Locale.US).format(System.currentTimeMillis()) ".png")
}
val savedUri = Uri.fromFile(file)
if (savedUri != null) {
// here your save image uri path
}
}
override fun onError(exception: ImageCaptureException) {
super.onError(exception)
}
})
Create a function for imageProxyToBitmap
private fun imageProxyToBitmap(image: ImageProxy): Bitmap {
val planeProxy = image.planes[0]
val buffer: ByteBuffer = planeProxy.buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}
Than create saveImage class in util or any where you want
@Throws(IOException::class)
fun saveImage(context: Context,bitmap: Bitmap, name: String): File? {
val saved: Boolean
var image : File?=null
val fos: OutputStream? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = context.contentResolver
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name)
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/")
val imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
image =File(imageUri?.let { FileUriUtils.getRealPath(context, uri = it) })
imageUri?.let { resolver.openOutputStream(it) }
} else {
val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString() File.separator
val file = File(imagesDir)
if (!file.exists()) {
file.mkdir()
}
image = File(imagesDir, "$name.png")
FileOutputStream(image)
}
saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
fos?.flush()
fos?.close()
return image
}