@@ -5,6 +5,7 @@ import android.content.Context.CAMERA_SERVICE
55import android.graphics.Bitmap
66import android.hardware.camera2.CameraCharacteristics
77import android.hardware.camera2.CameraManager
8+ import android.net.Uri
89import android.util.Base64
910import android.util.Log
1011import android.view.Surface
@@ -25,6 +26,8 @@ import androidx.camera.view.LifecycleCameraController
2526import androidx.camera.view.PreviewView
2627import androidx.core.content.ContextCompat
2728import androidx.lifecycle.LifecycleOwner
29+ import com.getcapacitor.FileUtils
30+ import com.getcapacitor.JSObject
2831import com.getcapacitor.Plugin
2932import com.google.mlkit.vision.barcode.BarcodeScanner
3033import com.google.mlkit.vision.barcode.BarcodeScannerOptions
@@ -35,6 +38,8 @@ import com.michaelwolz.capacitorcameraview.model.CameraDevice
3538import com.michaelwolz.capacitorcameraview.model.CameraSessionConfiguration
3639import com.michaelwolz.capacitorcameraview.model.ZoomFactors
3740import java.io.ByteArrayOutputStream
41+ import java.io.File
42+ import java.io.FileOutputStream
3843import java.util.concurrent.ExecutorService
3944import java.util.concurrent.Executors
4045
@@ -118,7 +123,11 @@ class CameraView(plugin: Plugin) {
118123 }
119124
120125 /* * Capture a photo with the current camera configuration */
121- fun capturePhoto (quality : Int , callback : (String? , Exception ? ) -> Unit ) {
126+ fun capturePhoto (
127+ quality : Int ,
128+ saveToFile : Boolean = false,
129+ callback : (JSObject ? , Exception ? ) -> Unit
130+ ) {
122131 val startTime = System .currentTimeMillis()
123132 val controller =
124133 this .cameraController
@@ -145,23 +154,58 @@ class CameraView(plugin: Plugin) {
145154 )
146155
147156 try {
148- controller.takePicture(
149- cameraExecutor,
150- object : ImageCapture .OnImageCapturedCallback () {
151- override fun onCaptureSuccess (image : ImageProxy ) {
152- Log .d(
153- TAG ,
154- " Image captured successfully in ${System .currentTimeMillis() - startTime} ms"
155- )
156- handleCaptureSuccess(image, quality, imageRotationDegrees, callback)
157+ if (saveToFile) {
158+ // Direct file capture - much more efficient!
159+ val tempFile =
160+ File .createTempFile(" camera_capture_photo" , " .jpg" , context.cacheDir)
161+ val outputFileOptions = ImageCapture .OutputFileOptions .Builder (tempFile).build()
162+
163+ controller.takePicture(
164+ outputFileOptions,
165+ cameraExecutor,
166+ object : ImageCapture .OnImageSavedCallback {
167+ override fun onImageSaved (output : ImageCapture .OutputFileResults ) {
168+ val processingTime = System .currentTimeMillis() - startTime
169+ Log .d(TAG , " Image saved directly to file in ${processingTime} ms" )
170+
171+ val result = JSObject ().apply {
172+ val capacitorFilePath = FileUtils .getPortablePath(
173+ context,
174+ pluginDelegate.bridge.localUrl,
175+ Uri .fromFile(tempFile)
176+ )
177+
178+ put(" webPath" , capacitorFilePath)
179+ }
180+ callback(result, null )
181+ }
182+
183+ override fun onError (exception : ImageCaptureException ) {
184+ Log .e(TAG , " Error saving image to file" , exception)
185+ callback(null , exception)
186+ }
157187 }
158-
159- override fun onError (exception : ImageCaptureException ) {
160- Log .e(TAG , " Error capturing image" , exception)
161- callback(null , exception)
188+ )
189+ } else {
190+ // Base64 capture using ImageProxy
191+ controller.takePicture(
192+ cameraExecutor,
193+ object : ImageCapture .OnImageCapturedCallback () {
194+ override fun onCaptureSuccess (image : ImageProxy ) {
195+ Log .d(
196+ TAG ,
197+ " Image captured successfully in ${System .currentTimeMillis() - startTime} ms"
198+ )
199+ handleCaptureSuccess(image, quality, imageRotationDegrees, callback)
200+ }
201+
202+ override fun onError (exception : ImageCaptureException ) {
203+ Log .e(TAG , " Error capturing image" , exception)
204+ callback(null , exception)
205+ }
162206 }
163- }
164- )
207+ )
208+ }
165209 } catch (e: Exception ) {
166210 Log .e(TAG , " Error setting up image capture" , e)
167211 callback(null , e)
@@ -170,20 +214,22 @@ class CameraView(plugin: Plugin) {
170214 }
171215
172216 /* *
173- * Handles the successful capture of an image, converting it to a Base64 string
217+ * Handles the successful capture of an image for base64 conversion
174218 */
175219 fun handleCaptureSuccess (
176220 image : ImageProxy ,
177221 quality : Int ,
178222 rotationDegrees : Int ,
179- callback : (String ? , Exception ? ) -> Unit
223+ callback : (JSObject ? , Exception ? ) -> Unit
180224 ) {
181225 val startTime = System .currentTimeMillis()
182226 try {
183- // Turn the image into a Base64 encoded string and apply rotation if necessary
184227 val base64String = imageProxyToBase64(image, quality, rotationDegrees)
228+ val result = JSObject ().apply {
229+ put(" photo" , base64String)
230+ }
185231 Log .d(TAG , " Image processed to Base64 in ${System .currentTimeMillis() - startTime} ms" )
186- callback(base64String , null )
232+ callback(result , null )
187233 } catch (e: Exception ) {
188234 Log .e(TAG , " Error processing captured image" , e)
189235 callback(null , e)
@@ -196,7 +242,11 @@ class CameraView(plugin: Plugin) {
196242 * Capture a frame directly from the preview without using the full photo pipeline which is
197243 * faster but has lower quality.
198244 */
199- fun captureSampleFromPreview (quality : Int , callback : (String? , Exception ? ) -> Unit ) {
245+ fun captureSampleFromPreview (
246+ quality : Int ,
247+ saveToFile : Boolean = false,
248+ callback : (JSObject ? , Exception ? ) -> Unit
249+ ) {
200250 val previewView =
201251 this .previewView
202252 ? : run {
@@ -205,7 +255,6 @@ class CameraView(plugin: Plugin) {
205255 }
206256
207257 mainHandler.post {
208- val outputStream = ByteArrayOutputStream ()
209258 try {
210259 val bitmap =
211260 previewView.bitmap
@@ -214,27 +263,48 @@ class CameraView(plugin: Plugin) {
214263 return @post
215264 }
216265
217- // Convert bitmap to Base64
218- bitmap.compress(Bitmap .CompressFormat .JPEG , quality, outputStream)
219- val byteArray = outputStream.toByteArray()
220- val base64String = Base64 .encodeToString(byteArray, Base64 .NO_WRAP )
266+ val result = JSObject ()
267+
268+ if (saveToFile) {
269+ val tempFile =
270+ File .createTempFile(" camera_capture_sample" , " .jpg" , context.cacheDir)
271+
272+ FileOutputStream (tempFile).use { outputStream ->
273+ bitmap.compress(Bitmap .CompressFormat .JPEG , quality, outputStream)
274+ }
275+
276+ val capacitorFilePath = FileUtils .getPortablePath(
277+ context,
278+ pluginDelegate.bridge.localUrl,
279+ Uri .fromFile(tempFile)
280+ )
281+
282+ result.put(" webPath" , capacitorFilePath)
283+ } else {
284+ // Convert bitmap to Base64
285+ val outputStream = ByteArrayOutputStream ()
286+ outputStream.use { stream ->
287+ bitmap.compress(Bitmap .CompressFormat .JPEG , quality, stream)
288+ val byteArray = stream.toByteArray()
289+ val base64String = Base64 .encodeToString(byteArray, Base64 .NO_WRAP )
290+ result.put(" photo" , base64String)
291+ }
292+ }
221293
222- callback(base64String , null )
294+ callback(result , null )
223295 } catch (e: Exception ) {
224296 Log .e(TAG , " Error capturing preview frame" , e)
225297 callback(null , e)
226- } finally {
227- outputStream.close()
228298 }
229299 }
230300 }
231301
232302 /* * Flip between front and back cameras */
233303 fun flipCamera (callback : (Exception ? ) -> Unit ) {
234304 currentCameraSelector = when (currentCameraSelector) {
235- CameraSelector .DEFAULT_FRONT_CAMERA -> CameraSelector .DEFAULT_BACK_CAMERA
236- else -> CameraSelector .DEFAULT_FRONT_CAMERA
237- }
305+ CameraSelector .DEFAULT_FRONT_CAMERA -> CameraSelector .DEFAULT_BACK_CAMERA
306+ else -> CameraSelector .DEFAULT_FRONT_CAMERA
307+ }
238308
239309 val controller =
240310 this .cameraController
@@ -420,21 +490,21 @@ class CameraView(plugin: Plugin) {
420490 setupPreviewView(context)
421491
422492 currentCameraSelector = if (config.position == " front" ) {
423- CameraSelector .DEFAULT_FRONT_CAMERA
424- } else {
425- CameraSelector .DEFAULT_BACK_CAMERA
426- }
493+ CameraSelector .DEFAULT_FRONT_CAMERA
494+ } else {
495+ CameraSelector .DEFAULT_BACK_CAMERA
496+ }
427497
428498 if (config.deviceId != null ) {
429499 // Prefer specific device id over position
430500 currentCameraSelector = CameraSelector .Builder ()
431- .addCameraFilter { cameraInfos ->
432- cameraInfos.filter { info ->
433- val cameraId = Camera2CameraInfo .from(info).cameraId
434- cameraId == config.deviceId
435- }
501+ .addCameraFilter { cameraInfos ->
502+ cameraInfos.filter { info ->
503+ val cameraId = Camera2CameraInfo .from(info).cameraId
504+ cameraId == config.deviceId
436505 }
437- .build()
506+ }
507+ .build()
438508 }
439509
440510 // Initialize camera controller
0 commit comments