react-native-rectangle-doc-scanner 3.191.0 → 3.193.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.
|
@@ -11,6 +11,8 @@ import androidx.camera.view.PreviewView
|
|
|
11
11
|
import androidx.core.content.ContextCompat
|
|
12
12
|
import androidx.lifecycle.Lifecycle
|
|
13
13
|
import androidx.lifecycle.LifecycleOwner
|
|
14
|
+
import androidx.lifecycle.LiveData
|
|
15
|
+
import androidx.lifecycle.Observer
|
|
14
16
|
import java.io.File
|
|
15
17
|
import java.util.concurrent.ExecutorService
|
|
16
18
|
import java.util.concurrent.Executors
|
|
@@ -28,6 +30,11 @@ class CameraController(
|
|
|
28
30
|
|
|
29
31
|
private var useFrontCamera = false
|
|
30
32
|
private var torchEnabled = false
|
|
33
|
+
private var detectionEnabled = true
|
|
34
|
+
private var isCaptureSession = false
|
|
35
|
+
private var hasFallbackAttempted = false
|
|
36
|
+
private var cameraStateLiveData: LiveData<CameraState>? = null
|
|
37
|
+
private var cameraStateObserver: Observer<CameraState>? = null
|
|
31
38
|
|
|
32
39
|
var onFrameAnalyzed: ((Rectangle?, Int, Int) -> Unit)? = null
|
|
33
40
|
|
|
@@ -51,6 +58,7 @@ class CameraController(
|
|
|
51
58
|
Log.d(TAG, "========================================")
|
|
52
59
|
|
|
53
60
|
this.useFrontCamera = useFrontCam
|
|
61
|
+
this.detectionEnabled = enableDetection
|
|
54
62
|
|
|
55
63
|
Log.d(TAG, "[CAMERA_CONTROLLER] Getting ProcessCameraProvider instance...")
|
|
56
64
|
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
|
|
@@ -61,7 +69,9 @@ class CameraController(
|
|
|
61
69
|
cameraProvider = cameraProviderFuture.get()
|
|
62
70
|
Log.d(TAG, "[CAMERA_CONTROLLER] Got cameraProvider: $cameraProvider")
|
|
63
71
|
Log.d(TAG, "[CAMERA_CONTROLLER] Calling bindCameraUseCases...")
|
|
64
|
-
|
|
72
|
+
// Bind preview + analysis only. ImageCapture is bound lazily during capture
|
|
73
|
+
// to avoid stream configuration timeouts on some devices.
|
|
74
|
+
bindCameraUseCases(enableDetection, useImageCapture = false)
|
|
65
75
|
} catch (e: Exception) {
|
|
66
76
|
Log.e(TAG, "[CAMERA_CONTROLLER] Failed to start camera", e)
|
|
67
77
|
e.printStackTrace()
|
|
@@ -80,9 +90,10 @@ class CameraController(
|
|
|
80
90
|
/**
|
|
81
91
|
* Bind camera use cases (preview, capture, analysis)
|
|
82
92
|
*/
|
|
83
|
-
private fun bindCameraUseCases(enableDetection: Boolean) {
|
|
93
|
+
private fun bindCameraUseCases(enableDetection: Boolean, useImageCapture: Boolean) {
|
|
84
94
|
Log.d(TAG, "[BIND] bindCameraUseCases called")
|
|
85
95
|
Log.d(TAG, "[BIND] enableDetection: $enableDetection")
|
|
96
|
+
Log.d(TAG, "[BIND] useImageCapture: $useImageCapture")
|
|
86
97
|
|
|
87
98
|
val cameraProvider = cameraProvider
|
|
88
99
|
if (cameraProvider == null) {
|
|
@@ -117,16 +128,20 @@ class CameraController(
|
|
|
117
128
|
.build()
|
|
118
129
|
Log.d(TAG, "[BIND] Preview created: $preview")
|
|
119
130
|
|
|
120
|
-
// Image capture use case (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
// Image capture use case (bound only when capture is requested)
|
|
132
|
+
if (useImageCapture) {
|
|
133
|
+
Log.d(TAG, "[BIND] Creating ImageCapture use case...")
|
|
134
|
+
imageCapture = ImageCapture.Builder()
|
|
135
|
+
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
|
|
136
|
+
// Cap resolution to avoid camera session timeouts on lower-end devices.
|
|
137
|
+
.setTargetResolution(Size(960, 720))
|
|
138
|
+
.setTargetRotation(targetRotation)
|
|
139
|
+
.setFlashMode(ImageCapture.FLASH_MODE_AUTO)
|
|
140
|
+
.build()
|
|
141
|
+
Log.d(TAG, "[BIND] ImageCapture created: $imageCapture")
|
|
142
|
+
} else {
|
|
143
|
+
imageCapture = null
|
|
144
|
+
}
|
|
130
145
|
|
|
131
146
|
// Image analysis use case for rectangle detection
|
|
132
147
|
imageAnalysis = if (enableDetection) {
|
|
@@ -166,7 +181,10 @@ class CameraController(
|
|
|
166
181
|
cameraProvider.unbindAll()
|
|
167
182
|
|
|
168
183
|
// Bind use cases to camera
|
|
169
|
-
val useCases = mutableListOf<UseCase>(preview
|
|
184
|
+
val useCases = mutableListOf<UseCase>(preview)
|
|
185
|
+
if (imageCapture != null) {
|
|
186
|
+
useCases.add(imageCapture!!)
|
|
187
|
+
}
|
|
170
188
|
if (imageAnalysis != null) {
|
|
171
189
|
useCases.add(imageAnalysis!!)
|
|
172
190
|
}
|
|
@@ -179,6 +197,7 @@ class CameraController(
|
|
|
179
197
|
*useCases.toTypedArray()
|
|
180
198
|
)
|
|
181
199
|
Log.d(TAG, "[BIND] Bound to lifecycle successfully, camera: $camera")
|
|
200
|
+
registerCameraStateObserver(camera)
|
|
182
201
|
|
|
183
202
|
// Restore torch state if it was enabled
|
|
184
203
|
if (torchEnabled) {
|
|
@@ -190,12 +209,38 @@ class CameraController(
|
|
|
190
209
|
Log.d(TAG, "[BIND] Camera started successfully!")
|
|
191
210
|
Log.d(TAG, "[BIND] hasFlashUnit: ${camera?.cameraInfo?.hasFlashUnit()}")
|
|
192
211
|
Log.d(TAG, "[BIND] ========================================")
|
|
212
|
+
isCaptureSession = useImageCapture
|
|
193
213
|
} catch (e: Exception) {
|
|
194
214
|
Log.e(TAG, "[BIND] Failed to bind camera use cases", e)
|
|
195
215
|
e.printStackTrace()
|
|
196
216
|
}
|
|
197
217
|
}
|
|
198
218
|
|
|
219
|
+
private fun registerCameraStateObserver(camera: Camera?) {
|
|
220
|
+
val cam = camera ?: return
|
|
221
|
+
cameraStateLiveData?.let { liveData ->
|
|
222
|
+
cameraStateObserver?.let { liveData.removeObserver(it) }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
val observer = Observer<CameraState> { state ->
|
|
226
|
+
val error = state.error
|
|
227
|
+
if (error != null && !hasFallbackAttempted && !isCaptureSession) {
|
|
228
|
+
hasFallbackAttempted = true
|
|
229
|
+
Log.e(TAG, "[STATE] Camera error detected (${error.code}), falling back to preview-only")
|
|
230
|
+
try {
|
|
231
|
+
cameraProvider?.unbindAll()
|
|
232
|
+
bindCameraUseCases(enableDetection = false, useImageCapture = false)
|
|
233
|
+
} catch (e: Exception) {
|
|
234
|
+
Log.e(TAG, "[STATE] Fallback bind failed", e)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
cameraStateObserver = observer
|
|
240
|
+
cameraStateLiveData = cam.cameraInfo.cameraState
|
|
241
|
+
cam.cameraInfo.cameraState.observe(lifecycleOwner, observer)
|
|
242
|
+
}
|
|
243
|
+
|
|
199
244
|
/**
|
|
200
245
|
* Analyze frame for rectangle detection
|
|
201
246
|
*/
|
|
@@ -301,6 +346,24 @@ class CameraController(
|
|
|
301
346
|
onImageCaptured: (File) -> Unit,
|
|
302
347
|
onError: (Exception) -> Unit
|
|
303
348
|
) {
|
|
349
|
+
if (!isCaptureSession) {
|
|
350
|
+
val provider = cameraProvider ?: run {
|
|
351
|
+
onError(Exception("Camera provider not initialized"))
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
ContextCompat.getMainExecutor(context).execute {
|
|
355
|
+
try {
|
|
356
|
+
// Rebind with ImageCapture only for the capture to avoid stream timeouts.
|
|
357
|
+
provider.unbindAll()
|
|
358
|
+
bindCameraUseCases(enableDetection = false, useImageCapture = true)
|
|
359
|
+
capturePhoto(outputDirectory, onImageCaptured, onError)
|
|
360
|
+
} catch (e: Exception) {
|
|
361
|
+
onError(e)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return
|
|
365
|
+
}
|
|
366
|
+
|
|
304
367
|
val imageCapture = imageCapture ?: run {
|
|
305
368
|
onError(Exception("Image capture not initialized"))
|
|
306
369
|
return
|
|
@@ -320,6 +383,11 @@ class CameraController(
|
|
|
320
383
|
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
|
|
321
384
|
Log.d(TAG, "Photo capture succeeded: ${photoFile.absolutePath}")
|
|
322
385
|
onImageCaptured(photoFile)
|
|
386
|
+
if (detectionEnabled) {
|
|
387
|
+
ContextCompat.getMainExecutor(context).execute {
|
|
388
|
+
bindCameraUseCases(enableDetection = true, useImageCapture = false)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
323
391
|
}
|
|
324
392
|
|
|
325
393
|
override fun onError(exception: ImageCaptureException) {
|
|
@@ -327,7 +395,12 @@ class CameraController(
|
|
|
327
395
|
if (exception.imageCaptureError == ImageCapture.ERROR_CAMERA_CLOSED) {
|
|
328
396
|
Log.w(TAG, "Camera was closed during capture, attempting restart")
|
|
329
397
|
stopCamera()
|
|
330
|
-
startCamera(useFrontCamera,
|
|
398
|
+
startCamera(useFrontCamera, detectionEnabled)
|
|
399
|
+
}
|
|
400
|
+
if (detectionEnabled) {
|
|
401
|
+
ContextCompat.getMainExecutor(context).execute {
|
|
402
|
+
bindCameraUseCases(enableDetection = true, useImageCapture = false)
|
|
403
|
+
}
|
|
331
404
|
}
|
|
332
405
|
onError(exception)
|
|
333
406
|
}
|
package/package.json
CHANGED