react-native-rectangle-doc-scanner 3.221.0 → 3.222.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.
@@ -15,6 +15,8 @@ import androidx.camera.core.AspectRatio
15
15
  import androidx.camera.core.Camera
16
16
  import androidx.camera.core.CameraSelector
17
17
  import androidx.camera.core.ImageAnalysis
18
+ import androidx.camera.core.ImageCapture
19
+ import androidx.camera.core.ImageCaptureException
18
20
  import androidx.camera.core.Preview
19
21
  import androidx.camera.lifecycle.ProcessCameraProvider
20
22
  import androidx.camera.view.PreviewView
@@ -37,6 +39,7 @@ class CameraController(
37
39
  private var cameraProvider: ProcessCameraProvider? = null
38
40
  private var preview: Preview? = null
39
41
  private var imageAnalysis: ImageAnalysis? = null
42
+ private var imageCapture: ImageCapture? = null
40
43
  private var camera: Camera? = null
41
44
  private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
42
45
  private val lastFrame = AtomicReference<LastFrame?>()
@@ -45,6 +48,18 @@ class CameraController(
45
48
  private var useFrontCamera = false
46
49
  private var detectionEnabled = true
47
50
 
51
+ // For periodic frame capture
52
+ private var isAnalysisActive = false
53
+ private val analysisHandler = android.os.Handler(android.os.Looper.getMainLooper())
54
+ private val analysisRunnable = object : Runnable {
55
+ override fun run() {
56
+ if (isAnalysisActive && onFrameAnalyzed != null) {
57
+ captureFrameForAnalysis()
58
+ analysisHandler.postDelayed(this, 200) // Capture every 200ms
59
+ }
60
+ }
61
+ }
62
+
48
63
  var onFrameAnalyzed: ((Rectangle?, Int, Int) -> Unit)? = null
49
64
 
50
65
  companion object {
@@ -63,12 +78,12 @@ class CameraController(
63
78
  useFrontCam: Boolean = false,
64
79
  enableDetection: Boolean = true
65
80
  ) {
66
- Log.d(TAG, "[CAMERAX] startCamera called")
81
+ Log.d(TAG, "[CAMERAX-V6] startCamera called")
67
82
  this.useFrontCamera = useFrontCam
68
83
  this.detectionEnabled = enableDetection
69
84
 
70
85
  if (!hasCameraPermission()) {
71
- Log.e(TAG, "[CAMERAX] Camera permission not granted")
86
+ Log.e(TAG, "[CAMERAX-V6] Camera permission not granted")
72
87
  return
73
88
  }
74
89
 
@@ -81,13 +96,15 @@ class CameraController(
81
96
  cameraProvider = cameraProviderFuture?.get()
82
97
  bindCameraUseCases()
83
98
  } catch (e: Exception) {
84
- Log.e(TAG, "[CAMERAX] Failed to get camera provider", e)
99
+ Log.e(TAG, "[CAMERAX-V6] Failed to get camera provider", e)
85
100
  }
86
101
  }, ContextCompat.getMainExecutor(context))
87
102
  }
88
103
 
89
104
  fun stopCamera() {
90
- Log.d(TAG, "[CAMERAX] stopCamera called")
105
+ Log.d(TAG, "[CAMERAX-V6] stopCamera called")
106
+ isAnalysisActive = false
107
+ analysisHandler.removeCallbacks(analysisRunnable)
91
108
  cameraProvider?.unbindAll()
92
109
  analysisBound = false
93
110
  }
@@ -119,10 +136,10 @@ class CameraController(
119
136
  }
120
137
  bitmap.recycle()
121
138
 
122
- Log.d(TAG, "[CAMERAX] Photo capture succeeded: ${photoFile.absolutePath}")
139
+ Log.d(TAG, "[CAMERAX-V6] Photo capture succeeded: ${photoFile.absolutePath}")
123
140
  onImageCaptured(photoFile)
124
141
  } catch (e: Exception) {
125
- Log.e(TAG, "[CAMERAX] Photo capture failed", e)
142
+ Log.e(TAG, "[CAMERAX-V6] Photo capture failed", e)
126
143
  onError(e)
127
144
  }
