react-native-rectangle-doc-scanner 3.149.0 → 3.151.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.
|
@@ -8,6 +8,7 @@ import androidx.camera.core.*
|
|
|
8
8
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
|
9
9
|
import androidx.camera.view.PreviewView
|
|
10
10
|
import androidx.core.content.ContextCompat
|
|
11
|
+
import androidx.lifecycle.Lifecycle
|
|
11
12
|
import androidx.lifecycle.LifecycleOwner
|
|
12
13
|
import java.io.File
|
|
13
14
|
import java.util.concurrent.ExecutorService
|
|
@@ -68,6 +69,13 @@ class CameraController(
|
|
|
68
69
|
private fun bindCameraUseCases(enableDetection: Boolean) {
|
|
69
70
|
val cameraProvider = cameraProvider ?: return
|
|
70
71
|
|
|
72
|
+
// Check lifecycle state
|
|
73
|
+
val lifecycle = lifecycleOwner.lifecycle
|
|
74
|
+
if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
|
|
75
|
+
Log.e(TAG, "Cannot bind camera - lifecycle is destroyed")
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
// Select camera
|
|
72
80
|
val cameraSelector = if (useFrontCamera) {
|
|
73
81
|
CameraSelector.DEFAULT_FRONT_CAMERA
|
|
@@ -79,14 +87,12 @@ class CameraController(
|
|
|
79
87
|
val preview = Preview.Builder()
|
|
80
88
|
.setTargetResolution(Size(1080, 1920))
|
|
81
89
|
.build()
|
|
82
|
-
.also {
|
|
83
|
-
it.setSurfaceProvider(previewView.surfaceProvider)
|
|
84
|
-
}
|
|
85
90
|
|
|
86
91
|
// Image capture use case (high resolution for document scanning)
|
|
87
92
|
imageCapture = ImageCapture.Builder()
|
|
88
93
|
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
|
|
89
94
|
.setTargetResolution(Size(1920, 2560))
|
|
95
|
+
.setFlashMode(ImageCapture.FLASH_MODE_AUTO)
|
|
90
96
|
.build()
|
|
91
97
|
|
|
92
98
|
// Image analysis use case for rectangle detection
|
|
@@ -121,12 +127,15 @@ class CameraController(
|
|
|
121
127
|
*useCases.toTypedArray()
|
|
122
128
|
)
|
|
123
129
|
|
|
130
|
+
// Set surface provider AFTER binding to lifecycle
|
|
131
|
+
preview.setSurfaceProvider(previewView.surfaceProvider)
|
|
132
|
+
|
|
124
133
|
// Restore torch state if it was enabled
|
|
125
134
|
if (torchEnabled) {
|
|
126
135
|
setTorchEnabled(true)
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
Log.d(TAG, "Camera started successfully")
|
|
138
|
+
Log.d(TAG, "Camera started successfully, hasFlashUnit: ${camera?.cameraInfo?.hasFlashUnit()}")
|
|
130
139
|
} catch (e: Exception) {
|
|
131
140
|
Log.e(TAG, "Failed to bind camera use cases", e)
|
|
132
141
|
}
|
|
@@ -19,6 +19,7 @@ import com.facebook.react.bridge.WritableMap
|
|
|
19
19
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
20
20
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
21
21
|
import kotlinx.coroutines.*
|
|
22
|
+
import kotlinx.coroutines.delay
|
|
22
23
|
import java.io.File
|
|
23
24
|
import kotlin.math.min
|
|
24
25
|
|
|
@@ -48,6 +49,7 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
48
49
|
private var stableCounter = 0
|
|
49
50
|
private var lastDetectionTimestamp: Long = 0L
|
|
50
51
|
private var isCapturing = false
|
|
52
|
+
private var isDetaching = false
|
|
51
53
|
|
|
52
54
|
// Coroutine scope for async operations
|
|
53
55
|
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
@@ -80,14 +82,22 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
80
82
|
|
|
81
83
|
private fun setupCamera() {
|
|
82
84
|
try {
|
|
85
|
+
// Move to STARTED state first
|
|
86
|
+
if (lifecycleRegistry.currentState == Lifecycle.State.CREATED) {
|
|
87
|
+
lifecycleRegistry.currentState = Lifecycle.State.STARTED
|
|
88
|
+
}
|
|
89
|
+
|
|
83
90
|
cameraController = CameraController(context, this, previewView)
|
|
84
91
|
cameraController?.onFrameAnalyzed = { rectangle, imageWidth, imageHeight ->
|
|
85
92
|
handleDetectionResult(rectangle, imageWidth, imageHeight)
|
|
86
93
|
}
|
|
87
94
|
lastDetectionTimestamp = 0L
|
|
95
|
+
|
|
96
|
+
// Move to RESUMED state before starting camera
|
|
88
97
|
if (lifecycleRegistry.currentState != Lifecycle.State.DESTROYED) {
|
|
89
98
|
lifecycleRegistry.currentState = Lifecycle.State.RESUMED
|
|
90
99
|
}
|
|
100
|
+
|
|
91
101
|
cameraController?.startCamera(isUsingFrontCamera, true)
|
|
92
102
|
if (isTorchEnabled) {
|
|
93
103
|
cameraController?.setTorchEnabled(true)
|
|
@@ -181,6 +191,12 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
181
191
|
return
|
|
182
192
|
}
|
|
183
193
|
|
|
194
|
+
if (isDetaching) {
|
|
195
|
+
Log.d(TAG, "View is detaching, cannot capture")
|
|
196
|
+
promise?.reject("VIEW_DETACHING", "View is being removed")
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
184
200
|
isCapturing = true
|
|
185
201
|
Log.d(TAG, "Capture initiated with promise: ${promise != null}")
|
|
186
202
|
|
|
@@ -193,8 +209,14 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
193
209
|
cameraController?.capturePhoto(
|
|
194
210
|
outputDirectory = outputDir,
|
|
195
211
|
onImageCaptured = { file ->
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
if (!isDetaching) {
|
|
213
|
+
scope.launch {
|
|
214
|
+
processAndEmitImage(file, promise)
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
Log.d(TAG, "View detaching, skipping image processing")
|
|
218
|
+
isCapturing = false
|
|
219
|
+
promise?.reject("VIEW_DETACHING", "View was removed during capture")
|
|
198
220
|
}
|
|
199
221
|
},
|
|
200
222
|
onError = { exception ->
|
|
@@ -343,25 +365,61 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
343
365
|
cameraController?.onFrameAnalyzed = { rectangle, imageWidth, imageHeight ->
|
|
344
366
|
handleDetectionResult(rectangle, imageWidth, imageHeight)
|
|
345
367
|
}
|
|
368
|
+
|
|
369
|
+
// Ensure proper lifecycle state before starting camera
|
|
370
|
+
if (lifecycleRegistry.currentState == Lifecycle.State.CREATED) {
|
|
371
|
+
lifecycleRegistry.currentState = Lifecycle.State.STARTED
|
|
372
|
+
}
|
|
373
|
+
if (lifecycleRegistry.currentState != Lifecycle.State.DESTROYED) {
|
|
374
|
+
lifecycleRegistry.currentState = Lifecycle.State.RESUMED
|
|
375
|
+
}
|
|
376
|
+
|
|
346
377
|
cameraController?.startCamera(isUsingFrontCamera, true)
|
|
347
378
|
if (isTorchEnabled) {
|
|
348
379
|
cameraController?.setTorchEnabled(true)
|
|
349
380
|
}
|
|
350
|
-
lifecycleRegistry.currentState = Lifecycle.State.RESUMED
|
|
351
381
|
}
|
|
352
382
|
|
|
353
383
|
fun stopCamera() {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
lifecycleRegistry.currentState
|
|
384
|
+
if (!isCapturing) {
|
|
385
|
+
cameraController?.stopCamera()
|
|
386
|
+
overlayView.setRectangle(null, overlayColor)
|
|
387
|
+
stableCounter = 0
|
|
388
|
+
if (lifecycleRegistry.currentState != Lifecycle.State.DESTROYED) {
|
|
389
|
+
lifecycleRegistry.currentState = Lifecycle.State.CREATED
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
Log.d(TAG, "Cannot stop camera while capturing")
|
|
359
393
|
}
|
|
360
394
|
}
|
|
361
395
|
|
|
362
396
|
override fun onDetachedFromWindow() {
|
|
363
397
|
super.onDetachedFromWindow()
|
|
364
|
-
|
|
398
|
+
Log.d(TAG, "onDetachedFromWindow called, isCapturing: $isCapturing")
|
|
399
|
+
|
|
400
|
+
// Mark as detaching to prevent new captures
|
|
401
|
+
isDetaching = true
|
|
402
|
+
|
|
403
|
+
// Wait for any ongoing capture to complete before cleaning up
|
|
404
|
+
if (isCapturing) {
|
|
405
|
+
Log.d(TAG, "Waiting for capture to complete before cleanup...")
|
|
406
|
+
// Use a coroutine to wait briefly for capture to complete
|
|
407
|
+
scope.launch {
|
|
408
|
+
var waitCount = 0
|
|
409
|
+
while (isCapturing && waitCount < 20) { // Wait up to 2 seconds
|
|
410
|
+
delay(100)
|
|
411
|
+
waitCount++
|
|
412
|
+
}
|
|
413
|
+
performCleanup()
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
performCleanup()
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private fun performCleanup() {
|
|
421
|
+
Log.d(TAG, "Performing cleanup")
|
|
422
|
+
cameraController?.stopCamera()
|
|
365
423
|
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
|
|
366
424
|
cameraController?.shutdown()
|
|
367
425
|
scope.cancel()
|
package/package.json
CHANGED