expo-camera 12.3.0 → 13.0.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 (54) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/camera/CameraExceptions.kt +7 -0
  4. package/android/src/main/java/expo/modules/camera/CameraViewHelper.kt +41 -55
  5. package/android/src/main/java/expo/modules/camera/CameraViewModule.kt +262 -0
  6. package/android/src/main/java/expo/modules/camera/Events.kt +32 -0
  7. package/android/src/main/java/expo/modules/camera/{Constants.kt → ExifTags.kt} +0 -39
  8. package/android/src/main/java/expo/modules/camera/ExpoCameraView.kt +197 -98
  9. package/android/src/main/java/expo/modules/camera/Options.kt +23 -0
  10. package/android/src/main/java/expo/modules/camera/tasks/ResolveTakenPictureAsyncTask.kt +28 -44
  11. package/android/src/main/java/expo/modules/camera/utils/BarCodeScannerListMappers.kt +13 -0
  12. package/build/Camera.d.ts.map +1 -1
  13. package/build/Camera.js.map +1 -1
  14. package/build/Camera.types.d.ts +43 -2
  15. package/build/Camera.types.d.ts.map +1 -1
  16. package/build/Camera.types.js.map +1 -1
  17. package/build/ExponentCameraManager.d.ts.map +1 -1
  18. package/build/ExponentCameraManager.js +2 -2
  19. package/build/ExponentCameraManager.js.map +1 -1
  20. package/build/useWebQRScanner.d.ts.map +1 -1
  21. package/build/useWebQRScanner.js +2 -0
  22. package/build/useWebQRScanner.js.map +1 -1
  23. package/expo-module.config.json +10 -0
  24. package/ios/EXCamera/CameraViewModule.swift +333 -0
  25. package/ios/EXCamera/EXCamera.h +3 -5
  26. package/ios/EXCamera/EXCamera.m +32 -4
  27. package/ios/EXCamera/TakePictureOptions.swift +23 -0
  28. package/ios/EXCamera/Utilities/EXCameraUtils.h +3 -3
  29. package/ios/EXCamera.podspec +8 -2
  30. package/package.json +3 -4
  31. package/plugin/build/withCamera.js +1 -1
  32. package/src/Camera.tsx +1 -0
  33. package/src/Camera.types.ts +43 -2
  34. package/src/ExponentCameraManager.ts +2 -3
  35. package/src/useWebQRScanner.ts +2 -0
  36. package/android/src/main/java/expo/modules/camera/CameraModule.kt +0 -217
  37. package/android/src/main/java/expo/modules/camera/CameraPackage.kt +0 -10
  38. package/android/src/main/java/expo/modules/camera/CameraViewManager.kt +0 -116
  39. package/android/src/main/java/expo/modules/camera/events/BarCodeScannedEvent.kt +0 -50
  40. package/android/src/main/java/expo/modules/camera/events/CameraMountErrorEvent.kt +0 -33
  41. package/android/src/main/java/expo/modules/camera/events/CameraReadyEvent.kt +0 -23
  42. package/android/src/main/java/expo/modules/camera/events/FaceDetectionErrorEvent.kt +0 -39
  43. package/android/src/main/java/expo/modules/camera/events/FacesDetectedEvent.kt +0 -46
  44. package/android/src/main/java/expo/modules/camera/events/PictureSavedEvent.kt +0 -41
  45. package/ios/EXCamera/EXCameraManager.h +0 -9
  46. package/ios/EXCamera/EXCameraManager.m +0 -435
  47. package/ios/EXCamera.xcframework/Info.plist +0 -40
  48. package/ios/EXCamera.xcframework/ios-arm64/EXCamera.framework/EXCamera +0 -0
  49. package/ios/EXCamera.xcframework/ios-arm64/EXCamera.framework/Info.plist +0 -0
  50. package/ios/EXCamera.xcframework/ios-arm64/EXCamera.framework/Modules/module.modulemap +0 -6
  51. package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/EXCamera +0 -0
  52. package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/Info.plist +0 -0
  53. package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/Modules/module.modulemap +0 -6
  54. package/unimodule.json +0 -4
