capacitor-camera-view 2.2.0-rc.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +17 -202
  2. package/android/build.gradle +0 -1
  3. package/android/src/main/AndroidManifest.xml +0 -1
  4. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraView.kt +4 -287
  5. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraViewPlugin.kt +8 -112
  6. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/BarcodeDetectionResult.kt +1 -0
  7. package/android/src/main/java/com/michaelwolz/capacitorcameraview/utils.kt +1 -21
  8. package/dist/docs.json +21 -201
  9. package/dist/esm/definitions.d.ts +23 -85
  10. package/dist/esm/definitions.js.map +1 -1
  11. package/dist/esm/web.d.ts +4 -20
  12. package/dist/esm/web.js +16 -151
  13. package/dist/esm/web.js.map +1 -1
  14. package/dist/plugin.cjs.js +16 -151
  15. package/dist/plugin.cjs.js.map +1 -1
  16. package/dist/plugin.js +16 -151
  17. package/dist/plugin.js.map +1 -1
  18. package/ios/Sources/CameraViewPlugin/CameraError.swift +0 -28
  19. package/ios/Sources/CameraViewPlugin/CameraEvents.swift +27 -3
  20. package/ios/Sources/CameraViewPlugin/CameraSessionConfiguration.swift +8 -8
  21. package/ios/Sources/CameraViewPlugin/CameraViewManager+BarcodeScan.swift +34 -3
  22. package/ios/Sources/CameraViewPlugin/CameraViewManager+PhotoCapture.swift +14 -13
  23. package/ios/Sources/CameraViewPlugin/CameraViewManager.swift +150 -159
  24. package/ios/Sources/CameraViewPlugin/CameraViewPlugin.swift +15 -114
  25. package/ios/Sources/CameraViewPlugin/TempFileManager.swift +34 -68
  26. package/package.json +12 -12
  27. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/VideoRecordingQuality.kt +0 -10
  28. package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoRecording.swift +0 -302
@@ -1,9 +1,7 @@
1
1
  package com.michaelwolz.capacitorcameraview
2
2
 
3
- import android.Manifest
4
3
  import android.content.Context
5
4
  import android.content.Context.CAMERA_SERVICE
6
- import android.content.pm.PackageManager
7
5
  import android.graphics.Bitmap
8
6
  import android.hardware.camera2.CameraCharacteristics
9
7
  import android.hardware.camera2.CameraManager
@@ -25,16 +23,8 @@ import androidx.camera.core.TorchState
25
23
  import androidx.camera.core.resolutionselector.AspectRatioStrategy
26
24
  import androidx.camera.core.resolutionselector.ResolutionSelector
27
25
  import androidx.camera.mlkit.vision.MlKitAnalyzer
28
- import androidx.camera.video.FallbackStrategy
29
- import androidx.camera.video.FileOutputOptions
30
- import androidx.camera.video.Quality
31
- import androidx.camera.video.QualitySelector
32
- import androidx.camera.video.Recording
33
- import androidx.camera.video.VideoRecordEvent
34
- import androidx.camera.view.CameraController
35
26
  import androidx.camera.view.LifecycleCameraController
36
27
  import androidx.camera.view.PreviewView
37
- import androidx.camera.view.video.AudioConfig
38
28
  import androidx.core.content.ContextCompat
39
29
  import androidx.lifecycle.LifecycleOwner
40
30
  import com.getcapacitor.FileUtils
@@ -48,9 +38,7 @@ import com.michaelwolz.capacitorcameraview.model.BarcodeDetectionResult
48
38
  import com.michaelwolz.capacitorcameraview.model.CameraDevice
49
39
  import com.michaelwolz.capacitorcameraview.model.CameraResult
50
40
  import com.michaelwolz.capacitorcameraview.model.CameraSessionConfiguration
51
- import com.michaelwolz.capacitorcameraview.model.VideoRecordingQuality
52
41
  import com.michaelwolz.capacitorcameraview.model.ZoomFactors
53
- import kotlinx.coroutines.CancellableContinuation
54
42
  import kotlinx.coroutines.CoroutineScope
55
43
  import kotlinx.coroutines.Dispatchers
56
44
  import kotlinx.coroutines.SupervisorJob
@@ -67,7 +55,6 @@ import java.io.File
67
55
  import java.io.FileOutputStream
68
56
  import java.util.concurrent.ExecutorService
69
57
  import java.util.concurrent.Executors
