feat: Add onStarted
and onStopped
events (#2273)
* feat: Add `onStarted` and `onStopped` events * Implement `onStart` for Android * Update CameraSession.kt * Update CameraSessionDelegate.swift
This commit is contained in:
parent
9ef4a9a210
commit
4ee52d6211
@ -19,6 +19,20 @@ fun CameraView.invokeOnInitialized() {
|
|||||||
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraInitialized", null)
|
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraInitialized", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CameraView.invokeOnStarted() {
|
||||||
|
Log.i(CameraView.TAG, "invokeOnStarted()")
|
||||||
|
|
||||||
|
val reactContext = context as ReactContext
|
||||||
|
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraStarted", null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CameraView.invokeOnStopped() {
|
||||||
|
Log.i(CameraView.TAG, "invokeOnStopped()")
|
||||||
|
|
||||||
|
val reactContext = context as ReactContext
|
||||||
|
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraStopped", null)
|
||||||
|
}
|
||||||
|
|
||||||
fun CameraView.invokeOnError(error: Throwable) {
|
fun CameraView.invokeOnError(error: Throwable) {
|
||||||
Log.e(CameraView.TAG, "invokeOnError(...):")
|
Log.e(CameraView.TAG, "invokeOnError(...):")
|
||||||
error.printStackTrace()
|
error.printStackTrace()
|
||||||
|
@ -239,6 +239,14 @@ class CameraView(context: Context) :
|
|||||||
invokeOnInitialized()
|
invokeOnInitialized()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStarted() {
|
||||||
|
invokeOnStarted()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStopped() {
|
||||||
|
invokeOnStopped()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCodeScanned(codes: List<Barcode>, scannerFrame: CodeScannerFrame) {
|
override fun onCodeScanned(codes: List<Barcode>, scannerFrame: CodeScannerFrame) {
|
||||||
invokeOnCodeScanned(codes, scannerFrame)
|
invokeOnCodeScanned(codes, scannerFrame)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ class CameraViewManager : ViewGroupManager<CameraView>() {
|
|||||||
MapBuilder.builder<String, Any>()
|
MapBuilder.builder<String, Any>()
|
||||||
.put("cameraViewReady", MapBuilder.of("registrationName", "onViewReady"))
|
.put("cameraViewReady", MapBuilder.of("registrationName", "onViewReady"))
|
||||||
.put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized"))
|
.put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized"))
|
||||||
|
.put("cameraStarted", MapBuilder.of("registrationName", "onStarted"))
|
||||||
|
.put("cameraStopped", MapBuilder.of("registrationName", "onStopped"))
|
||||||
.put("cameraError", MapBuilder.of("registrationName", "onError"))
|
.put("cameraError", MapBuilder.of("registrationName", "onError"))
|
||||||
.put("cameraCodeScanned", MapBuilder.of("registrationName", "onCodeScanned"))
|
.put("cameraCodeScanned", MapBuilder.of("registrationName", "onCodeScanned"))
|
||||||
.build()
|
.build()
|
||||||
|
@ -76,7 +76,9 @@ data class CameraConfiguration(
|
|||||||
// Outputs & Session (Photo, Video, CodeScanner, HDR, Format)
|
// Outputs & Session (Photo, Video, CodeScanner, HDR, Format)
|
||||||
val outputsChanged: Boolean,
|
val outputsChanged: Boolean,
|
||||||
// Side-Props for CaptureRequest (fps, low-light-boost, torch, zoom, videoStabilization)
|
// Side-Props for CaptureRequest (fps, low-light-boost, torch, zoom, videoStabilization)
|
||||||
val sidePropsChanged: Boolean
|
val sidePropsChanged: Boolean,
|
||||||
|
// (isActive) changed
|
||||||
|
val isActiveChanged: Boolean
|
||||||
) {
|
) {
|
||||||
val hasAnyDifference: Boolean
|
val hasAnyDifference: Boolean
|
||||||
get() = sidePropsChanged || outputsChanged || deviceChanged
|
get() = sidePropsChanged || outputsChanged || deviceChanged
|
||||||
@ -98,10 +100,13 @@ data class CameraConfiguration(
|
|||||||
left.zoom != right.zoom || left.videoStabilizationMode != right.videoStabilizationMode || left.isActive != right.isActive ||
|
left.zoom != right.zoom || left.videoStabilizationMode != right.videoStabilizationMode || left.isActive != right.isActive ||
|
||||||
left.exposure != right.exposure
|
left.exposure != right.exposure
|
||||||
|
|
||||||
|
val isActiveChanged = left?.isActive != right.isActive
|
||||||
|
|
||||||
return Difference(
|
return Difference(
|
||||||
deviceChanged,
|
deviceChanged,
|
||||||
outputsChanged,
|
outputsChanged,
|
||||||
sidePropsChanged
|
sidePropsChanged,
|
||||||
|
isActiveChanged
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,16 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
if (diff.deviceChanged) {
|
if (diff.deviceChanged) {
|
||||||
callback.onInitialized()
|
callback.onInitialized()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify about Camera start/stop
|
||||||
|
if (diff.isActiveChanged) {
|
||||||
|
// TODO: Move that into the CaptureRequest callback to get actual first-frame arrive time?
|
||||||
|
if (config.isActive) {
|
||||||
|
callback.onStarted()
|
||||||
|
} else {
|
||||||
|
callback.onStopped()
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error: Throwable) {
|
} catch (error: Throwable) {
|
||||||
Log.e(TAG, "Failed to configure CameraSession! Error: ${error.message}, Config-Diff: $diff", error)
|
Log.e(TAG, "Failed to configure CameraSession! Error: ${error.message}, Config-Diff: $diff", error)
|
||||||
callback.onError(error)
|
callback.onError(error)
|
||||||
@ -367,6 +377,7 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
// TODO: Do we want to do stopRepeating() or entirely destroy the session?
|
// TODO: Do we want to do stopRepeating() or entirely destroy the session?
|
||||||
// If the Camera is not active, we don't do anything.
|
// If the Camera is not active, we don't do anything.
|
||||||
captureSession?.stopRepeating()
|
captureSession?.stopRepeating()
|
||||||
|
isRunning = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,6 +632,8 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
interface CameraSessionCallback {
|
interface CameraSessionCallback {
|
||||||
fun onError(error: Throwable)
|
fun onError(error: Throwable)
|
||||||
fun onInitialized()
|
fun onInitialized()
|
||||||
|
fun onStarted()
|
||||||
|
fun onStopped()
|
||||||
fun onCodeScanned(codes: List<Barcode>, scannerFrame: CodeScannerFrame)
|
fun onCodeScanned(codes: List<Barcode>, scannerFrame: CodeScannerFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,9 @@ PODS:
|
|||||||
- libwebp/sharpyuv (1.3.2)
|
- libwebp/sharpyuv (1.3.2)
|
||||||
- libwebp/webp (1.3.2):
|
- libwebp/webp (1.3.2):
|
||||||
- libwebp/sharpyuv
|
- libwebp/sharpyuv
|
||||||
- MMKV (1.3.1):
|
- MMKV (1.3.2):
|
||||||
- MMKVCore (~> 1.3.1)
|
- MMKVCore (~> 1.3.2)
|
||||||
- MMKVCore (1.3.1)
|
- MMKVCore (1.3.2)
|
||||||
- RCT-Folly (2021.07.22.00):
|
- RCT-Folly (2021.07.22.00):
|
||||||
- boost
|
- boost
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
@ -507,7 +507,7 @@ PODS:
|
|||||||
- libwebp (~> 1.0)
|
- libwebp (~> 1.0)
|
||||||
- SDWebImage/Core (~> 5.10)
|
- SDWebImage/Core (~> 5.10)
|
||||||
- SocketRocket (0.6.1)
|
- SocketRocket (0.6.1)
|
||||||
- VisionCamera (3.6.11):
|
- VisionCamera (3.6.14):
|
||||||
- React
|
- React
|
||||||
- React-callinvoker
|
- React-callinvoker
|
||||||
- React-Core
|
- React-Core
|
||||||
@ -698,8 +698,8 @@ SPEC CHECKSUMS:
|
|||||||
hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322
|
hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322
|
||||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||||
MMKV: 5a07930c70c70b86cd87761a42c8f3836fb681d7
|
MMKV: f21593c0af4b3f2a0ceb8f820f28bb639ea22bb7
|
||||||
MMKVCore: e50135dbd33235b6ab390635991bab437ab873c0
|
MMKVCore: 31b4cb83f8266467eef20a35b6d78e409a11060d
|
||||||
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
||||||
RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18
|
RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18
|
||||||
RCTTypeSafety: cb09f3e4747b6d18331a15eb05271de7441ca0b3
|
RCTTypeSafety: cb09f3e4747b6d18331a15eb05271de7441ca0b3
|
||||||
@ -747,7 +747,7 @@ SPEC CHECKSUMS:
|
|||||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||||
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
||||||
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
||||||
VisionCamera: b35fc51a521ce0a9b9da41d8b13127e3d414d195
|
VisionCamera: 3cf177fa91fa9fe04622071415032c5af618a5ac
|
||||||
Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce
|
Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce
|
||||||
|
|
||||||
PODFILE CHECKSUM: 27f53791141a3303d814e09b55770336416ff4eb
|
PODFILE CHECKSUM: 27f53791141a3303d814e09b55770336416ff4eb
|
||||||
|
@ -59,6 +59,8 @@ public final class CameraView: UIView, CameraSessionDelegate {
|
|||||||
// events
|
// events
|
||||||
@objc var onInitialized: RCTDirectEventBlock?
|
@objc var onInitialized: RCTDirectEventBlock?
|
||||||
@objc var onError: RCTDirectEventBlock?
|
@objc var onError: RCTDirectEventBlock?
|
||||||
|
@objc var onStarted: RCTDirectEventBlock?
|
||||||
|
@objc var onStopped: RCTDirectEventBlock?
|
||||||
@objc var onViewReady: RCTDirectEventBlock?
|
@objc var onViewReady: RCTDirectEventBlock?
|
||||||
@objc var onCodeScanned: RCTDirectEventBlock?
|
@objc var onCodeScanned: RCTDirectEventBlock?
|
||||||
// zoom
|
// zoom
|
||||||
@ -283,7 +285,23 @@ public final class CameraView: UIView, CameraSessionDelegate {
|
|||||||
guard let onInitialized = onInitialized else {
|
guard let onInitialized = onInitialized else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
onInitialized([String: Any]())
|
onInitialized([:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func onCameraStarted() {
|
||||||
|
ReactLogger.log(level: .info, message: "Camera started!")
|
||||||
|
guard let onStarted = onStarted else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onStarted([:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func onCameraStopped() {
|
||||||
|
ReactLogger.log(level: .info, message: "Camera stopped!")
|
||||||
|
guard let onStopped = onStopped else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onStopped([:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func onFrame(sampleBuffer: CMSampleBuffer) {
|
func onFrame(sampleBuffer: CMSampleBuffer) {
|
||||||
|
@ -52,6 +52,8 @@ RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
|||||||
// Camera View Events
|
// Camera View Events
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onInitialized, RCTDirectEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onInitialized, RCTDirectEventBlock);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(onStarted, RCTDirectEventBlock);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(onStopped, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onViewReady, RCTDirectEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onViewReady, RCTDirectEventBlock);
|
||||||
// Code Scanner
|
// Code Scanner
|
||||||
RCT_EXPORT_VIEW_PROPERTY(codeScannerOptions, NSDictionary);
|
RCT_EXPORT_VIEW_PROPERTY(codeScannerOptions, NSDictionary);
|
||||||
|
@ -249,8 +249,10 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
|
|||||||
// Start/Stop session
|
// Start/Stop session
|
||||||
if configuration.isActive {
|
if configuration.isActive {
|
||||||
captureSession.startRunning()
|
captureSession.startRunning()
|
||||||
|
delegate?.onCameraStarted()
|
||||||
} else {
|
} else {
|
||||||
captureSession.stopRunning()
|
captureSession.stopRunning()
|
||||||
|
delegate?.onCameraStopped()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,14 @@ protocol CameraSessionDelegate: AnyObject {
|
|||||||
Called when the [CameraSession] successfully initializes
|
Called when the [CameraSession] successfully initializes
|
||||||
*/
|
*/
|
||||||
func onSessionInitialized()
|
func onSessionInitialized()
|
||||||
|
/**
|
||||||
|
Called when the [CameraSession] starts streaming frames. (isActive=true)
|
||||||
|
*/
|
||||||
|
func onCameraStarted()
|
||||||
|
/**
|
||||||
|
Called when the [CameraSession] stopped streaming frames. (isActive=false)
|
||||||
|
*/
|
||||||
|
func onCameraStopped()
|
||||||
/**
|
/**
|
||||||
Called for every frame (if video or frameProcessor is enabled)
|
Called for every frame (if video or frameProcessor is enabled)
|
||||||
*/
|
*/
|
||||||
|
@ -33,6 +33,8 @@ type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onE
|
|||||||
onInitialized?: (event: NativeSyntheticEvent<void>) => void
|
onInitialized?: (event: NativeSyntheticEvent<void>) => void
|
||||||
onError?: (event: NativeSyntheticEvent<OnErrorEvent>) => void
|
onError?: (event: NativeSyntheticEvent<OnErrorEvent>) => void
|
||||||
onCodeScanned?: (event: NativeSyntheticEvent<OnCodeScannedEvent>) => void
|
onCodeScanned?: (event: NativeSyntheticEvent<OnCodeScannedEvent>) => void
|
||||||
|
onStarted?: (event: NativeSyntheticEvent<void>) => void
|
||||||
|
onStopped?: (event: NativeSyntheticEvent<void>) => void
|
||||||
onViewReady: () => void
|
onViewReady: () => void
|
||||||
}
|
}
|
||||||
type NativeRecordVideoOptions = Omit<RecordVideoOptions, 'onRecordingError' | 'onRecordingFinished' | 'videoBitRate'> & {
|
type NativeRecordVideoOptions = Omit<RecordVideoOptions, 'onRecordingError' | 'onRecordingFinished' | 'videoBitRate'> & {
|
||||||
@ -89,6 +91,8 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
|||||||
super(props)
|
super(props)
|
||||||
this.onViewReady = this.onViewReady.bind(this)
|
this.onViewReady = this.onViewReady.bind(this)
|
||||||
this.onInitialized = this.onInitialized.bind(this)
|
this.onInitialized = this.onInitialized.bind(this)
|
||||||
|
this.onStarted = this.onStarted.bind(this)
|
||||||
|
this.onStopped = this.onStopped.bind(this)
|
||||||
this.onError = this.onError.bind(this)
|
this.onError = this.onError.bind(this)
|
||||||
this.onCodeScanned = this.onCodeScanned.bind(this)
|
this.onCodeScanned = this.onCodeScanned.bind(this)
|
||||||
this.ref = React.createRef<RefType>()
|
this.ref = React.createRef<RefType>()
|
||||||
@ -416,6 +420,14 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
|||||||
private onInitialized(): void {
|
private onInitialized(): void {
|
||||||
this.props.onInitialized?.()
|
this.props.onInitialized?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onStarted(): void {
|
||||||
|
this.props.onStarted?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
private onStopped(): void {
|
||||||
|
this.props.onStopped?.()
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
private onCodeScanned(event: NativeSyntheticEvent<OnCodeScannedEvent>): void {
|
private onCodeScanned(event: NativeSyntheticEvent<OnCodeScannedEvent>): void {
|
||||||
@ -481,6 +493,8 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
|||||||
onViewReady={this.onViewReady}
|
onViewReady={this.onViewReady}
|
||||||
onInitialized={this.onInitialized}
|
onInitialized={this.onInitialized}
|
||||||
onCodeScanned={this.onCodeScanned}
|
onCodeScanned={this.onCodeScanned}
|
||||||
|
onStarted={this.onStarted}
|
||||||
|
onStopped={this.onStopped}
|
||||||
onError={this.onError}
|
onError={this.onError}
|
||||||
codeScannerOptions={codeScanner}
|
codeScannerOptions={codeScanner}
|
||||||
enableFrameProcessor={frameProcessor != null}
|
enableFrameProcessor={frameProcessor != null}
|
||||||
|
@ -246,9 +246,17 @@ export interface CameraProps extends ViewProps {
|
|||||||
*/
|
*/
|
||||||
onError?: (error: CameraRuntimeError) => void
|
onError?: (error: CameraRuntimeError) => void
|
||||||
/**
|
/**
|
||||||
* Called when the camera was successfully initialized.
|
* Called when the camera session was successfully initialized. This will get called everytime a new device is set.
|
||||||
*/
|
*/
|
||||||
onInitialized?: () => void
|
onInitialized?: () => void
|
||||||
|
/**
|
||||||
|
* Called when the camera started the session (`isActive={true}`)
|
||||||
|
*/
|
||||||
|
onStarted?: () => void
|
||||||
|
/**
|
||||||
|
* Called when the camera stopped the session (`isActive={false}`)
|
||||||
|
*/
|
||||||
|
onStopped?: () => void
|
||||||
/**
|
/**
|
||||||
* A worklet which will be called for every frame the Camera "sees".
|
* A worklet which will be called for every frame the Camera "sees".
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user