@@ -1,79 +1,92 @@
1
1
  package expo.modules.camera
2
2
 
3
- import android.annotation.SuppressLint
3
+ import android.Manifest
4
4
  import android.content.Context
5
5
  import android.graphics.Color
6
- import android.Manifest
7
- import android.media.CamcorderProfile
6
+ import android.graphics.SurfaceTexture
8
7
  import android.net.Uri
9
- import android.os.Build
10
8
  import android.os.Bundle
11
9
  import android.view.View
12
-
13
10
  import com.google.android.cameraview.CameraView
14
-
15
- import expo.modules.camera.CameraViewHelper.emitCameraReadyEvent
16
- import expo.modules.camera.CameraViewHelper.emitMountErrorEvent
17
- import expo.modules.camera.CameraViewHelper.getCorrectCameraRotation
18
- import expo.modules.camera.CameraViewHelper.emitPictureSavedEvent
19
11
  import expo.modules.camera.CameraViewHelper.getCamcorderProfile
20
- import expo.modules.camera.CameraViewHelper.emitBarCodeReadEvent
21
- import expo.modules.camera.CameraViewHelper.emitFacesDetectedEvent
22
- import expo.modules.camera.CameraViewHelper.emitFaceDetectionErrorEvent
12
+ import expo.modules.camera.CameraViewHelper.getCorrectCameraRotation
13
+ import expo.modules.camera.tasks.BarCodeScannerAsyncTask
23
14
  import expo.modules.camera.tasks.BarCodeScannerAsyncTaskDelegate
24
15
  import expo.modules.camera.tasks.FaceDetectorAsyncTaskDelegate
16
+ import expo.modules.camera.tasks.FaceDetectorTask
25
17
  import expo.modules.camera.tasks.PictureSavedDelegate
26
18
  import expo.modules.camera.tasks.ResolveTakenPictureAsyncTask
27
- import expo.modules.camera.tasks.BarCodeScannerAsyncTask
28
- import expo.modules.camera.tasks.FaceDetectorTask
29
19
  import expo.modules.camera.utils.FileSystemUtils
30
20
  import expo.modules.camera.utils.ImageDimensions
31
- import expo.modules.core.ModuleRegistryDelegate
32
- import expo.modules.core.Promise
33
21
  import expo.modules.core.interfaces.LifecycleEventListener
34
22
  import expo.modules.core.interfaces.services.UIManager
35
- import expo.modules.core.interfaces.services.EventEmitter
36
- import expo.modules.interfaces.camera.CameraViewInterface
23
+ import expo.modules.core.utilities.EmulatorUtilities
37
24
  import expo.modules.interfaces.barcodescanner.BarCodeScannerInterface
38
- import expo.modules.interfaces.facedetector.FaceDetectorInterface
39
25
  import expo.modules.interfaces.barcodescanner.BarCodeScannerProviderInterface
40
- import expo.modules.interfaces.barcodescanner.BarCodeScannerSettings
41
26
  import expo.modules.interfaces.barcodescanner.BarCodeScannerResult
27
+ import expo.modules.interfaces.barcodescanner.BarCodeScannerSettings
28
+ import expo.modules.interfaces.camera.CameraViewInterface
29
+ import expo.modules.interfaces.facedetector.FaceDetectorInterface
42
30
  import expo.modules.interfaces.facedetector.FaceDetectorProviderInterface
43
- import expo.modules.interfaces.permissions.Permissions
44
-
31
+ import expo.modules.kotlin.AppContext
32
+ import expo.modules.kotlin.Promise
33
+ import expo.modules.kotlin.views.ExpoView
45
34
  import java.io.File
46
35
  import java.io.IOException
