expo-camera 12.0.2 → 12.1.2
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 +23 -2
- package/README.md +1 -1
- package/android/build.gradle +22 -19
- package/android/src/main/java/expo/modules/camera/CameraModule.kt +217 -0
- package/android/src/main/java/expo/modules/camera/CameraPackage.kt +10 -0
- package/android/src/main/java/expo/modules/camera/CameraViewHelper.kt +139 -0
- package/android/src/main/java/expo/modules/camera/CameraViewManager.kt +116 -0
- package/android/src/main/java/expo/modules/camera/Constants.kt +184 -0
- package/android/src/main/java/expo/modules/camera/ExpoCameraView.kt +329 -0
- package/android/src/main/java/expo/modules/camera/events/BarCodeScannedEvent.kt +50 -0
- package/android/src/main/java/expo/modules/camera/events/CameraMountErrorEvent.kt +33 -0
- package/android/src/main/java/expo/modules/camera/events/CameraReadyEvent.kt +23 -0
- package/android/src/main/java/expo/modules/camera/events/FaceDetectionErrorEvent.kt +39 -0
- package/android/src/main/java/expo/modules/camera/events/FacesDetectedEvent.kt +46 -0
- package/android/src/main/java/expo/modules/camera/events/PictureSavedEvent.kt +41 -0
- package/android/src/main/java/expo/modules/camera/tasks/BarCodeScannerAsyncTask.kt +26 -0
- package/android/src/main/java/expo/modules/camera/tasks/BarCodeScannerAsyncTaskDelegate.kt +8 -0
- package/android/src/main/java/expo/modules/camera/tasks/FaceDetectorAsyncTaskDelegate.kt +10 -0
- package/android/src/main/java/expo/modules/camera/tasks/FaceDetectorTask.kt +34 -0
- package/android/src/main/java/expo/modules/camera/tasks/PictureSavedDelegate.kt +7 -0
- package/android/src/main/java/expo/modules/camera/tasks/ResolveTakenPictureAsyncTask.kt +231 -0
- package/android/src/main/java/expo/modules/camera/utils/FileSystemUtils.kt +23 -0
- package/android/src/main/java/expo/modules/camera/utils/ImageDimensions.kt +14 -0
- package/build/ExponentCameraManager.web.js +3 -1
- package/build/ExponentCameraManager.web.js.map +1 -1
- 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_x86_64-simulator/EXCamera.framework/EXCamera +0 -0
- package/ios/EXCamera.xcframework/ios-arm64_x86_64-simulator/EXCamera.framework/Info.plist +0 -0
- package/package.json +7 -5
- package/plugin/build/withCamera.d.ts +2 -1
- package/plugin/build/withCamera.js +50 -30
- package/plugin/src/withCamera.ts +66 -27
- package/src/ExponentCameraManager.web.ts +3 -2
- package/src/{types → ts-declarations}/image-capture.d.ts +0 -0
- package/src/ts-declarations/lib.dom.d.ts +34 -0
- package/android/src/main/java/expo/modules/camera/CameraModule.java +0 -359
- package/android/src/main/java/expo/modules/camera/CameraPackage.java +0 -23
- package/android/src/main/java/expo/modules/camera/CameraViewHelper.java +0 -294
- package/android/src/main/java/expo/modules/camera/CameraViewManager.java +0 -142
- package/android/src/main/java/expo/modules/camera/ExpoCameraView.java +0 -376
- package/android/src/main/java/expo/modules/camera/events/BarCodeScannedEvent.java +0 -59
- package/android/src/main/java/expo/modules/camera/events/CameraMountErrorEvent.java +0 -38
- package/android/src/main/java/expo/modules/camera/events/CameraReadyEvent.java +0 -30
- package/android/src/main/java/expo/modules/camera/events/FaceDetectionErrorEvent.java +0 -50
- package/android/src/main/java/expo/modules/camera/events/FacesDetectedEvent.java +0 -63
- package/android/src/main/java/expo/modules/camera/events/PictureSavedEvent.java +0 -53
- package/android/src/main/java/expo/modules/camera/tasks/BarCodeScannerAsyncTask.java +0 -47
- package/android/src/main/java/expo/modules/camera/tasks/BarCodeScannerAsyncTaskDelegate.java +0 -8
- package/android/src/main/java/expo/modules/camera/tasks/FaceDetectorAsyncTaskDelegate.java +0 -13
- package/android/src/main/java/expo/modules/camera/tasks/FaceDetectorTask.java +0 -53
- package/android/src/main/java/expo/modules/camera/tasks/PictureSavedDelegate.java +0 -7
- package/android/src/main/java/expo/modules/camera/tasks/ResolveTakenPictureAsyncTask.java +0 -288
- package/android/src/main/java/expo/modules/camera/utils/FileSystemUtils.java +0 -21
- package/android/src/main/java/expo/modules/camera/utils/ImageDimensions.java +0 -64
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package expo.modules.camera.events
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import androidx.core.util.Pools
|
|
5
|
+
|
|
6
|
+
import expo.modules.camera.CameraViewManager
|
|
7
|
+
import expo.modules.core.interfaces.services.EventEmitter.BaseEvent
|
|
8
|
+
|
|
9
|
+
class FacesDetectedEvent private constructor() : BaseEvent() {
|
|
10
|
+
private lateinit var faces: List<Bundle>
|
|
11
|
+
private var viewTag = 0
|
|
12
|
+
|
|
13
|
+
private fun init(viewTag: Int, faces: List<Bundle>) {
|
|
14
|
+
this.viewTag = viewTag
|
|
15
|
+
this.faces = faces
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* note(@sjchmiela)
|
|
20
|
+
* Should events about detected faces coalesce, the best strategy will be
|
|
21
|
+
* to ensure that events with different faces count are always being transmitted.
|
|
22
|
+
*/
|
|
23
|
+
override fun getCoalescingKey() =
|
|
24
|
+
if (faces.size > Short.MAX_VALUE) Short.MAX_VALUE
|
|
25
|
+
else faces.size.toShort()
|
|
26
|
+
|
|
27
|
+
override fun getEventName() = CameraViewManager.Events.EVENT_ON_FACES_DETECTED.toString()
|
|
28
|
+
|
|
29
|
+
override fun getEventBody() = Bundle().apply {
|
|
30
|
+
putString("type", "face")
|
|
31
|
+
putParcelableArray("faces", faces.toTypedArray())
|
|
32
|
+
putInt("target", viewTag)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
private val EVENTS_POOL = Pools.SynchronizedPool<FacesDetectedEvent>(3)
|
|
37
|
+
fun obtain(viewTag: Int, faces: List<Bundle>): FacesDetectedEvent {
|
|
38
|
+
var event = EVENTS_POOL.acquire()
|
|
39
|
+
if (event == null) {
|
|
40
|
+
event = FacesDetectedEvent()
|
|
41
|
+
}
|
|
42
|
+
event.init(viewTag, faces)
|
|
43
|
+
return event
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package expo.modules.camera.events
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import androidx.core.util.Pools
|
|
5
|
+
|
|
6
|
+
import expo.modules.camera.CameraViewManager
|
|
7
|
+
import expo.modules.core.interfaces.services.EventEmitter.BaseEvent
|
|
8
|
+
|
|
9
|
+
class PictureSavedEvent private constructor() : BaseEvent() {
|
|
10
|
+
private lateinit var response: Bundle
|
|
11
|
+
|
|
12
|
+
private fun init(response: Bundle) {
|
|
13
|
+
this.response = response
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override fun getCoalescingKey(): Short {
|
|
17
|
+
val fallback: Short = -1
|
|
18
|
+
val data = response
|
|
19
|
+
.getBundle("data")
|
|
20
|
+
?.takeIf { it.containsKey("uri") }
|
|
21
|
+
?: return fallback
|
|
22
|
+
val uri = data.getString("uri") ?: return fallback
|
|
23
|
+
return (uri.hashCode() % Short.MAX_VALUE).toShort()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override fun getEventName() = CameraViewManager.Events.EVENT_ON_PICTURE_SAVED.toString()
|
|
27
|
+
|
|
28
|
+
override fun getEventBody() = response
|
|
29
|
+
|
|
30
|
+
companion object {
|
|
31
|
+
private val EVENTS_POOL = Pools.SynchronizedPool<PictureSavedEvent>(3)
|
|
32
|
+
fun obtain(response: Bundle): PictureSavedEvent {
|
|
33
|
+
var event = EVENTS_POOL.acquire()
|
|
34
|
+
if (event == null) {
|
|
35
|
+
event = PictureSavedEvent()
|
|
36
|
+
}
|
|
37
|
+
event.init(response)
|
|
38
|
+
return event
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package expo.modules.camera.tasks
|
|
2
|
+
|
|
3
|
+
import android.os.AsyncTask
|
|
4
|
+
|
|
5
|
+
import expo.modules.interfaces.barcodescanner.BarCodeScannerInterface
|
|
6
|
+
import expo.modules.interfaces.barcodescanner.BarCodeScannerResult
|
|
7
|
+
|
|
8
|
+
class BarCodeScannerAsyncTask(
|
|
9
|
+
private val delegate: BarCodeScannerAsyncTaskDelegate,
|
|
10
|
+
private val barCodeScanner: BarCodeScannerInterface,
|
|
11
|
+
private val imageData: ByteArray,
|
|
12
|
+
private val width: Int,
|
|
13
|
+
private val height: Int,
|
|
14
|
+
private val rotation: Int
|
|
15
|
+
) : AsyncTask<Void?, Void?, BarCodeScannerResult?>() {
|
|
16
|
+
override fun doInBackground(vararg params: Void?) = if (!isCancelled) {
|
|
17
|
+
barCodeScanner.scan(imageData, width, height, rotation)
|
|
18
|
+
} else null
|
|
19
|
+
|
|
20
|
+
override fun onPostExecute(result: BarCodeScannerResult?) {
|
|
21
|
+
super.onPostExecute(result)
|
|
22
|
+
result?.let {
|
|
23
|
+
delegate.onBarCodeScanned(result)
|
|
24
|
+
} ?: delegate.onBarCodeScanningTaskCompleted()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
package expo.modules.camera.tasks
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import expo.modules.interfaces.facedetector.FaceDetectorInterface
|
|
5
|
+
|
|
6
|
+
interface FaceDetectorAsyncTaskDelegate {
|
|
7
|
+
fun onFacesDetected(faces: List<Bundle>)
|
|
8
|
+
fun onFaceDetectionError(faceDetector: FaceDetectorInterface)
|
|
9
|
+
fun onFaceDetectingTaskCompleted()
|
|
10
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package expo.modules.camera.tasks
|
|
2
|
+
|
|
3
|
+
import expo.modules.interfaces.facedetector.FaceDetectorInterface
|
|
4
|
+
|
|
5
|
+
class FaceDetectorTask(
|
|
6
|
+
private val mDelegate: FaceDetectorAsyncTaskDelegate,
|
|
7
|
+
private val mFaceDetector: FaceDetectorInterface,
|
|
8
|
+
private val mImageData: ByteArray,
|
|
9
|
+
private val mWidth: Int,
|
|
10
|
+
private val mHeight: Int,
|
|
11
|
+
private val mRotation: Int,
|
|
12
|
+
private val mMirrored: Boolean,
|
|
13
|
+
private val mScaleX: Double,
|
|
14
|
+
private val mScaleY: Double
|
|
15
|
+
) {
|
|
16
|
+
fun execute() {
|
|
17
|
+
mFaceDetector.detectFaces(
|
|
18
|
+
mImageData, mWidth, mHeight, mRotation, mMirrored, mScaleX, mScaleY,
|
|
19
|
+
{ result ->
|
|
20
|
+
result?.let {
|
|
21
|
+
mDelegate.onFacesDetected(result)
|
|
22
|
+
} ?: run {
|
|
23
|
+
mDelegate.onFaceDetectionError(mFaceDetector)
|
|
24
|
+
}
|
|
25
|
+
mDelegate.onFaceDetectingTaskCompleted()
|
|
26
|
+
},
|
|
27
|
+
{ error ->
|
|
28
|
+
mDelegate.onFaceDetectionError(mFaceDetector)
|
|
29
|
+
mDelegate.onFaceDetectingTaskCompleted()
|
|
30
|
+
},
|
|
31
|
+
{ skippedReason -> mDelegate.onFaceDetectingTaskCompleted() }
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
package expo.modules.camera.tasks
|
|
2
|
+
|
|
3
|
+
import android.content.res.Resources
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.graphics.BitmapFactory
|
|
6
|
+
import android.graphics.Matrix
|
|
7
|
+
import android.net.Uri
|
|
8
|
+
import android.os.AsyncTask
|
|
9
|
+
import android.os.Bundle
|
|
10
|
+
import android.util.Base64
|
|
11
|
+
import androidx.exifinterface.media.ExifInterface
|
|
12
|
+
|
|
13
|
+
import expo.modules.camera.CameraViewHelper.getExifData
|
|
14
|
+
import expo.modules.camera.CameraViewHelper.addExifData
|
|
15
|
+
import expo.modules.camera.utils.FileSystemUtils
|
|
16
|
+
import expo.modules.core.Promise
|
|
17
|
+
|
|
18
|
+
import java.io.ByteArrayInputStream
|
|
19
|
+
import java.io.ByteArrayOutputStream
|
|
20
|
+
import java.io.File
|
|
21
|
+
import java.io.FileOutputStream
|
|
22
|
+
import java.io.IOException
|
|
23
|
+
import java.lang.Exception
|
|
24
|
+
|
|
25
|
+
private const val DIRECTORY_NOT_FOUND_MSG = "Documents directory of the app could not be found."
|
|
26
|
+
private const val UNKNOWN_IO_EXCEPTION_MSG = "An unknown I/O exception has occurred."
|
|
27
|
+
private const val UNKNOWN_EXCEPTION_MSG = "An unknown exception has occurred."
|
|
28
|
+
private const val ERROR_TAG = "E_TAKING_PICTURE_FAILED"
|
|
29
|
+
private const val DIRECTORY_NAME = "Camera"
|
|
30
|
+
private const val EXTENSION = ".jpg"
|
|
31
|
+
private const val SKIP_PROCESSING_KEY = "skipProcessing"
|
|
32
|
+
private const val FAST_MODE_KEY = "fastMode"
|
|
33
|
+
private const val QUALITY_KEY = "quality"
|
|
34
|
+
private const val BASE64_KEY = "base64"
|
|
35
|
+
private const val HEIGHT_KEY = "height"
|
|
36
|
+
private const val WIDTH_KEY = "width"
|
|
37
|
+
private const val EXIF_KEY = "exif"
|
|
38
|
+
private const val DATA_KEY = "data"
|
|
39
|
+
private const val URI_KEY = "uri"
|
|
40
|
+
private const val ID_KEY = "id"
|
|
41
|
+
private const val DEFAULT_QUALITY = 1
|
|
42
|
+
|
|
43
|
+
class ResolveTakenPictureAsyncTask(
|
|
44
|
+
private var promise: Promise,
|
|
45
|
+
private var options: Map<String, Any>,
|
|
46
|
+
private val directory: File,
|
|
47
|
+
private var pictureSavedDelegate: PictureSavedDelegate
|
|
48
|
+
) : AsyncTask<Void?, Void?, Bundle?>() {
|
|
49
|
+
private var imageData: ByteArray? = null
|
|
50
|
+
private var bitmap: Bitmap? = null
|
|
51
|
+
|
|
52
|
+
constructor(imageData: ByteArray?, promise: Promise, options: Map<String, Any>, directory: File, delegate: PictureSavedDelegate) : this(
|
|
53
|
+
promise, options, directory, delegate
|
|
54
|
+
) {
|
|
55
|
+
this.imageData = imageData
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
constructor(bitmap: Bitmap?, promise: Promise, options: Map<String, Any>, directory: File, delegate: PictureSavedDelegate) : this(
|
|
59
|
+
promise, options, directory, delegate
|
|
60
|
+
) {
|
|
61
|
+
this.bitmap = bitmap
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private val quality: Int
|
|
65
|
+
get() = options[QUALITY_KEY]?.let {
|
|
66
|
+
val requestedQuality = (it as Number).toDouble()
|
|
67
|
+
(requestedQuality * 100).toInt()
|
|
68
|
+
} ?: DEFAULT_QUALITY * 100
|
|
69
|
+
|
|
70
|
+
override fun doInBackground(vararg params: Void?): Bundle? {
|
|
71
|
+
// handle SkipProcessing
|
|
72
|
+
if (imageData != null && isOptionEnabled(SKIP_PROCESSING_KEY)) {
|
|
73
|
+
return handleSkipProcessing()
|
|
74
|
+
}
|
|
75
|
+
if (bitmap == null) {
|
|
76
|
+
bitmap = imageData?.let { BitmapFactory.decodeByteArray(imageData, 0, it.size) }
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
ByteArrayInputStream(imageData).use { inputStream ->
|
|
80
|
+
val response = Bundle()
|
|
81
|
+
|
|
82
|
+
val exifInterface = ExifInterface(inputStream)
|
|
83
|
+
// Get orientation of the image from mImageData via inputStream
|
|
84
|
+
val orientation = exifInterface.getAttributeInt(
|
|
85
|
+
ExifInterface.TAG_ORIENTATION,
|
|
86
|
+
ExifInterface.ORIENTATION_UNDEFINED
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// Rotate the bitmap to the proper orientation if needed
|
|
90
|
+
if (orientation != ExifInterface.ORIENTATION_UNDEFINED) {
|
|
91
|
+
bitmap = bitmap?.let {
|
|
92
|
+
rotateBitmap(it, getImageRotation(orientation))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Write Exif data to the response if requested
|
|
97
|
+
if (isOptionEnabled(EXIF_KEY)) {
|
|
98
|
+
val exifData = getExifData(exifInterface)
|
|
99
|
+
response.putBundle(EXIF_KEY, exifData)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Upon rotating, write the image's dimensions to the response
|
|
103
|
+
response.apply {
|
|
104
|
+
putInt(WIDTH_KEY, bitmap!!.width)
|
|
105
|
+
putInt(HEIGHT_KEY, bitmap!!.height)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Cache compressed image in imageStream
|
|
109
|
+
ByteArrayOutputStream().use { imageStream ->
|
|
110
|
+
bitmap!!.compress(Bitmap.CompressFormat.JPEG, quality, imageStream)
|
|
111
|
+
// Write compressed image to file in cache directory
|
|
112
|
+
val filePath = writeStreamToFile(imageStream)
|
|
113
|
+
|
|
114
|
+
// Save Exif data to the image if requested
|
|
115
|
+
if (isOptionEnabled(EXIF_KEY)) {
|
|
116
|
+
val exifFromFile = ExifInterface(filePath!!)
|
|
117
|
+
addExifData(exifFromFile, exifInterface)
|
|
118
|
+
}
|
|
119
|
+
val imageFile = File(filePath)
|
|
120
|
+
val fileUri = Uri.fromFile(imageFile).toString()
|
|
121
|
+
response.putString(URI_KEY, fileUri)
|
|
122
|
+
|
|
123
|
+
// Write base64-encoded image to the response if requested
|
|
124
|
+
if (isOptionEnabled(BASE64_KEY)) {
|
|
125
|
+
response.putString(BASE64_KEY, Base64.encodeToString(imageStream.toByteArray(), Base64.NO_WRAP))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return response
|
|
129
|
+
}
|
|
130
|
+
} catch (e: Exception) {
|
|
131
|
+
when (e) {
|
|
132
|
+
is Resources.NotFoundException -> promise.reject(ERROR_TAG, DIRECTORY_NOT_FOUND_MSG, e)
|
|
133
|
+
is IOException -> promise.reject(ERROR_TAG, UNKNOWN_IO_EXCEPTION_MSG, e)
|
|
134
|
+
else -> promise.reject(ERROR_TAG, UNKNOWN_EXCEPTION_MSG, e)
|
|
135
|
+
}
|
|
136
|
+
e.printStackTrace()
|
|
137
|
+
}
|
|
138
|
+
// An exception had to occur, promise has already been rejected. Do not try to resolve it again.
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private fun handleSkipProcessing(): Bundle? {
|
|
143
|
+
try {
|
|
144
|
+
// save byte array (it's already a JPEG)
|
|
145
|
+
ByteArrayOutputStream().use { imageStream ->
|
|
146
|
+
imageStream.write(imageData)
|
|
147
|
+
|
|
148
|
+
// write compressed image to file in cache directory
|
|
149
|
+
val filePath = writeStreamToFile(imageStream)
|
|
150
|
+
val imageFile = File(filePath)
|
|
151
|
+
|
|
152
|
+
// handle image uri
|
|
153
|
+
val fileUri = Uri.fromFile(imageFile).toString()
|
|
154
|
+
|
|
155
|
+
// read exif information
|
|
156
|
+
val exifInterface = ExifInterface(filePath!!)
|
|
157
|
+
|
|
158
|
+
return Bundle().apply {
|
|
159
|
+
putString(URI_KEY, fileUri)
|
|
160
|
+
putInt(WIDTH_KEY, exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, -1))
|
|
161
|
+
putInt(HEIGHT_KEY, exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, -1))
|
|
162
|
+
// handle exif request
|
|
163
|
+
if (isOptionEnabled(EXIF_KEY)) {
|
|
164
|
+
val exifData = getExifData(exifInterface)
|
|
165
|
+
putBundle(EXIF_KEY, exifData)
|
|
166
|
+
}
|
|
167
|
+
// handle base64
|
|
168
|
+
if (isOptionEnabled(BASE64_KEY)) {
|
|
169
|
+
putString(BASE64_KEY, Base64.encodeToString(imageData, Base64.NO_WRAP))
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (e: Exception) {
|
|
174
|
+
if (e is IOException) {
|
|
175
|
+
promise.reject(ERROR_TAG, UNKNOWN_IO_EXCEPTION_MSG, e)
|
|
176
|
+
} else {
|
|
177
|
+
promise.reject(ERROR_TAG, UNKNOWN_EXCEPTION_MSG, e)
|
|
178
|
+
}
|
|
179
|
+
e.printStackTrace()
|
|
180
|
+
}
|
|
181
|
+
// error occurred
|
|
182
|
+
return null
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
override fun onPostExecute(response: Bundle?) {
|
|
186
|
+
super.onPostExecute(response)
|
|
187
|
+
|
|
188
|
+
// If the response is not null everything went well and we can resolve the promise.
|
|
189
|
+
if (response != null) {
|
|
190
|
+
if (isOptionEnabled(FAST_MODE_KEY)) {
|
|
191
|
+
val wrapper = Bundle()
|
|
192
|
+
wrapper.putInt(ID_KEY, (options[ID_KEY] as Double).toInt())
|
|
193
|
+
wrapper.putBundle(DATA_KEY, response)
|
|
194
|
+
pictureSavedDelegate.onPictureSaved(wrapper)
|
|
195
|
+
} else {
|
|
196
|
+
promise.resolve(response)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Write stream to file in cache directory
|
|
202
|
+
@Throws(Exception::class)
|
|
203
|
+
private fun writeStreamToFile(inputStream: ByteArrayOutputStream): String? {
|
|
204
|
+
try {
|
|
205
|
+
val outputPath = FileSystemUtils.generateOutputPath(directory, DIRECTORY_NAME, EXTENSION)
|
|
206
|
+
FileOutputStream(outputPath).use { outputStream ->
|
|
207
|
+
inputStream.writeTo(outputStream)
|
|
208
|
+
}
|
|
209
|
+
return outputPath
|
|
210
|
+
} catch (e: IOException) {
|
|
211
|
+
e.printStackTrace()
|
|
212
|
+
}
|
|
213
|
+
return null
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private fun rotateBitmap(source: Bitmap, angle: Int): Bitmap {
|
|
217
|
+
val matrix = Matrix()
|
|
218
|
+
matrix.postRotate(angle.toFloat())
|
|
219
|
+
return Bitmap.createBitmap(source, 0, 0, source.width, source.height, matrix, true)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Get rotation degrees from Exif orientation enum
|
|
223
|
+
private fun getImageRotation(orientation: Int) = when (orientation) {
|
|
224
|
+
ExifInterface.ORIENTATION_ROTATE_90 -> 90
|
|
225
|
+
ExifInterface.ORIENTATION_ROTATE_180 -> 180
|
|
226
|
+
ExifInterface.ORIENTATION_ROTATE_270 -> 270
|
|
227
|
+
else -> 0
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private fun isOptionEnabled(key: String) = options[key] != null && (options[key] as Boolean)
|
|
231
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package expo.modules.camera.utils
|
|
2
|
+
|
|
3
|
+
import java.io.File
|
|
4
|
+
import java.io.IOException
|
|
5
|
+
import java.util.*
|
|
6
|
+
|
|
7
|
+
object FileSystemUtils {
|
|
8
|
+
@Throws(IOException::class)
|
|
9
|
+
fun ensureDirExists(dir: File): File {
|
|
10
|
+
if (!(dir.isDirectory || dir.mkdirs())) {
|
|
11
|
+
throw IOException("Couldn't create directory '$dir'")
|
|
12
|
+
}
|
|
13
|
+
return dir
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Throws(IOException::class)
|
|
17
|
+
fun generateOutputPath(internalDirectory: File, dirName: String, extension: String): String {
|
|
18
|
+
val directory = File(internalDirectory.toString() + File.separator + dirName)
|
|
19
|
+
ensureDirExists(directory)
|
|
20
|
+
val filename = UUID.randomUUID().toString()
|
|
21
|
+
return directory.toString() + File.separator + filename + extension
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package expo.modules.camera.utils
|
|
2
|
+
|
|
3
|
+
data class ImageDimensions @JvmOverloads constructor(private val mWidth: Int, private val mHeight: Int, val rotation: Int = 0, val facing: Int = -1) {
|
|
4
|
+
private val isLandscape: Boolean
|
|
5
|
+
get() = rotation % 180 == 90
|
|
6
|
+
val width: Int
|
|
7
|
+
get() = if (isLandscape) {
|
|
8
|
+
mHeight
|
|
9
|
+
} else mWidth
|
|
10
|
+
val height: Int
|
|
11
|
+
get() = if (isLandscape) {
|
|
12
|
+
mWidth
|
|
13
|
+
} else mHeight
|
|
14
|
+
}
|
|
@@ -9,7 +9,9 @@ function getUserMedia(constraints) {
|
|
|
9
9
|
// with getUserMedia as it would overwrite existing properties.
|
|
10
10
|
// Here, we will just add the getUserMedia property if it's missing.
|
|
11
11
|
// First get ahold of the legacy getUserMedia, if present
|
|
12
|
-
const getUserMedia =
|
|
12
|
+
const getUserMedia =
|
|
13
|
+
// TODO: this method is deprecated, migrate to https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
|
|
14
|
+
navigator.getUserMedia ||
|
|
13
15
|
navigator.webkitGetUserMedia ||
|
|
14
16
|
navigator.mozGetUserMedia ||
|
|
15
17
|
function () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExponentCameraManager.web.js","sourceRoot":"","sources":["../src/ExponentCameraManager.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport {\n CameraCapturedPicture,\n CameraPictureOptions,\n CameraType,\n PermissionResponse,\n PermissionStatus,\n} from './Camera.types';\nimport { ExponentCameraRef } from './ExponentCamera.web';\nimport {\n canGetUserMedia,\n isBackCameraAvailableAsync,\n isFrontCameraAvailableAsync,\n} from './WebUserMediaManager';\n\nfunction getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream> {\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n return navigator.mediaDevices.getUserMedia(constraints);\n }\n\n // Some browsers partially implement mediaDevices. We can't just assign an object\n // with getUserMedia as it would overwrite existing properties.\n // Here, we will just add the getUserMedia property if it's missing.\n\n // First get ahold of the legacy getUserMedia, if present\n const getUserMedia =\n navigator.getUserMedia ||\n (navigator as any).webkitGetUserMedia ||\n (navigator as any).mozGetUserMedia ||\n function () {\n const error: any = new Error('Permission unimplemented');\n error.code = 0;\n error.name = 'NotAllowedError';\n throw error;\n };\n\n return new Promise((resolve, reject) => {\n getUserMedia.call(navigator, constraints, resolve, reject);\n });\n}\n\nfunction handleGetUserMediaError({ message }: { message: string }): PermissionResponse {\n // name: NotAllowedError\n // code: 0\n if (message === 'Permission dismissed') {\n return {\n status: PermissionStatus.UNDETERMINED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n } else {\n // TODO: Bacon: [OSX] The system could deny access to chrome.\n // TODO: Bacon: add: { status: 'unimplemented' }\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n }\n}\n\nasync function handleRequestPermissionsAsync(): Promise<PermissionResponse> {\n try {\n await getUserMedia({\n video: true,\n });\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n } catch ({ message }) {\n return handleGetUserMediaError({ message });\n }\n}\n\nasync function handlePermissionsQueryAsync(\n query: 'camera' | 'microphone'\n): Promise<PermissionResponse> {\n if (!navigator?.permissions?.query) {\n throw new UnavailabilityError('expo-camera', 'navigator.permissions API is not available');\n }\n\n const { state } = await navigator.permissions.query({ name: query });\n switch (state) {\n case 'prompt':\n return {\n status: PermissionStatus.UNDETERMINED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n case 'granted':\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n case 'denied':\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n }\n}\n\nexport default {\n get name(): string {\n return 'ExponentCameraManager';\n },\n get Type() {\n return {\n back: 'back',\n front: 'front',\n };\n },\n get FlashMode() {\n return {\n on: 'on',\n off: 'off',\n auto: 'auto',\n torch: 'torch',\n };\n },\n get AutoFocus() {\n return {\n on: 'on',\n off: 'off',\n auto: 'auto',\n singleShot: 'singleShot',\n };\n },\n get WhiteBalance() {\n return {\n auto: 'auto',\n continuous: 'continuous',\n manual: 'manual',\n };\n },\n get VideoQuality() {\n return {};\n },\n get VideoStabilization() {\n return {};\n },\n async isAvailableAsync(): Promise<boolean> {\n return canGetUserMedia();\n },\n async takePicture(\n options: CameraPictureOptions,\n camera: ExponentCameraRef\n ): Promise<CameraCapturedPicture> {\n return await camera.takePicture(options);\n },\n async pausePreview(camera: ExponentCameraRef): Promise<void> {\n await camera.pausePreview();\n },\n async resumePreview(camera: ExponentCameraRef): Promise<void> {\n return await camera.resumePreview();\n },\n async getAvailableCameraTypesAsync(): Promise<string[]> {\n if (!canGetUserMedia() || !navigator.mediaDevices.enumerateDevices) return [];\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n\n const types: (string | null)[] = await Promise.all([\n (await isFrontCameraAvailableAsync(devices)) && CameraType.front,\n (await isBackCameraAvailableAsync()) && CameraType.back,\n ]);\n\n return types.filter(Boolean) as string[];\n },\n async getAvailablePictureSizes(ratio: string, camera: ExponentCameraRef): Promise<string[]> {\n return await camera.getAvailablePictureSizes(ratio);\n },\n /* async getSupportedRatios(camera: ExponentCameraRef): Promise<string[]> {\n // TODO: Support on web\n },\n async record(\n options?: CameraRecordingOptions,\n camera: ExponentCameraRef\n ): Promise<{ uri: string }> {\n // TODO: Support on web\n },\n async stopRecording(camera: ExponentCameraRef): Promise<void> {\n // TODO: Support on web\n }, */\n async getPermissionsAsync(): Promise<PermissionResponse> {\n return handlePermissionsQueryAsync('camera');\n },\n async requestPermissionsAsync(): Promise<PermissionResponse> {\n return handleRequestPermissionsAsync();\n },\n async getCameraPermissionsAsync(): Promise<PermissionResponse> {\n return handlePermissionsQueryAsync('camera');\n },\n async requestCameraPermissionsAsync(): Promise<PermissionResponse> {\n return handleRequestPermissionsAsync();\n },\n async getMicrophonePermissionsAsync(): Promise<PermissionResponse> {\n return handlePermissionsQueryAsync('microphone');\n },\n async requestMicrophonePermissionsAsync(): Promise<PermissionResponse> {\n try {\n await getUserMedia({\n audio: true,\n });\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n } catch ({ message }) {\n return handleGetUserMediaError({ message });\n }\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"ExponentCameraManager.web.js","sourceRoot":"","sources":["../src/ExponentCameraManager.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport {\n CameraCapturedPicture,\n CameraPictureOptions,\n CameraType,\n PermissionResponse,\n PermissionStatus,\n} from './Camera.types';\nimport { ExponentCameraRef } from './ExponentCamera.web';\nimport {\n canGetUserMedia,\n isBackCameraAvailableAsync,\n isFrontCameraAvailableAsync,\n} from './WebUserMediaManager';\n\nfunction getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream> {\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n return navigator.mediaDevices.getUserMedia(constraints);\n }\n\n // Some browsers partially implement mediaDevices. We can't just assign an object\n // with getUserMedia as it would overwrite existing properties.\n // Here, we will just add the getUserMedia property if it's missing.\n\n // First get ahold of the legacy getUserMedia, if present\n const getUserMedia =\n // TODO: this method is deprecated, migrate to https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia\n navigator.getUserMedia ||\n navigator.webkitGetUserMedia ||\n navigator.mozGetUserMedia ||\n function () {\n const error: any = new Error('Permission unimplemented');\n error.code = 0;\n error.name = 'NotAllowedError';\n throw error;\n };\n\n return new Promise((resolve, reject) => {\n getUserMedia.call(navigator, constraints, resolve, reject);\n });\n}\n\nfunction handleGetUserMediaError({ message }: { message: string }): PermissionResponse {\n // name: NotAllowedError\n // code: 0\n if (message === 'Permission dismissed') {\n return {\n status: PermissionStatus.UNDETERMINED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n } else {\n // TODO: Bacon: [OSX] The system could deny access to chrome.\n // TODO: Bacon: add: { status: 'unimplemented' }\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n }\n}\n\nasync function handleRequestPermissionsAsync(): Promise<PermissionResponse> {\n try {\n await getUserMedia({\n video: true,\n });\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n } catch ({ message }) {\n return handleGetUserMediaError({ message });\n }\n}\n\nasync function handlePermissionsQueryAsync(\n query: 'camera' | 'microphone'\n): Promise<PermissionResponse> {\n if (!navigator?.permissions?.query) {\n throw new UnavailabilityError('expo-camera', 'navigator.permissions API is not available');\n }\n\n const { state } = await navigator.permissions.query({ name: query });\n switch (state) {\n case 'prompt':\n return {\n status: PermissionStatus.UNDETERMINED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n case 'granted':\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n case 'denied':\n return {\n status: PermissionStatus.DENIED,\n expires: 'never',\n canAskAgain: true,\n granted: false,\n };\n }\n}\n\nexport default {\n get name(): string {\n return 'ExponentCameraManager';\n },\n get Type() {\n return {\n back: 'back',\n front: 'front',\n };\n },\n get FlashMode() {\n return {\n on: 'on',\n off: 'off',\n auto: 'auto',\n torch: 'torch',\n };\n },\n get AutoFocus() {\n return {\n on: 'on',\n off: 'off',\n auto: 'auto',\n singleShot: 'singleShot',\n };\n },\n get WhiteBalance() {\n return {\n auto: 'auto',\n continuous: 'continuous',\n manual: 'manual',\n };\n },\n get VideoQuality() {\n return {};\n },\n get VideoStabilization() {\n return {};\n },\n async isAvailableAsync(): Promise<boolean> {\n return canGetUserMedia();\n },\n async takePicture(\n options: CameraPictureOptions,\n camera: ExponentCameraRef\n ): Promise<CameraCapturedPicture> {\n return await camera.takePicture(options);\n },\n async pausePreview(camera: ExponentCameraRef): Promise<void> {\n await camera.pausePreview();\n },\n async resumePreview(camera: ExponentCameraRef): Promise<void> {\n return await camera.resumePreview();\n },\n async getAvailableCameraTypesAsync(): Promise<string[]> {\n if (!canGetUserMedia() || !navigator.mediaDevices.enumerateDevices) return [];\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n\n const types: (string | null)[] = await Promise.all([\n (await isFrontCameraAvailableAsync(devices)) && CameraType.front,\n (await isBackCameraAvailableAsync()) && CameraType.back,\n ]);\n\n return types.filter(Boolean) as string[];\n },\n async getAvailablePictureSizes(ratio: string, camera: ExponentCameraRef): Promise<string[]> {\n return await camera.getAvailablePictureSizes(ratio);\n },\n /* async getSupportedRatios(camera: ExponentCameraRef): Promise<string[]> {\n // TODO: Support on web\n },\n async record(\n options?: CameraRecordingOptions,\n camera: ExponentCameraRef\n ): Promise<{ uri: string }> {\n // TODO: Support on web\n },\n async stopRecording(camera: ExponentCameraRef): Promise<void> {\n // TODO: Support on web\n }, */\n async getPermissionsAsync(): Promise<PermissionResponse> {\n return handlePermissionsQueryAsync('camera');\n },\n async requestPermissionsAsync(): Promise<PermissionResponse> {\n return handleRequestPermissionsAsync();\n },\n async getCameraPermissionsAsync(): Promise<PermissionResponse> {\n return handlePermissionsQueryAsync('camera');\n },\n async requestCameraPermissionsAsync(): Promise<PermissionResponse> {\n return handleRequestPermissionsAsync();\n },\n async getMicrophonePermissionsAsync(): Promise<PermissionResponse> {\n return handlePermissionsQueryAsync('microphone');\n },\n async requestMicrophonePermissionsAsync(): Promise<PermissionResponse> {\n try {\n await getUserMedia({\n audio: true,\n });\n return {\n status: PermissionStatus.GRANTED,\n expires: 'never',\n canAskAgain: true,\n granted: true,\n };\n } catch ({ message }) {\n return handleGetUserMediaError({ message });\n }\n },\n};\n"]}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-camera",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.1.2",
|
|
4
4
|
"description": "A React component that renders a preview for the device's either front or back camera. Camera's parameters like zoom, auto focus, white balance and flash mode are adjustable. With expo-camera, one can also take photos and record videos that are saved to the app's cache. Morever, the component is also capable of detecting faces and bar codes appearing on the preview.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -34,13 +34,15 @@
|
|
|
34
34
|
"preset": "expo-module-scripts"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@expo/config-plugins": "^
|
|
37
|
+
"@expo/config-plugins": "^4.0.2",
|
|
38
38
|
"@koale/useworker": "^4.0.2",
|
|
39
|
-
"
|
|
40
|
-
"invariant": "2.2.4"
|
|
39
|
+
"invariant": "^2.2.4"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"expo-module-scripts": "^2.0.0"
|
|
44
43
|
},
|
|
45
|
-
"
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"expo": "*"
|
|
46
|
+
},
|
|
47
|
+
"gitHead": "43d2652ada705ed7e57801830ad935605f73b9be"
|
|
46
48
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
|
-
|
|
2
|
+
import { MergeResults } from '@expo/config-plugins/build/utils/generateCode';
|
|
3
|
+
export declare function addCameraImport(src: string): MergeResults;
|
|
3
4
|
declare const _default: ConfigPlugin<void | {
|
|
4
5
|
cameraPermission?: string | undefined;
|
|
5
6
|
microphonePermission?: string | undefined;
|