Added support for saveToCameraRoll on Android. Doc updates.

This commit is contained in:
Seph Soliman 2021-06-08 15:04:07 -07:00
parent d18ca47b76
commit 6be7a1f6cb
4 changed files with 76 additions and 21 deletions

View File

@ -142,10 +142,10 @@ import { Camera, CameraType } from 'react-native-camera-kit';
| `torchMode` | `'on'`/`'off'` | Toggle flash light when camera is active. Default: `off` |
| `ratioOverlay` | `['int':'int', ...]` | Show a guiding overlay in the camera preview for the selected ratio. Does not crop image as of v9.0. Example: `['16:9', '1:1', '3:4']` |
| `ratioOverlayColor` | Color | Any color with alpha. Default: `'#ffffff77'` |
| `resetFocusTimeout` | Number | iOS only. Dismiss tap to focus after this many milliseconds. Default `0` (disabled). Example: `5000` is 5 seconds. |
| `resetFocusWhenMotionDetected` | Boolean | iOS only. Dismiss tap to focus when focus area content changes. Native iOS feature, see documentation: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624644-subjectareachangemonitoringenabl?language=objc). Default `true`. |
| `saveToCameraRoll` | Boolean | Using the camera roll is slower than using regular files stored in your app. On an iPhone X in debug mode, on a real phone, we measured around 100-150ms processing time to save to the camera roll. _<span style="color: red">**Note:**</span> This only work on real devices. It will hang indefinitly on simulators._ |
| `saveToCameraRollWithPhUrl` | Boolean | iOS only. If true, speeds up photo taking by about 5-50ms (measured on iPhone X) by only returning a [rn-cameraroll-compatible](https://github.com/react-native-community/react-native-cameraroll/blob/a09af08f0a46a98b29f6ad470e59d3dc627864a2/ios/RNCAssetsLibraryRequestHandler.m#L36) `ph://..` URL instead of a regular `file://..` URL. | |
| `resetFocusTimeout` | Number | **iOS only.** Dismiss tap to focus after this many milliseconds. Default `0` (disabled). Example: `5000` is 5 seconds. |
| `resetFocusWhenMotionDetected` | Boolean | **iOS only.** Dismiss tap to focus when focus area content changes. Native iOS feature, see documentation: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624644-subjectareachangemonitoringenabl?language=objc). Default `true`. |
| `saveToCameraRoll` | Boolean | Using the camera roll is slower than using regular files stored in your app. On an iPhone X in debug mode, on a real phone, we measured around 100-150ms processing time to save to the camera roll. _<span style="color: red">**Note:**</span> This only work on real devices. It will hang indefinitly on simulators._ Default `true` |
| `saveToCameraRollWithPhUrl` | Boolean | **iOS only.** If true, speeds up photo taking by about 5-50ms (measured on iPhone X) by only returning a [rn-cameraroll-compatible](https://github.com/react-native-community/react-native-cameraroll/blob/a09af08f0a46a98b29f6ad470e59d3dc627864a2/ios/RNCAssetsLibraryRequestHandler.m#L36) `ph://..` URL instead of a regular `file://..` URL. | |
| `onOrientationChange` | Function | Callback when physical device orientation changes. Returned event contains `orientation`. Ex: `onOrientationChange={(event) => console.log(event.nativeEvent.orientation)}`. Use `import { Orientation } from 'react-native-camera-kit'; if (event.nativeEvent.orientation === Orientation.PORTRAIT) { ... }` to understand the new value |
### Barcode Props (Optional)
@ -163,15 +163,41 @@ import { Camera, CameraType } from 'react-native-camera-kit';
_Note: Must be called on a valid camera ref_
#### capture({ ... })
#### capture()
Capture image (`{ saveToCameraRoll: boolean }`). Using the camera roll is slower than using regular files stored in your app. On an iPhone X in debug mode, on a real phone, we measured around 100-150ms processing time to save to the camera roll.
Capture image as JPEG.
If you are not using `saveToCameraRoll` then a temporary file is created. You *must* move this file to a permanent location (e.g. the app's 'Documents' folder) if you need it beyond the current session of the app as it may be deleted when the user leaves the app. You can move files by using a file system library such as [react-native-fs](https://github.com/itinance/react-native-fs) or [expo-filesystem](https://docs.expo.io/versions/latest/sdk/filesystem/).
(On Android we currently have an unsupported `outputPath` prop but it's subject to change at any time).
Note that the reason you're getting a URL despite it being a file is because Android 10+ encourages URIs. To keep things consistent regardless of settings or platform we always send back a URI.
```ts
const image = await this.camera.capture();
const { uri } = await this.camera.capture();
// uri = 'file:///data/user/0/com.myorg.myapp/cache/ckcap123123123123.jpg'
```
#### checkDeviceCameraAuthorizationStatus (iOS only)
If you want to store it permanently, here's an example using [react-native-fs](https://github.com/itinance/react-native-fs):
```ts
import RNFS from 'react-native-fs';
// [...]
let { uri } = await this.camera.capture();
if (uri.startsWith('file://')) {
// Platform dependent, iOS & Android uses '/'
const pathSplitter = '/';
// file:///foo/bar.jpg => /foo/bar.jpg
const filePath = uri.replace('file://', '');
// /foo/bar.jpg => [foo, bar.jpg]
const pathSegments = filePath.split(pathSplitter);
// [foo, bar.jpg] => bar.jpg
const fileName = pathSegments[pathSegments.length - 1];
await RNFS.moveFile(filePath, `${RNFS.DocumentDirectoryPath}/${fileName}`);
uri = `file://${destFilePath}`;
}
```
#### checkDeviceCameraAuthorizationStatus (**iOS only**)
```ts
const isCameraAuthorized = await Camera.checkDeviceCameraAuthorizationStatus();
@ -185,7 +211,7 @@ return values:
otherwise, returns `false`
#### requestDeviceCameraAuthorization (iOS only)
#### requestDeviceCameraAuthorization (**iOS only**)
```ts
const isUserAuthorizedCamera = await Camera.requestDeviceCameraAuthorization();

View File

@ -12,6 +12,7 @@ import android.graphics.Color
import android.hardware.SensorManager
import android.media.AudioManager
import android.media.MediaActionSound
import android.net.Uri
import android.provider.MediaStore
import android.util.DisplayMetrics
import android.util.Log
@ -56,6 +57,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
private var outputPath: String? = null
private var shutterAnimationDuration: Int = 50
private var effectLayer = View(context)
private var saveToCameraRoll = true
// Camera Props
private var lensType = CameraSelector.LENS_FACING_BACK
@ -287,16 +289,29 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
}
// Create the output file option to store the captured image in MediaStore
val outputOptions = when (outputPath) {
null -> ImageCapture.OutputFileOptions
val outputPath = when {
saveToCameraRoll -> null
outputPath != null -> outputPath
else -> {
val out = File.createTempFile("ckcap", ".jpg", context.cacheDir)
out.deleteOnExit();
out.canonicalPath
}
}
var outputFile: File? = null
val outputOptions = if (saveToCameraRoll) {
ImageCapture.OutputFileOptions
.Builder(
context.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
.build()
else -> ImageCapture.OutputFileOptions
.Builder(File(outputPath))
} else {
outputFile = File(outputPath)
ImageCapture.OutputFileOptions
.Builder(outputFile)
.build()
}
@ -318,17 +333,22 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
try {
val savedUri = output.savedUri.toString()
val uri = output.savedUri ?: Uri.fromFile(outputFile)
val id = uri?.path
val name = uri?.lastPathSegment
val path = uri?.path
val savedUri = (output.savedUri ?: outputPath).toString()
onPictureTaken(savedUri)
Log.d(TAG, "CameraView: Photo capture succeeded: $savedUri")
val imageInfo = Arguments.createMap()
imageInfo.putString("uri", savedUri)
imageInfo.putString("id", output.savedUri?.path)
imageInfo.putString("name", output.savedUri?.lastPathSegment)
imageInfo.putString("uri", uri.toString())
imageInfo.putString("id", id)
imageInfo.putString("name", name)
imageInfo.putInt("width", width)
imageInfo.putInt("height", height)
imageInfo.putString("path", output.savedUri?.path)
imageInfo.putString("path", path)
promise.resolve(imageInfo)
} catch (ex: Exception) {
@ -442,6 +462,10 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
zoomMode = mode
}
fun setSaveToCameraRoll(enabled: Boolean = true) {
saveToCameraRoll = enabled
}
fun setScanBarcode(enabled: Boolean) {
val restartCamera = enabled != scanBarcode
scanBarcode = enabled

View File

@ -101,6 +101,11 @@ class CKCameraManager : SimpleViewManager<CKCamera>() {
view.setOutputPath(path)
}
@ReactProp(name = "saveToCameraRoll")
fun saveToCameraRoll(view: CKCamera, enabled: Boolean) {
view.setSaveToCameraRoll(enabled);
}
@ReactProp(name = "shutterAnimationDuration")
fun setShutterAnimationDuration(view: CKCamera, duration: Int) {
view.setShutterAnimationDuration(duration)

View File

@ -16,10 +16,10 @@ const Camera = React.forwardRef((props: any, ref) => {
// we must use the general module and tell it what View it's supposed to be using
return await RNCameraKitModule.capture(options, findNodeHandle(nativeRef.current ?? null));
},
requestDeviceCameraAuthorization: async () => {
return await RNCameraKitModule.requestDeviceCameraAuthorization();
requestDeviceCameraAuthorization: () => {
throw new Error('Not implemented');
},
checkDeviceCameraAuthorizationStatus: async () => {
checkDeviceCameraAuthorizationStatus: () => {
throw new Error('Not implemented');
},
}));