47
- import java.lang.Exception
48
36
  import java.util.*
49
37
  import java.util.concurrent.ConcurrentHashMap
50
38
  import java.util.concurrent.ConcurrentLinkedQueue
51
-
52
- private const val MUTE_KEY = "mute"
53
- private const val QUALITY_KEY = "quality"
54
- private const val FAST_MODE_KEY = "fastMode"
55
- private const val MAX_DURATION_KEY = "maxDuration"
56
- private const val MAX_FILE_SIZE_KEY = "maxFileSize"
57
- private const val VIDEO_BITRATE_KEY = "videoBitrate"
39
+ import expo.modules.camera.utils.mapX
40
+ import expo.modules.camera.utils.mapY
41
+ import kotlin.math.roundToInt
42
+ import android.view.WindowManager
43
+ import expo.modules.interfaces.barcodescanner.BarCodeScannerResult.BoundingBox
44
+ import expo.modules.kotlin.viewevent.EventDispatcher
58
45
 
59
46
  class ExpoCameraView(
60
- themedReactContext: Context,
61
- private val moduleRegistryDelegate: ModuleRegistryDelegate = ModuleRegistryDelegate()
62
- ) : CameraView(themedReactContext, true),
47
+ context: Context,
48
+ appContext: AppContext,
49
+ ) : ExpoView(context, appContext),
63
50
  LifecycleEventListener,
64
51
  BarCodeScannerAsyncTaskDelegate,
65
52
  FaceDetectorAsyncTaskDelegate,
66
53
  PictureSavedDelegate,
