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.
- package/CHANGELOG.md +29 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/camera/CameraExceptions.kt +7 -0
- package/android/src/main/java/expo/modules/camera/CameraViewHelper.kt +41 -55
- package/android/src/main/java/expo/modules/camera/CameraViewModule.kt +262 -0
- package/android/src/main/java/expo/modules/camera/Events.kt +32 -0
- package/android/src/main/java/expo/modules/camera/{Constants.kt → ExifTags.kt} +0 -39
- package/android/src/main/java/expo/modules/camera/ExpoCameraView.kt +197 -98
- package/android/src/main/java/expo/modules/camera/Options.kt +23 -0
- package/android/src/main/java/expo/modules/camera/tasks/ResolveTakenPictureAsyncTask.kt +28 -44
- package/android/src/main/java/expo/modules/camera/utils/BarCodeScannerListMappers.kt +13 -0
- package/build/Camera.d.ts.map +1 -1
- package/build/Camera.js.map +1 -1
- package/build/Camera.types.d.ts +43 -2
- package/build/Camera.types.d.ts.map +1 -1
- package/build/Camera.types.js.map +1 -1
- package/build/ExponentCameraManager.d.ts.map +1 -1
- package/build/ExponentCameraManager.js +2 -2
- package/build/ExponentCameraManager.js.map +1 -1
- package/build/useWebQRScanner.d.ts.map +1 -1
- package/build/useWebQRScanner.js +2 -0
- package/build/useWebQRScanner.js.map +1 -1
- package/expo-module.config.json +10 -0
- package/ios/EXCamera/CameraViewModule.swift +333 -0
- package/ios/EXCamera/EXCamera.h +3 -5
- package/ios/EXCamera/EXCamera.m +32 -4
- package/ios/EXCamera/TakePictureOptions.swift +23 -0
- package/ios/EXCamera/Utilities/EXCameraUtils.h +3 -3
- package/ios/EXCamera.podspec +8 -2
- package/package.json +3 -4
- package/plugin/build/withCamera.js +1 -1
- package/src/Camera.tsx +1 -0
- package/src/Camera.types.ts +43 -2
- package/src/ExponentCameraManager.ts +2 -3
- package/src/useWebQRScanner.ts +2 -0
- package/android/src/main/java/expo/modules/camera/CameraModule.kt +0 -217
- package/android/src/main/java/expo/modules/camera/CameraPackage.kt +0 -10
- package/android/src/main/java/expo/modules/camera/CameraViewManager.kt +0 -116
- package/android/src/main/java/expo/modules/camera/events/BarCodeScannedEvent.kt +0 -50
- package/android/src/main/java/expo/modules/camera/events/CameraMountErrorEvent.kt +0 -33
- package/android/src/main/java/expo/modules/camera/events/CameraReadyEvent.kt +0 -23
- package/android/src/main/java/expo/modules/camera/events/FaceDetectionErrorEvent.kt +0 -39
- package/android/src/main/java/expo/modules/camera/events/FacesDetectedEvent.kt +0 -46
- package/android/src/main/java/expo/modules/camera/events/PictureSavedEvent.kt +0 -41
- package/ios/EXCamera/EXCameraManager.h +0 -9
- package/ios/EXCamera/EXCameraManager.m +0 -435
- package/ios/EXCamera.xcframework/Info.plist +0 -40
- package/ios/EXCamera.xcframework/ios-arm64/EXCamera.framework/EXCamera +0 -0
- package/ios/EXCamera.xcframework/ios-arm64/EXCamera.framework/Info.plist +0 -0
- package/ios/EXCamera.xcframework/ios-arm64/EXCamera.framework/Modules/module.modulemap +0 -6
- package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/EXCamera +0 -0
- package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/Info.plist +0 -0
- package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/Modules/module.modulemap +0 -6
- package/unimodule.json +0 -4
|
@@ -1,79 +1,92 @@
|
|
|
1
1
|
package expo.modules.camera
|
|
2
2
|
|
|
3
|
-
import android.
|
|
3
|
+
import android.Manifest
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.graphics.Color
|
|
6
|
-
import android.
|
|
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.
|
|
21
|
-
import expo.modules.camera.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
) :
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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 (
|
|
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 ===
|
|
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 !==
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
160
|
+
onPictureSaved(PictureSavedEvent(response.getInt("id"), response.getBundle("data")!!))
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
fun record(options:
|
|
163
|
+
fun record(options: RecordingOptions, promise: Promise, cacheDirectory: File) {
|
|
153
164
|
try {
|
|
154
165
|
val path = FileSystemUtils.generateOutputPath(cacheDirectory, "Camera", ".mp4")
|
|
155
|
-
val
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
212
|
-
start()
|
|
213
|
-
val faceDetectorProvider
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
276
|
-
uIManager
|
|
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
|
-
|
|
376
|
+
onCameraReady(Unit)
|
|
280
377
|
}
|
|
281
378
|
|
|
282
379
|
override fun onMountError(cameraView: CameraView) {
|
|
283
|
-
|
|
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)
|
|
290
|
-
if (options.
|
|
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
|
|
409
|
+
val correctRotation = getCorrectCameraRotation(rotation, cameraView.facing)
|
|
410
|
+
if (mShouldScanBarCodes && !barCodeScannerTaskLock) {
|
|
312
411
|
barCodeScannerTaskLock = true
|
|
313
|
-
|
|
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
|
|
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
|
|
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
|
+
}
|