70
- import java.util.concurrent.atomic.AtomicBoolean
71
58
  import java.util.concurrent.atomic.AtomicLong
72
59
  import java.util.concurrent.atomic.AtomicReference
73
60
  import kotlin.coroutines.resume
@@ -85,9 +72,7 @@ class CameraView(plugin: Plugin) {
85
72
  // Camera components (using atomic reference for thread safety)
86
73
  private var cameraController: LifecycleCameraController?
87
74
  get() = cameraControllerRef.get()
88
- set(value) {
89
- cameraControllerRef.set(value)
90
- }
75
+ set(value) { cameraControllerRef.set(value) }
91
76
 
92
77
  private val cameraExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor() }
93
78
  private var previewView: PreviewView? = null
@@ -96,18 +81,6 @@ class CameraView(plugin: Plugin) {
96
81
  private var currentCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
97
82
  private var currentFlashMode: Int = ImageCapture.FLASH_MODE_OFF
98
83
 
99
- // Active video recording
100
- private var activeRecording: Recording? = null
101
-
102
- /**
103
- * Holds the pending stop-recording continuation result handler.
104
- * Needed because CameraX delivers the final recording outcome asynchronously via Finalize.
105
- */
106
- private var pendingStopCallback: ((CameraResult<JSObject>) -> Unit)? = null
107
-
108
- // Track the output file for the current recording
109
- private var currentRecordingFile: File? = null
110
-
111
84
  // Plugin context
112
85
  private var lifecycleOwner: LifecycleOwner? = null
113
86
  private var pluginDelegate: Plugin = plugin
@@ -157,15 +130,6 @@ class CameraView(plugin: Plugin) {
157
130
  /** Stop the camera session and release resources. */
158
131
  suspend fun stopSessionAsync(): CameraResult<Unit> = withContext(Dispatchers.Main) {
159
132
  try {
160
- // Stop any active recording before unbinding
161
- activeRecording?.stop()
162
- activeRecording = null
163
- pendingStopCallback?.invoke(
164
- CameraResult.Error(Exception("Recording was interrupted because the camera session stopped"))
165
- )
166
- pendingStopCallback = null
167
- currentRecordingFile = null
168
-
169
133
  cameraController?.unbind()
170
134
 
171
135
  previewView?.let { view ->
@@ -278,15 +242,11 @@ class CameraView(plugin: Plugin) {
278
242
  "Image captured successfully in ${System.currentTimeMillis() - startTime}ms"
279
243
  )
280
244
  try {
281
- val base64String =
282
- imageProxyToBase64(image, quality, imageRotationDegrees)
245
+ val base64String = imageProxyToBase64(image, quality, imageRotationDegrees)
283
246
  val result = JSObject().apply {
284
247
  put("photo", base64String)
285
248
  }
286
- Log.d(
287
- TAG,
288
- "Image processed to Base64 in ${System.currentTimeMillis() - startTime}ms"
289
- )
249
+ Log.d(TAG, "Image processed to Base64 in ${System.currentTimeMillis() - startTime}ms")
290
250
  continuation.resume(CameraResult.Success(result))
291
251
  } catch (e: Exception) {
292
252
  Log.e(TAG, "Error processing captured image", e)
@@ -386,244 +346,6 @@ class CameraView(plugin: Plugin) {
386
346
  }
387
347
  }
388
348
 
389
- /**
390
- * Starts video recording to a temporary file.
391
- */
392
- suspend fun startRecordingAsync(
393
- enableAudio: Boolean,
394
- videoQuality: VideoRecordingQuality,
395
- ): CameraResult<Unit> = suspendCancellableCoroutine { continuation ->
396
- mainHandler.post {
397
- startRecordingOnMainThread(enableAudio, videoQuality, continuation)
398
- }
399
- }
400
-
401
- private fun startRecordingOnMainThread(
402
- enableAudio: Boolean,
403
- videoQuality: VideoRecordingQuality,
404
- continuation: CancellableContinuation<CameraResult<Unit>>
405
- ) {
406
- val controller = validateRecordingPreconditions(continuation) ?: return
407
-
408
- try {
409
- controller.videoCaptureQualitySelector = videoQuality.toQualitySelector()
410
-
411
- // Enable VIDEO_CAPTURE use case alongside IMAGE_CAPTURE
412
- controller.setEnabledUseCases(
413
- CameraController.IMAGE_CAPTURE or CameraController.VIDEO_CAPTURE
414
- )
415
-
416
- val outputOptions = createRecordingOutputOptions()
417
- val audioConfig = resolveAudioConfig(enableAudio, continuation) ?: return
418
-
419
- startCameraRecording(controller, outputOptions, audioConfig, continuation)
420
- } catch (e: SecurityException) {
421
- Log.e(TAG, "Security exception when starting recording. Missing permission?", e)
422
- // Restore normal use cases on permission error
423
- cameraController?.setEnabledUseCases(CameraController.IMAGE_CAPTURE)
424
- continuation.resume(CameraResult.Error(e))
425
- } catch (e: Exception) {
426
- Log.e(TAG, "Error starting recording", e)
427
- // Restore normal use cases on error
428
- cameraController?.setEnabledUseCases(CameraController.IMAGE_CAPTURE)
429
- continuation.resume(CameraResult.Error(e))
430
- }
431
- }
432
-
433
- private fun validateRecordingPreconditions(
434
- continuation: CancellableContinuation<CameraResult<Unit>>
435
- ): LifecycleCameraController? {
436
- val controller = cameraController
437
- if (controller == null) {
438
- continuation.resume(CameraResult.Error(CameraError.CameraNotInitialized()))
439
- return null
440
- }
441
-
442
- if (activeRecording != null) {
443
- continuation.resume(CameraResult.Error(Exception("Recording is already in progress")))
444
- return null
445
- }
446
-
447
- return controller
448
- }
449
-
450
- private fun createRecordingOutputOptions(): FileOutputOptions {
451
- val tempFile = File.createTempFile(
452
- "camera_recording_",
453
- ".mp4",
454
- context.cacheDir
455
- )
456
- currentRecordingFile = tempFile
457
- return FileOutputOptions.Builder(tempFile).build()
458
- }
459
-
460
- private fun resolveAudioConfig(
461
- enableAudio: Boolean,
462
- continuation: CancellableContinuation<CameraResult<Unit>>
463
- ): AudioConfig? {
464
- if (!enableAudio) {
465
- return AudioConfig.AUDIO_DISABLED
466
- }
467
-
468
- if (hasMicrophonePermission()) {
469
- return try {
470
- AudioConfig.create(true)
471
- } catch (e: SecurityException) {
472
- continuation.resume(CameraResult.Error(e))
473
- null
474
- }
475
- }
476
-
477
- continuation.resume(
478
- CameraResult.Error(
479
- SecurityException("Microphone permission is required for audio recording")
480
- )
481
- )
482
- return null
483
- }
484
-
485
- private fun hasMicrophonePermission(): Boolean {
486
- return ContextCompat.checkSelfPermission(
487
- context,
488
- Manifest.permission.RECORD_AUDIO
489
- ) == PackageManager.PERMISSION_GRANTED
490
- }
491
-
492
- private fun startCameraRecording(
493
- controller: LifecycleCameraController,
494
- outputOptions: FileOutputOptions,
495
- audioConfig: AudioConfig,
496
- continuation: CancellableContinuation<CameraResult<Unit>>
497
- ) {
498
- val startResumed = AtomicBoolean(false)
499
- activeRecording = controller.startRecording(
500
- outputOptions,
501
- audioConfig,
502
- cameraExecutor
503
- ) { event ->
504
- when (event) {
505
- is VideoRecordEvent.Start -> handleRecordingStartEvent(startResumed, continuation)
506
- is VideoRecordEvent.Finalize -> {
507
- handleRecordingFinalizeEvent(event, startResumed, continuation)
508
- }
509
-
510
- else -> Unit
511
- }
512
- }
513
- }
514
-
515
- private fun handleRecordingStartEvent(
516
- startResumed: AtomicBoolean,
517
- continuation: CancellableContinuation<CameraResult<Unit>>
518
- ) {
519
- Log.d(TAG, "Video recording started")
520
- if (continuation.isActive && startResumed.compareAndSet(false, true)) {
521
- continuation.resume(CameraResult.Success(Unit))
522
- }
523
- }
524
-
525
- private fun handleRecordingFinalizeEvent(
526
- event: VideoRecordEvent.Finalize,
527
- startResumed: AtomicBoolean,
528
- continuation: CancellableContinuation<CameraResult<Unit>>
529
- ) {
530
- // If recording finalized before Start was emitted, resume the
531
- // startRecording continuation with an error
532
- if (continuation.isActive && startResumed.compareAndSet(false, true)) {
533
- continuation.resume(
534
- CameraResult.Error(
535
- Exception("Recording failed to start: error code ${event.error}")
536
- )
537
- )
538
- }
539
-
540
- finalizeRecordingAndNotifyStopCallback(event)
541
- }
542
-
543
- private fun finalizeRecordingAndNotifyStopCallback(event: VideoRecordEvent.Finalize) {
544
- mainHandler.post {
545
- // CameraX requires use case changes on the main thread.
546
- cameraController?.setEnabledUseCases(CameraController.IMAGE_CAPTURE)
547
-
548
- val callback = pendingStopCallback
549
- pendingStopCallback = null
550
- // Always clean up recording state
551
- activeRecording = null
552
-
553
- if (event.hasError()) {
554
- Log.e(TAG, "Recording error: ${event.error}")
555
- currentRecordingFile = null
556
- callback?.invoke(CameraResult.Error(Exception("Recording failed with error code: ${event.error}")))
557
- return@post
558
- }
559
-
560
- val file = currentRecordingFile
561
- currentRecordingFile = null
562
- if (file == null) {
563
- callback?.invoke(CameraResult.Error(Exception("Recording file not found")))
564
- return@post
565
- }
566
-
567
- val capacitorFilePath = FileUtils.getPortablePath(
568
- context,
569
- pluginDelegate.bridge.localUrl,
570
- Uri.fromFile(file)
571
- )
572
- val result = JSObject().apply {
573
- put("webPath", capacitorFilePath)
574
- }
575
- callback?.invoke(CameraResult.Success(result))
576
- }
577
- }
578
-
579
- private fun VideoRecordingQuality.toQualitySelector(): QualitySelector {
580
- return when (this) {
581
- VideoRecordingQuality.LOWEST -> QualitySelector.from(Quality.LOWEST)
582
- VideoRecordingQuality.SD -> QualitySelector.from(
583
- Quality.SD,
584
- FallbackStrategy.lowerQualityOrHigherThan(Quality.SD)
585
- )
586
-
587
- VideoRecordingQuality.HD -> QualitySelector.from(
588
- Quality.HD,
589
- FallbackStrategy.lowerQualityOrHigherThan(Quality.HD)
590
- )
591
-
592
- VideoRecordingQuality.FHD -> QualitySelector.from(
593
- Quality.FHD,
594
- FallbackStrategy.lowerQualityOrHigherThan(Quality.FHD)
595
- )
596
-
597
- VideoRecordingQuality.UHD -> QualitySelector.from(
598
- Quality.UHD,
599
- FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD)
600
- )
601
-
602
- VideoRecordingQuality.HIGHEST -> QualitySelector.from(Quality.HIGHEST)
603
- }
604
- }
605
-
606
- /**
607
- * Stops the current video recording and returns the file path.
608
- */
609
- suspend fun stopRecordingAsync(): CameraResult<JSObject> =
610
- suspendCancellableCoroutine { continuation ->
611
- mainHandler.post {
612
- val recording = activeRecording
613
- if (recording == null) {
614
- continuation.resume(CameraResult.Error(Exception("No recording is in progress")))
615
- return@post
616
- }
617
-
618
- pendingStopCallback = { result ->
619
- continuation.resume(result)
620
- }
621
-
622
- activeRecording = null
623
- recording.stop()
624
- }
625
- }
626
-
627
349
  /** Flip between front and back cameras */
628
350
  fun flipCamera(callback: (Exception?) -> Unit) {
629
351
  currentCameraSelector = when (currentCameraSelector) {
@@ -811,12 +533,6 @@ class CameraView(plugin: Plugin) {
811
533
 
812
534
  mainHandler.post {
813
535
  try {
814
- // Stop any active recording before cleanup
815
- activeRecording?.stop()
816
- activeRecording = null
817
- pendingStopCallback = null
818
- currentRecordingFile = null
819
-
820
536
  // Stop camera session
821
537
  cameraController?.unbind()
822
538
  cameraController = null
@@ -1007,6 +723,7 @@ class CameraView(plugin: Plugin) {
1007
723
  val barcodeResult =
1008
724
  BarcodeDetectionResult(
1009
725
  value = barcode.rawValue ?: "",
726
+ rawBytes = barcode.rawBytes ?: ByteArray(0),
1010
727
  displayValue = barcode.displayValue ?: "",
1011
728
  type = getBarcodeFormatString(barcode.format),
1012
729
  boundingRect = webBoundingRect
@@ -12,7 +12,6 @@ import com.getcapacitor.annotation.CapacitorPlugin
12
12
  import com.getcapacitor.annotation.Permission
13
13
  import com.getcapacitor.annotation.PermissionCallback
14
14
  import com.michaelwolz.capacitorcameraview.model.BarcodeDetectionResult
15
- import com.michaelwolz.capacitorcameraview.model.VideoRecordingQuality
16
15
  import kotlinx.coroutines.CoroutineScope
17
16
  import kotlinx.coroutines.Dispatchers
18
17
  import kotlinx.coroutines.Job
@@ -22,10 +21,7 @@ import kotlinx.coroutines.launch
22
21
 
23
22
  @CapacitorPlugin(
24
23
  name = "CameraView",
25
- permissions = [
26
- Permission(strings = [Manifest.permission.CAMERA], alias = "camera"),
27
- Permission(strings = [Manifest.permission.RECORD_AUDIO], alias = "microphone")
28
- ]
24
+ permissions = [Permission(strings = [Manifest.permission.CAMERA], alias = "camera")]
29
25
  )
30
26
  class CameraViewPlugin : Plugin() {
31
27
  // Coroutine scope for async operations
@@ -148,113 +144,7 @@ class CameraViewPlugin : Plugin() {
148
144
  },
149
145
  onError = { error ->
150
146
  call.reject("Failed to capture frame: ${error.message}", error)
151
- Log.d(
152
- TAG,
153
- "captureSample failed after ${System.currentTimeMillis() - timeStart}ms"
154
- )
155
- }
156
- )
157
- }
158
- }
159
-
160
- @PluginMethod
161
- override fun requestPermissions(call: PluginCall) {
162
- val permissionsList = call.getArray("permissions")
163
- ?.toList<String>()
164
- ?: listOf("camera")
165
-
166
- // Determine which aliases still need to be requested
167
- val aliasesToRequest = permissionsList.filter { alias ->
168
- getPermissionState(alias) != PermissionState.GRANTED
169
- }
170
-
171
- if (aliasesToRequest.isEmpty()) {
172
- checkPermissions(call)
173
- return
174
- }
175
-
176
- // Store which permissions to request so callback can continue the chain
177
- call.data.put("_pendingAliases", com.getcapacitor.JSArray(aliasesToRequest))
178
- requestPermissionForAlias(aliasesToRequest.first(), call, "requestedPermsCallback")
179
- }
180
-
181
- @PermissionCallback
182
- private fun requestedPermsCallback(call: PluginCall) {
183
- val pendingAliases = call.getArray("_pendingAliases")?.toList<String>() ?: emptyList()
184
-
185
- // Find remaining aliases that still need requesting
186
- val remaining = pendingAliases.drop(1).filter { alias ->
187
- getPermissionState(alias) != PermissionState.GRANTED
188
- }
189
-
190
- if (remaining.isNotEmpty()) {
191
- call.data.put("_pendingAliases", com.getcapacitor.JSArray(remaining))
192
- requestPermissionForAlias(remaining.first(), call, "requestedPermsCallback")
193
- } else {
194
- checkPermissions(call)
195
- }
196
- }
197
-
198
- @PluginMethod
199
- fun startRecording(call: PluginCall) {
200
- val enableAudio = call.getBoolean("enableAudio") ?: false
201
- val videoQuality =
202
- parseVideoRecordingQuality(call.getString("videoQuality"))
203
- ?: run {
204
- call.reject("Invalid videoQuality. Use one of: lowest, sd, hd, fhd, uhd, highest")
205
- return
206
- }
207
-
208
- if (enableAudio && getPermissionState("microphone") != PermissionState.GRANTED) {
209
- requestPermissionForAlias("microphone", call, "microphonePermsCallback")
210
- return
211
- }
212
-
213
- doStartRecording(call, enableAudio, videoQuality)
214
- }
215
-
216
- @PermissionCallback
217
- private fun microphonePermsCallback(call: PluginCall) {
218
- if (getPermissionState("microphone") == PermissionState.GRANTED) {
219
- val enableAudio = call.getBoolean("enableAudio") ?: false
220
- val videoQuality =
221
- parseVideoRecordingQuality(call.getString("videoQuality"))
222
- ?: run {
223
- call.reject("Invalid videoQuality. Use one of: lowest, sd, hd, fhd, uhd, highest")
224
- return
225
- }
226
- doStartRecording(call, enableAudio, videoQuality)
227
- } else {
228
- call.reject("Microphone permission is required for audio recording")
229
- }
230
- }
231
-
232
-
233
- /**
234
- * Helper method to start recording after ensuring permissions are granted.
235
- */
236
- private fun doStartRecording(
237
- call: PluginCall,
238
- enableAudio: Boolean,
239
- videoQuality: VideoRecordingQuality
240
- ) {
241
- pluginScope.launch {
242
- implementation.startRecordingAsync(enableAudio, videoQuality).fold(
243
- onSuccess = { call.resolve() },
244
- onError = { error ->
245
- call.reject("Failed to start recording: ${error.message}", error)
246
- }
247
- )
248
- }
249
- }
250
-
251
- @PluginMethod
252
- fun stopRecording(call: PluginCall) {
253
- pluginScope.launch {
254
- implementation.stopRecordingAsync().fold(
255
- onSuccess = { result -> call.resolve(result) },
256
- onError = { error ->
257
- call.reject("Failed to stop recording: ${error.message}", error)
147
+ Log.d(TAG, "captureSample failed after ${System.currentTimeMillis() - timeStart}ms")
258
148
  }
259
149
  )
260
150
  }
@@ -399,8 +289,14 @@ class CameraViewPlugin : Plugin() {
399
289
  * Called by the CameraView when a barcode is detected.
400
290
  */
401
291
  fun notifyBarcodeDetected(result: BarcodeDetectionResult) {
292
+ val rawBytesArray = JSArray().apply {
293
+ result.rawBytes.forEach { put(it.toInt() and 0xFF) }
294
+ }
295
+
402
296
  val jsObject = JSObject().apply {
403
297
  put("value", result.value)
298
+ put("displayValue", result.displayValue)
299
+ put("rawBytes", rawBytesArray)
404
300
  put("type", result.type)
405
301
  put("boundingRect", JSObject().apply {
406
302
  put("x", result.boundingRect.x)
@@ -5,6 +5,7 @@ package com.michaelwolz.capacitorcameraview.model
5
5
  */
6
6
  data class BarcodeDetectionResult(
7
7
  val value: String,
8
+ val rawBytes: ByteArray,
8
9
  val displayValue: String,
9
10
  val type: String,
10
11
  val boundingRect: WebBoundingRect
@@ -13,7 +13,6 @@ import androidx.camera.view.PreviewView
13
13
  import com.getcapacitor.PluginCall
14
14
  import com.google.mlkit.vision.barcode.common.Barcode
15
15
  import com.michaelwolz.capacitorcameraview.model.CameraSessionConfiguration
16
- import com.michaelwolz.capacitorcameraview.model.VideoRecordingQuality
17
16
  import com.michaelwolz.capacitorcameraview.model.WebBoundingRect
18
17
  import java.io.ByteArrayOutputStream
19
18
 
@@ -172,7 +171,7 @@ fun sessionConfigFromPluginCall(call: PluginCall): CameraSessionConfiguration {
172
171
  jsonArray.optString(i)?.let { stringTypes.add(it) }
173
172
  }
174
173
  val converted = convertToNativeBarcodeFormats(stringTypes)
175
- converted.ifEmpty { null }
174
+ if (converted.isNotEmpty()) converted else null
176
175
  }
177
176
 
178
177
  return CameraSessionConfiguration(
@@ -251,23 +250,4 @@ fun imageProxyToBase64(image: ImageProxy, quality: Int, rotationDegrees: Int): S
251
250
  // Ensure bitmap is always recycled
252
251
  bitmap.recycle()
253
252
  }
254
- }
255
-
256
- /**
257
- * Parses a string representation of video recording quality into a [VideoRecordingQuality] enum.
258
- */
259
- fun parseVideoRecordingQuality(rawValue: String?): VideoRecordingQuality? {
260
- if (rawValue == null) {
261
- return VideoRecordingQuality.HIGHEST
262
- }
263
-
264
- return when (rawValue) {
265
- "lowest" -> VideoRecordingQuality.LOWEST
266
- "sd" -> VideoRecordingQuality.SD
267
- "hd" -> VideoRecordingQuality.HD
268
- "fhd" -> VideoRecordingQuality.FHD
269
- "uhd" -> VideoRecordingQuality.UHD
270
- "highest" -> VideoRecordingQuality.HIGHEST
271
- else -> null
272
- }
273
253
  }