67
54
  CameraViewInterface {
55
+ internal val cameraView = CameraView(context, true)
68
56
 
69
- private inline fun <reified T> moduleRegistry() = moduleRegistryDelegate.getFromModuleRegistry<T>()
70
57
  private val pictureTakenPromises: Queue<Promise> = ConcurrentLinkedQueue()
71
- private val pictureTakenOptions: MutableMap<Promise, Map<String, Any>> = ConcurrentHashMap()
58
+ private val pictureTakenOptions: MutableMap<Promise, PictureOptions> = ConcurrentHashMap()
72
59
  private val pictureTakenDirectories: MutableMap<Promise, File> = ConcurrentHashMap()
73
60
  private var videoRecordedPromise: Promise? = null
74
61
  private var isPaused = false
75
62
  private var isNew = true
76
- private val eventEmitter: EventEmitter by moduleRegistry()
63
+
64
+ private val onCameraReady by EventDispatcher<Unit>()
65
+ private val onMountError by EventDispatcher<CameraMountErrorEvent>()
66
+ private val onBarCodeScanned by EventDispatcher<BarCodeScannedEvent>(
67
+ /**
68
+ * We want every distinct barcode to be reported to the JS listener.
69
+ * If we return some static value as a coalescing key there may be two barcode events
70
+ * containing two different barcodes waiting to be transmitted to JS
71
+ * that would get coalesced (because both of them would have the same coalescing key).
72
+ * So let's differentiate them with a hash of the contents (mod short's max value).
73
+ */
74
+ coalescingKey = { event -> (event.data.hashCode() % Short.MAX_VALUE).toShort() }
75
+ )
76
+ private val onFacesDetected by EventDispatcher<FacesDetectedEvent>(
77
+ /**
78
+ * Should events about detected faces coalesce, the best strategy will be
79
+ * to ensure that events with different faces count are always being transmitted.
80
+ */
81
+ coalescingKey = { event -> (event.faces.size % Short.MAX_VALUE).toShort() }
82
+ )
83
+ private val onFaceDetectionError by EventDispatcher<FaceDetectionErrorEvent>()
84
+ private val onPictureSaved by EventDispatcher<PictureSavedEvent>(
85
+ coalescingKey = { event ->
86
+ val uriHash = event.data.getString("uri")?.hashCode() ?: -1
87
+ (uriHash % Short.MAX_VALUE).toShort()
88
+ }
89
+ )
77
90
 
78
91
  // Concurrency lock for scanners to avoid flooding the runtime
79
92
  @Volatile
@@ -90,16 +103,14 @@ class ExpoCameraView(
90
103
  private var mShouldScanBarCodes = false
91
104
 
92
105
  override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
93
- val preview = view ?: return
94
- setBackgroundColor(Color.BLACK)
95
106
  val width = right - left
96
107
  val height = bottom - top
97
- preview.layout(0, 0, width, height)
98
- }
99
108
 
100
- @SuppressLint("MissingSuperCall")
101
- override fun requestLayout() {
102
- // React handles this for us, so we don't need to call super.requestLayout();
109
+ cameraView.layout(0, 0, width, height)
110
+ cameraView.setBackgroundColor(Color.BLACK)
111
+
112
+ val preview = cameraView.view ?: return
113
+ preview.layout(0, 0, width, height)
103
114
  }
104
115
 
105
116
  override fun onViewAdded(child: View) {
@@ -108,7 +119,7 @@ class ExpoCameraView(
108
119
  // while we need this preview to be rendered last beneath all other children
109
120
 
110
121
  // child is not preview
111
- if (this.view === child || this.view == null) {
122
+ if (cameraView === child) {
112
123
  return
113
124
  }
114
125
 
@@ -116,27 +127,27 @@ class ExpoCameraView(
116
127
  val childrenToBeReordered = mutableListOf<View>()
117
128
  for (i in 0 until this.childCount) {
118
129
  val childView = getChildAt(i)
119
- if (i == 0 && childView === this.view) {
130
+ if (i == 0 && childView === cameraView) {
120
131
  // preview is already first in children list - do not reorder anything
121
132
  return
122
133
  }
123
- if (childView !== this.view) {
134
+ if (childView !== cameraView) {
124
135
  childrenToBeReordered.add(childView)
125
136
  }
126
137
  }
127
138
  for (childView in childrenToBeReordered) {
128
139
  bringChildToFront(childView)
129
140
  }
130
- requestLayout()
131
- invalidate()
141
+ cameraView.requestLayout()
142
+ cameraView.invalidate()
132
143
  }
133
144
 
134
- fun takePicture(options: Map<String, Any>, promise: Promise, cacheDirectory: File) {
145
+ fun takePicture(options: PictureOptions, promise: Promise, cacheDirectory: File) {
135
146
  pictureTakenPromises.add(promise)
136
147
  pictureTakenOptions[promise] = options
137
148
  pictureTakenDirectories[promise] = cacheDirectory
138
149
  try {
139
- super.takePicture()
150
+ cameraView.takePicture()
140
151
  } catch (e: Exception) {
141
152
  pictureTakenPromises.remove(promise)
142
153
  pictureTakenOptions.remove(promise)
@@ -146,29 +157,21 @@ class ExpoCameraView(
146
157
  }
147
158
 
148
159
  override fun onPictureSaved(response: Bundle) {
149
- emitPictureSavedEvent(eventEmitter, this, response)
160
+ onPictureSaved(PictureSavedEvent(response.getInt("id"), response.getBundle("data")!!))
150
161
  }
151
162
 
152
- fun record(options: Map<String?, Any?>, promise: Promise, cacheDirectory: File) {
163
+ fun record(options: RecordingOptions, promise: Promise, cacheDirectory: File) {
153
164
  try {
154
165
  val path = FileSystemUtils.generateOutputPath(cacheDirectory, "Camera", ".mp4")
155
- val maxDuration = options[MAX_DURATION_KEY]?.let { it as Double } ?: -1.0
156
- val maxFileSize = options[MAX_FILE_SIZE_KEY]?.let { it as Double } ?: -1.0
157
- val profile = if (options[QUALITY_KEY] != null) {
158
- getCamcorderProfile(cameraId, (options[QUALITY_KEY] as Double).toInt())
159
- } else {
160
- CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH)
161
- }
162
- options[VIDEO_BITRATE_KEY]?.let { profile.videoBitRate = (it as Double).toInt() }
163
- val muteValue = options[MUTE_KEY] as Boolean?
164
- val recordAudio = muteValue != true
165
- if (super.record(path, maxDuration.toInt() * 1000, maxFileSize.toInt(), recordAudio, profile)) {
166
+ val profile = getCamcorderProfile(cameraView.cameraId, options.quality)
167
+ options.videoBitrate?.let { profile.videoBitRate = it }
168
+ if (cameraView.record(path, options.maxDuration * 1000, options.maxFileSize, !options.muteValue, profile)) {
166
169
  videoRecordedPromise = promise
167
170
  } else {
168
- promise.reject("E_RECORDING_FAILED", "Starting video recording failed. Another recording might be in progress.")
171
+ promise.reject("E_RECORDING_FAILED", "Starting video recording failed. Another recording might be in progress.", null)
169
172
  }
170
173
  } catch (e: IOException) {
171
- promise.reject("E_RECORDING_FAILED", "Starting video recording failed - could not create video file.")
174
+ promise.reject("E_RECORDING_FAILED", "Starting video recording failed - could not create video file.", null)
172
175
  }
173
176
  }
174
177
 
@@ -178,22 +181,106 @@ class ExpoCameraView(
178
181
  * Additionally supports [codabar, code128, maxicode, rss14, rssexpanded, upc_a, upc_ean]
179
182
  */
180
183
  private fun initBarCodeScanner() {
181
- val barCodeScannerProvider: BarCodeScannerProviderInterface? by moduleRegistry()
184
+ val barCodeScannerProvider = appContext.legacyModule<BarCodeScannerProviderInterface>()
182
185
  barCodeScanner = barCodeScannerProvider?.createBarCodeDetectorWithContext(context)
183
186
  }
184
187
 
185
188
  fun setShouldScanBarCodes(shouldScanBarCodes: Boolean) {
186
189
  mShouldScanBarCodes = shouldScanBarCodes
187
- scanning = mShouldScanBarCodes || shouldDetectFaces
190
+ cameraView.scanning = mShouldScanBarCodes || shouldDetectFaces
188
191
  }
189
192
 
190
193
  fun setBarCodeScannerSettings(settings: BarCodeScannerSettings) {
191
194
  barCodeScanner?.setSettings(settings)
192
195
  }
193
196
 
197
+ // Even = portrait, odd = landscape
198
+ private fun getDeviceOrientation() =
199
+ (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.rotation
200
+
201
+ private fun transformBarCodeScannerResultToViewCoordinates(barCode: BarCodeScannerResult) {
202
+ val cornerPoints = barCode.cornerPoints
203
+
204
+ // For some reason they're swapped, I don't know anymore...
205
+ val cameraWidth = barCode.referenceImageHeight
206
+ val cameraHeight = barCode.referenceImageWidth
207
+
208
+ val facingBack = cameraView.facing == CameraView.FACING_BACK
209
+ val facingFront = cameraView.facing == CameraView.FACING_FRONT
210
+ val portrait = getDeviceOrientation() % 2 == 0
211
+ val landscape = getDeviceOrientation() % 2 == 1
212
+
213
+ if (facingBack && portrait) {
214
+ cornerPoints.mapX { cameraWidth - cornerPoints[it] }
215
+ }
216
+ if (facingBack && landscape) {
217
+ cornerPoints.mapY { cameraHeight - cornerPoints[it] }
218
+ }
219
+ if (facingFront) {
220
+ cornerPoints.mapX { cameraWidth - cornerPoints[it] }
221
+ cornerPoints.mapY { cameraHeight - cornerPoints[it] }
222
+ }
223
+
224
+ val scaleX = width / cameraWidth.toDouble()
225
+ val scaleY = height / cameraHeight.toDouble()
226
+
227
+ cornerPoints.mapX {
228
+ (cornerPoints[it] * scaleX)
229
+ .roundToInt()
230
+ }
231
+ cornerPoints.mapY {
232
+ (cornerPoints[it] * scaleY)
233
+ .roundToInt()
234
+ }
235
+
236
+ barCode.cornerPoints = cornerPoints
237
+ }
238
+
239
+ private fun getCornerPointsAndBoundingBox(cornerPoints: List<Int>, boundingBox: BoundingBox): Pair<ArrayList<Bundle>, Bundle> {
240
+ val density = cameraView.resources.displayMetrics.density
241
+ val convertedCornerPoints = ArrayList<Bundle>()
242
+ for (i in cornerPoints.indices step 2) {
243
+ val y = cornerPoints[i].toFloat() / density
244
+ val x = cornerPoints[i + 1].toFloat() / density
245
+ convertedCornerPoints.add(
246
+ Bundle().apply {
247
+ putFloat("x", x)
248
+ putFloat("y", y)
249
+ }
250
+ )
251
+ }
252
+ val boundingBoxBundle = Bundle().apply {
253
+ putParcelable(
254
+ "origin",
255
+ Bundle().apply {
256
+ putFloat("x", boundingBox.x.toFloat() / density)
257
+ putFloat("y", boundingBox.y.toFloat() / density)
258
+ }
259
+ )
260
+ putParcelable(
261
+ "size",
262
+ Bundle().apply {
263
+ putFloat("width", boundingBox.width.toFloat() / density)
264
+ putFloat("height", boundingBox.height.toFloat() / density)
265
+ }
266
+ )
267
+ }
268
+ return convertedCornerPoints to boundingBoxBundle
269
+ }
270
+
194
271
  override fun onBarCodeScanned(barCode: BarCodeScannerResult) {
195
272
  if (mShouldScanBarCodes) {
196
- emitBarCodeReadEvent(eventEmitter, this, barCode)
273
+ transformBarCodeScannerResultToViewCoordinates(barCode)
274
+ val (cornerPoints, boundingBox) = getCornerPointsAndBoundingBox(barCode.cornerPoints, barCode.boundingBox)
275
+ onBarCodeScanned(
276
+ BarCodeScannedEvent(
277
+ target = id,
278
+ data = barCode.value,
279
+ type = barCode.type,
280
+ cornerPoints = cornerPoints,
281
+ boundingBox = boundingBox
282
+ )
283
+ )
197
284
  }
198
285
  }
199
286
 
@@ -201,16 +288,20 @@ class ExpoCameraView(
201
288
  barCodeScannerTaskLock = false
202
289
  }
203
290
 
204
- override fun getPreviewSizeAsArray() = intArrayOf(previewSize.width, previewSize.height)
291
+ override fun setPreviewTexture(surfaceTexture: SurfaceTexture?) {
292
+ cameraView.setPreviewTexture(surfaceTexture)
293
+ }
294
+
295
+ override fun getPreviewSizeAsArray() = intArrayOf(cameraView.previewSize.width, cameraView.previewSize.height)
205
296
 
206
297
  override fun onHostResume() {
207
298
  if (hasCameraPermissions()) {
208
- if (isPaused && !isCameraOpened || isNew) {
299
+ if (isPaused && !cameraView.isCameraOpened || isNew) {
209
300
  isPaused = false
210
301
  isNew = false
211
- if (!Build.FINGERPRINT.contains("generic")) {
212
- start()
213
- val faceDetectorProvider: FaceDetectorProviderInterface? by moduleRegistry()
302
+ if (!EmulatorUtilities.isRunningOnEmulator()) {
303
+ cameraView.start()
304
+ val faceDetectorProvider = appContext.legacyModule<FaceDetectorProviderInterface>()
214
305
  faceDetector = faceDetectorProvider?.createFaceDetectorWithContext(context)
215
306
  pendingFaceDetectorSettings?.let {
216
307
  faceDetector?.setSettings(it)
@@ -219,31 +310,31 @@ class ExpoCameraView(
219
310
  }
220
311
  }
221
312
  } else {
222
- emitMountErrorEvent(eventEmitter, this, "Camera permissions not granted - component could not be rendered.")
313
+ onMountError(CameraMountErrorEvent("Camera permissions not granted - component could not be rendered."))
223
314
  }
224
315
  }
225
316
 
226
317
  override fun onHostPause() {
227
- if (!isPaused && isCameraOpened) {
318
+ if (!isPaused && cameraView.isCameraOpened) {
228
319
  faceDetector?.release()
229
320
  isPaused = true
230
- stop()
321
+ cameraView.stop()
231
322
  }
232
323
  }
233
324
 
234
325
  override fun onHostDestroy() {
235
326
  faceDetector?.release()
236
- stop()
327
+ cameraView.stop()
237
328
  }
238
329
 
239
330
  private fun hasCameraPermissions(): Boolean {
240
- val permissionsManager: Permissions by moduleRegistry()
331
+ val permissionsManager = appContext.permissions ?: return false
241
332
  return permissionsManager.hasGrantedPermissions(Manifest.permission.CAMERA)
242
333
  }
243
334
 
244
335
  fun setShouldDetectFaces(shouldDetectFaces: Boolean) {
245
336
  this.shouldDetectFaces = shouldDetectFaces
246
- scanning = mShouldScanBarCodes || shouldDetectFaces
337
+ cameraView.scanning = mShouldScanBarCodes || shouldDetectFaces
247
338
  }
248
339
 
249
340
  fun setFaceDetectorSettings(settings: Map<String, Any>?) {
@@ -254,14 +345,20 @@ class ExpoCameraView(
254
345
 
255
346
  override fun onFacesDetected(faces: List<Bundle>) {
256
347
  if (shouldDetectFaces) {
257
- emitFacesDetectedEvent(eventEmitter, this, faces)
348
+ onFacesDetected(
349
+ FacesDetectedEvent(
350
+ "face",
351
+ faces,
352
+ id
353
+ )
354
+ )
258
355
  }
259
356
  }
260
357
 
261
358
  override fun onFaceDetectionError(faceDetector: FaceDetectorInterface) {
262
359
  faceDetectorTaskLock = false
263
360
  if (shouldDetectFaces) {
264
- emitFaceDetectionErrorEvent(eventEmitter, this, faceDetector)
361
+ onFaceDetectionError(FaceDetectionErrorEvent(true))
265
362
  }
266
363
  }
267
364
 
@@ -272,22 +369,24 @@ class ExpoCameraView(
272
369
  init {
273
370
  initBarCodeScanner()
274
371
  isChildrenDrawingOrderEnabled = true
275
- val uIManager: UIManager by moduleRegistry()
276
- uIManager.registerLifecycleEventListener(this)
277
- addCallback(object : Callback() {
372
+ val uIManager = appContext.legacyModule<UIManager>()
373
+ uIManager!!.registerLifecycleEventListener(this)
374
+ cameraView.addCallback(object : CameraView.Callback() {
278
375
  override fun onCameraOpened(cameraView: CameraView) {
279
- emitCameraReadyEvent(eventEmitter, cameraView)
376
+ onCameraReady(Unit)
280
377
  }
281
378
 
282
379
  override fun onMountError(cameraView: CameraView) {
283
- emitMountErrorEvent(eventEmitter, cameraView, "Camera component could not be rendered - is there any other instance running?")
380
+ onMountError(
381
+ CameraMountErrorEvent("Camera component could not be rendered - is there any other instance running?")
382
+ )
284
383
  }
285
384
 
286
385
  override fun onPictureTaken(cameraView: CameraView, data: ByteArray) {
287
- val promise = pictureTakenPromises.poll()
386
+ val promise = pictureTakenPromises.poll() ?: return
288
387
  val cacheDirectory = pictureTakenDirectories.remove(promise)
289
- val options = pictureTakenOptions.remove(promise) as MutableMap
290
- if (options.containsKey(FAST_MODE_KEY) && options[FAST_MODE_KEY] as Boolean) {
388
+ val options = pictureTakenOptions.remove(promise)!!
389
+ if (options.fastMode) {
291
390
  promise.resolve(null)
292
391
  }
293
392
  cacheDirectory?.let {
@@ -307,23 +406,23 @@ class ExpoCameraView(
307
406
  }
308
407
 
309
408
  override fun onFramePreview(cameraView: CameraView, data: ByteArray, width: Int, height: Int, rotation: Int) {
310
- val correctRotation = getCorrectCameraRotation(rotation, facing)
311
- if (mShouldScanBarCodes && !barCodeScannerTaskLock && cameraView is BarCodeScannerAsyncTaskDelegate) {
409
+ val correctRotation = getCorrectCameraRotation(rotation, cameraView.facing)
410
+ if (mShouldScanBarCodes && !barCodeScannerTaskLock) {
312
411
  barCodeScannerTaskLock = true
313
- val delegate = cameraView as BarCodeScannerAsyncTaskDelegate
314
- barCodeScanner?.let { BarCodeScannerAsyncTask(delegate, it, data, width, height, rotation).execute() }
412
+ barCodeScanner?.let { BarCodeScannerAsyncTask(this@ExpoCameraView, it, data, width, height, rotation).execute() }
315
413
  }
316
- if (shouldDetectFaces && !faceDetectorTaskLock && cameraView is FaceDetectorAsyncTaskDelegate) {
414
+ if (shouldDetectFaces && !faceDetectorTaskLock) {
317
415
  faceDetectorTaskLock = true
318
416
  val density = cameraView.resources.displayMetrics.density
319
- val dimensions = ImageDimensions(width, height, correctRotation, facing)
417
+ val dimensions = ImageDimensions(width, height, correctRotation, cameraView.facing)
320
418
  val scaleX = cameraView.width.toDouble() / (dimensions.width * density)
321
419
  val scaleY = cameraView.height.toDouble() / (dimensions.height * density)
322
- val delegate = cameraView as FaceDetectorAsyncTaskDelegate
323
- val task = faceDetector?.let { FaceDetectorTask(delegate, it, data, width, height, correctRotation, facing == FACING_FRONT, scaleX, scaleY) }
420
+ val task = faceDetector?.let { FaceDetectorTask(this@ExpoCameraView, it, data, width, height, correctRotation, cameraView.facing == CameraView.FACING_FRONT, scaleX, scaleY) }
324
421
  task?.execute()
325
422
  }
326
423
  }
327
424
  })
425
+
426
+ addView(cameraView)
328
427
  }
329
428
  }
@@ -0,0 +1,23 @@
1
+ package expo.modules.camera
2
+
3
+ import android.media.CamcorderProfile
4
+ import expo.modules.kotlin.records.Field
5
+ import expo.modules.kotlin.records.Record
6
+
7
+ class PictureOptions : Record {
8
+ @Field val quality: Double = 1.0
9
+ @Field val base64: Boolean = false
10
+ @Field val exif: Boolean = false
11
+ @Field val additionalExif: Map<String, Any>? = null
12
+ @Field val skipProcessing: Boolean = false
13
+ @Field val fastMode: Boolean = false
14
+ @Field val id: Int? = null
15
+ }
16
+
17
+ class RecordingOptions : Record {
18
+ @Field val maxDuration: Int = -1
19
+ @Field val maxFileSize: Int = -1
20
+ @Field val quality: Int = CamcorderProfile.QUALITY_HIGH
21
+ @Field val muteValue: Boolean = false
22
+ @Field val videoBitrate: Int? = null
23
+ }