128
145
  }
@@ -154,10 +171,11 @@ class CameraController(
154
171
  val provider = cameraProvider ?: return
155
172
  provider.unbindAll()
156
173
  analysisBound = false
174
+ isAnalysisActive = false
157
175
 
158
176
  val rotation = previewView.display?.rotation ?: Surface.ROTATION_0
159
177
 
160
- // Build Preview ONLY first
178
+ // Build Preview with lower resolution
161
179
  preview = Preview.Builder()
162
180
  .setTargetResolution(Size(1280, 720))
163
181
  .setTargetRotation(rotation)
@@ -166,125 +184,90 @@ class CameraController(
166
184
  it.setSurfaceProvider(previewView.surfaceProvider)
167
185
  }
168
186
 
187
+ // Build ImageCapture for periodic frame capture (instead of ImageAnalysis)
188
+ imageCapture = ImageCapture.Builder()
189
+ .setTargetResolution(Size(640, 480))
190
+ .setTargetRotation(rotation)
191
+ .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
192
+ .build()
193
+
169
194
  val cameraSelector = if (useFrontCamera) {
170
195
  CameraSelector.DEFAULT_FRONT_CAMERA
171
196
  } else {
172
197
  CameraSelector.DEFAULT_BACK_CAMERA
173
198
  }
174
199
 
175
- // TEMPORARY: Bind Preview ONLY to test if Preview works without ImageAnalysis
200
+ // Bind Preview and ImageCapture together
176
201
  try {
177
- camera = provider.bindToLifecycle(
178
- lifecycleOwner,
179
- cameraSelector,
180
- preview
181
- )
182
- Log.d(TAG, "[CAMERAX-DEBUG] Preview ONLY bound successfully - NO ImageAnalysis")
183
- analysisBound = false
184
-
185
- // TODO: If Preview works, we need to find alternative for document detection
186
- // Options:
187
- // 1. Use ImageCapture instead of ImageAnalysis
188
- // 2. Use lower resolution for ImageAnalysis (already tried)
189
- // 3. Use Camera2 API directly instead of CameraX
190
-
191
- } catch (e: Exception) {
192
- Log.e(TAG, "[CAMERAX-DEBUG] Failed to bind preview", e)
193
- analysisBound = false
194
- }
195
- }
196
-
197
- private fun bindImageAnalysis(provider: ProcessCameraProvider, cameraSelector: CameraSelector, rotation: Int) {
198
- if (analysisBound) return
199
-
200
- Log.d(TAG, "[CAMERAX-FIX-V5] Starting to add ImageAnalysis...")
201
-
202
- try {
203
- // Build ImageAnalysis with very low resolution
204
- imageAnalysis = ImageAnalysis.Builder()
205
- .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
206
- .setTargetRotation(rotation)
207
- .setTargetResolution(Size(480, 360))
208
- .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
209
- .build()
210
- .also {
211
- it.setAnalyzer(cameraExecutor, DocumentAnalyzer())
212
- }
213
-
214
- // IMPORTANT: Unbind all first, then rebind together to avoid session reconfiguration timeout
215
- provider.unbindAll()
216
-
217
- // Rebind BOTH at the same time
218
202
  camera = provider.bindToLifecycle(
219
203
  lifecycleOwner,
220
204
  cameraSelector,
221
205
  preview,
222
- imageAnalysis
206
+ imageCapture
223
207
  )
224
- analysisBound = true
225
- Log.d(TAG, "[CAMERAX-FIX-V5] ImageAnalysis added successfully after unbind/rebind")
226
- } catch (e: Exception) {
227
- Log.e(TAG, "[CAMERAX-FIX-V5] Failed to add ImageAnalysis", e)
228
- analysisBound = false
208
+ Log.d(TAG, "[CAMERAX-V6] Preview + ImageCapture bound successfully")
229
209
 
230
- // Fallback: rebind preview only
231
- try {
232
- camera = provider.bindToLifecycle(
233
- lifecycleOwner,
234
- cameraSelector,
235
- preview
236
- )
237
- Log.d(TAG, "[CAMERAX-FIX-V5] Fallback: Preview only")
238
- } catch (fallbackException: Exception) {
239
- Log.e(TAG, "[CAMERAX-FIX-V5] Fallback also failed", fallbackException)
210
+ // Start periodic frame capture for analysis
211
+ if (detectionEnabled) {
212
+ isAnalysisActive = true
213
+ analysisHandler.postDelayed(analysisRunnable, 500) // Start after 500ms
214
+ Log.d(TAG, "[CAMERAX-V6] Started periodic frame capture for analysis")
240
215
  }
216
+
217
+ } catch (e: Exception) {
218
+ Log.e(TAG, "[CAMERAX-V6] Failed to bind camera use cases", e)
241
219
  }
242
220
  }
243
221
 
244
- private inner class DocumentAnalyzer : ImageAnalysis.Analyzer {
245
- override fun analyze(imageProxy: androidx.camera.core.ImageProxy) {
246
- try {
247
- val rotationDegrees = imageProxy.imageInfo.rotationDegrees
248
- val nv21 = imageProxy.toNv21()
249
- lastFrame.set(
250
- LastFrame(
251
- nv21,
252
- imageProxy.width,
253
- imageProxy.height,
254
- rotationDegrees,
255
- useFrontCamera
222
+ private fun captureFrameForAnalysis() {
223
+ val capture = imageCapture ?: return
224
+
225
+ capture.takePicture(cameraExecutor, object : ImageCapture.OnImageCapturedCallback() {
226
+ override fun onCaptureSuccess(image: androidx.camera.core.ImageProxy) {
227
+ try {
228
+ val rotationDegrees = image.imageInfo.rotationDegrees
229
+ val nv21 = image.toNv21()
230
+
231
+ lastFrame.set(
232
+ LastFrame(
233
+ nv21,
234
+ image.width,
235
+ image.height,
236
+ rotationDegrees,
237
+ useFrontCamera
238
+ )
256
239
  )
257
- )
258
240
 
259
- val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) {
260
- imageProxy.height
261
- } else {
262
- imageProxy.width
263
- }
241
+ val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) {
242
+ image.height
243
+ } else {
244
+ image.width
245
+ }
264
246
 
265
- val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) {
266
- imageProxy.width
267
- } else {
268
- imageProxy.height
269
- }
247
+ val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) {
248
+ image.width
249
+ } else {
250
+ image.height
251
+ }
270
252
 
271
- if (detectionEnabled) {
272
253
  val rectangle = DocumentDetector.detectRectangleInYUV(
273
254
  nv21,
274
- imageProxy.width,
275
- imageProxy.height,
255
+ image.width,
256
+ image.height,
276
257
  rotationDegrees
277
258
  )
278
259
  onFrameAnalyzed?.invoke(rectangle, frameWidth, frameHeight)
279
- } else {
280
- onFrameAnalyzed?.invoke(null, frameWidth, frameHeight)
260
+ } catch (e: Exception) {
261
+ Log.e(TAG, "[CAMERAX-V6] Error analyzing frame", e)
262
+ } finally {
263
+ image.close()
281
264
  }
282
- } catch (e: Exception) {
283
- Log.e(TAG, "[CAMERAX] Error analyzing frame", e)
284
- } finally {
285
- imageProxy.close()
286
265
  }
287
- }
266
+
267
+ override fun onError(exception: ImageCaptureException) {
268
+ Log.e(TAG, "[CAMERAX-V6] Frame capture for analysis failed", exception)
269
+ }
270
+ })
288
271
  }
289
272
 
290
273
  private fun nv21ToJpeg(nv21: ByteArray, width: Int, height: Int, quality: Int): ByteArray {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.221.0",
3
+ "version": "3.222.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",