react-native-rectangle-doc-scanner 10.7.0 → 10.9.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.
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
package com.reactnativerectangledocscanner
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.graphics.SurfaceTexture
|
|
4
5
|
import android.util.Log
|
|
6
|
+
import android.util.Size
|
|
7
|
+
import android.view.Surface
|
|
8
|
+
import android.view.TextureView
|
|
5
9
|
import androidx.camera.core.*
|
|
6
10
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
|
7
|
-
import androidx.camera.view.PreviewView
|
|
8
11
|
import androidx.core.content.ContextCompat
|
|
9
12
|
import androidx.lifecycle.LifecycleOwner
|
|
10
13
|
import com.google.mlkit.vision.common.InputImage
|
|
@@ -16,12 +19,12 @@ import java.util.concurrent.Executors
|
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* CameraX-based camera controller for document scanning
|
|
19
|
-
* Handles Preview, ImageAnalysis (ML Kit + OpenCV), and ImageCapture
|
|
22
|
+
* Handles Preview (via TextureView), ImageAnalysis (ML Kit + OpenCV), and ImageCapture
|
|
20
23
|
*/
|
|
21
24
|
class CameraController(
|
|
22
25
|
private val context: Context,
|
|
23
26
|
private val lifecycleOwner: LifecycleOwner,
|
|
24
|
-
private val
|
|
27
|
+
private val textureView: TextureView
|
|
25
28
|
) {
|
|
26
29
|
private var camera: Camera? = null
|
|
27
30
|
private var cameraProvider: ProcessCameraProvider? = null
|
|
@@ -93,19 +96,57 @@ class CameraController(
|
|
|
93
96
|
CameraSelector.DEFAULT_BACK_CAMERA
|
|
94
97
|
}
|
|
95
98
|
|
|
96
|
-
// Preview UseCase
|
|
97
|
-
Log.d(TAG, "[CAMERAX]
|
|
98
|
-
Log.d(TAG, "[CAMERAX]
|
|
99
|
-
Log.d(TAG, "[CAMERAX]
|
|
100
|
-
|
|
99
|
+
// Preview UseCase with TextureView
|
|
100
|
+
Log.d(TAG, "[CAMERAX] TextureView size: ${textureView.width}x${textureView.height}")
|
|
101
|
+
Log.d(TAG, "[CAMERAX] TextureView visibility: ${textureView.visibility}")
|
|
102
|
+
Log.d(TAG, "[CAMERAX] TextureView isAvailable: ${textureView.isAvailable}")
|
|
103
|
+
|
|
104
|
+
// Force portrait orientation (app is portrait-only)
|
|
105
|
+
val targetRotation = android.view.Surface.ROTATION_0
|
|
106
|
+
Log.d(TAG, "[CAMERAX] Setting target rotation to ROTATION_0 (portrait-only app)")
|
|
107
|
+
|
|
101
108
|
preview = Preview.Builder()
|
|
102
|
-
.
|
|
109
|
+
.setTargetRotation(targetRotation) // Force portrait
|
|
103
110
|
.build()
|
|
104
111
|
.also { previewUseCase ->
|
|
105
|
-
Log.d(TAG, "[CAMERAX] Setting SurfaceProvider...")
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
previewUseCase.setSurfaceProvider(ContextCompat.getMainExecutor(context)
|
|
112
|
+
Log.d(TAG, "[CAMERAX] Setting SurfaceProvider for TextureView...")
|
|
113
|
+
|
|
114
|
+
// Set custom SurfaceProvider for TextureView
|
|
115
|
+
previewUseCase.setSurfaceProvider(ContextCompat.getMainExecutor(context)) { request ->
|
|
116
|
+
Log.d(TAG, "[CAMERAX] Surface requested - resolution: ${request.resolution}")
|
|
117
|
+
|
|
118
|
+
val surfaceTexture = textureView.surfaceTexture
|
|
119
|
+
if (surfaceTexture != null) {
|
|
120
|
+
Log.d(TAG, "[CAMERAX] SurfaceTexture available, providing surface")
|
|
121
|
+
surfaceTexture.setDefaultBufferSize(
|
|
122
|
+
request.resolution.width,
|
|
123
|
+
request.resolution.height
|
|
124
|
+
)
|
|
125
|
+
val surface = Surface(surfaceTexture)
|
|
126
|
+
request.provideSurface(surface, ContextCompat.getMainExecutor(context)) { result ->
|
|
127
|
+
Log.d(TAG, "[CAMERAX] Surface provided - result: ${result.resultCode}")
|
|
128
|
+
surface.release()
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
Log.e(TAG, "[CAMERAX] SurfaceTexture is null! Waiting for TextureView to be ready...")
|
|
132
|
+
// Set listener for when SurfaceTexture becomes available
|
|
133
|
+
textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
|
|
134
|
+
override fun onSurfaceTextureAvailable(st: SurfaceTexture, width: Int, height: Int) {
|
|
135
|
+
Log.d(TAG, "[CAMERAX] SurfaceTexture now available ($width x $height)")
|
|
136
|
+
st.setDefaultBufferSize(request.resolution.width, request.resolution.height)
|
|
137
|
+
val surface = Surface(st)
|
|
138
|
+
request.provideSurface(surface, ContextCompat.getMainExecutor(context)) { result ->
|
|
139
|
+
Log.d(TAG, "[CAMERAX] Surface provided (delayed) - result: ${result.resultCode}")
|
|
140
|
+
surface.release()
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {}
|
|
145
|
+
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = true
|
|
146
|
+
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
109
150
|
|
|
110
151
|
Log.d(TAG, "[CAMERAX] SurfaceProvider set successfully")
|
|
111
152
|
}
|
|
@@ -114,6 +155,7 @@ class CameraController(
|
|
|
114
155
|
imageAnalyzer = ImageAnalysis.Builder()
|
|
115
156
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
116
157
|
.setTargetResolution(android.util.Size(1280, 960)) // Limit resolution for analysis
|
|
158
|
+
.setTargetRotation(targetRotation) // Match preview rotation
|
|
117
159
|
.build()
|
|
118
160
|
.also {
|
|
119
161
|
it.setAnalyzer(cameraExecutor) { imageProxy ->
|
|
@@ -128,6 +170,7 @@ class CameraController(
|
|
|
128
170
|
// ImageCapture UseCase
|
|
129
171
|
imageCapture = ImageCapture.Builder()
|
|
130
172
|
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
|
|
173
|
+
.setTargetRotation(targetRotation) // Match preview rotation
|
|
131
174
|
.build()
|
|
132
175
|
|
|
133
176
|
try {
|
|
@@ -147,7 +190,7 @@ class CameraController(
|
|
|
147
190
|
|
|
148
191
|
// Add ImageAnalysis after a short delay to avoid timeout
|
|
149
192
|
if (detectionEnabled) {
|
|
150
|
-
|
|
193
|
+
textureView.post {
|
|
151
194
|
try {
|
|
152
195
|
Log.d(TAG, "[CAMERAX] Adding ImageAnalysis...")
|
|
153
196
|
camera = cameraProvider.bindToLifecycle(
|
|
@@ -166,19 +209,6 @@ class CameraController(
|
|
|
166
209
|
|
|
167
210
|
Log.d(TAG, "[CAMERAX] Camera bound successfully")
|
|
168
211
|
|
|
169
|
-
// Monitor preview stream state
|
|
170
|
-
previewView.previewStreamState.observe(lifecycleOwner) { state ->
|
|
171
|
-
Log.d(TAG, "[CAMERAX] PreviewStreamState changed: $state")
|
|
172
|
-
when (state) {
|
|
173
|
-
androidx.camera.view.PreviewView.StreamState.IDLE ->
|
|
174
|
-
Log.w(TAG, "[CAMERAX] Preview stream is IDLE")
|
|
175
|
-
androidx.camera.view.PreviewView.StreamState.STREAMING ->
|
|
176
|
-
Log.d(TAG, "[CAMERAX] Preview stream is STREAMING ✓")
|
|
177
|
-
else ->
|
|
178
|
-
Log.d(TAG, "[CAMERAX] Preview stream state: $state")
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
212
|
} catch (e: Exception) {
|
|
183
213
|
Log.e(TAG, "[CAMERAX] Use case binding failed", e)
|
|
184
214
|
}
|
|
@@ -353,19 +383,17 @@ class CameraController(
|
|
|
353
383
|
}
|
|
354
384
|
|
|
355
385
|
fun refreshTransform() {
|
|
356
|
-
// CameraX
|
|
357
|
-
|
|
358
|
-
Log.d(TAG, "[CAMERAX] Transform refresh requested - handled automatically by PreviewView")
|
|
386
|
+
// CameraX with TextureView - no manual transform needed
|
|
387
|
+
Log.d(TAG, "[CAMERAX] Transform refresh requested - handled automatically")
|
|
359
388
|
}
|
|
360
389
|
|
|
361
|
-
// Simplified coordinate mapping
|
|
390
|
+
// Simplified coordinate mapping for TextureView
|
|
362
391
|
fun mapRectangleToView(rectangle: Rectangle?, imageWidth: Int, imageHeight: Int): Rectangle? {
|
|
363
392
|
if (rectangle == null || imageWidth <= 0 || imageHeight <= 0) return null
|
|
364
393
|
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
val
|
|
368
|
-
val viewHeight = previewView.height.toFloat()
|
|
394
|
+
// Simple proportional scaling for TextureView
|
|
395
|
+
val viewWidth = textureView.width.toFloat()
|
|
396
|
+
val viewHeight = textureView.height.toFloat()
|
|
369
397
|
|
|
370
398
|
if (viewWidth <= 0 || viewHeight <= 0) return null
|
|
371
399
|
|
|
@@ -389,9 +417,9 @@ class CameraController(
|
|
|
389
417
|
}
|
|
390
418
|
|
|
391
419
|
fun getPreviewViewport(): android.graphics.RectF? {
|
|
392
|
-
// With
|
|
393
|
-
val width =
|
|
394
|
-
val height =
|
|
420
|
+
// With TextureView, the viewport is simply the view bounds
|
|
421
|
+
val width = textureView.width.toFloat()
|
|
422
|
+
val height = textureView.height.toFloat()
|
|
395
423
|
|
|
396
424
|
if (width <= 0 || height <= 0) return null
|
|
397
425
|
|
package/android/src/camera2/kotlin/com/reactnativerectangledocscanner/DocumentScannerView.kt
CHANGED
|
@@ -10,8 +10,8 @@ import android.graphics.PorterDuffXfermode
|
|
|
10
10
|
import org.opencv.core.Point
|
|
11
11
|
import android.util.Log
|
|
12
12
|
import android.view.View
|
|
13
|
+
import android.view.TextureView
|
|
13
14
|
import android.widget.FrameLayout
|
|
14
|
-
import androidx.camera.view.PreviewView
|
|
15
15
|
import androidx.lifecycle.Lifecycle
|
|
16
16
|
import androidx.lifecycle.LifecycleOwner
|
|
17
17
|
import androidx.lifecycle.LifecycleRegistry
|
|
@@ -28,7 +28,7 @@ import kotlin.math.max
|
|
|
28
28
|
|
|
29
29
|
class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), LifecycleOwner {
|
|
30
30
|
private val themedContext = context
|
|
31
|
-
private val previewView:
|
|
31
|
+
private val previewView: TextureView
|
|
32
32
|
private val overlayView: OverlayView
|
|
33
33
|
private val useNativeOverlay = false
|
|
34
34
|
private var cameraController: CameraController? = null
|
|
@@ -81,21 +81,19 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
81
81
|
lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
|
|
82
82
|
Log.d(TAG, "[INIT] Lifecycle state: ${lifecycleRegistry.currentState}")
|
|
83
83
|
|
|
84
|
-
// Create preview
|
|
85
|
-
Log.d(TAG, "[INIT] Creating
|
|
86
|
-
previewView =
|
|
84
|
+
// Create TextureView for camera preview (CameraX compatible)
|
|
85
|
+
Log.d(TAG, "[INIT] Creating TextureView...")
|
|
86
|
+
previewView = TextureView(context).apply {
|
|
87
87
|
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
88
88
|
visibility = View.VISIBLE
|
|
89
89
|
keepScreenOn = true
|
|
90
|
-
implementationMode = PreviewView.ImplementationMode.COMPATIBLE // Use TextureView - more compatible with React Native
|
|
91
|
-
scaleType = PreviewView.ScaleType.FILL_CENTER // Fill and center the preview
|
|
92
90
|
}
|
|
93
|
-
Log.d(TAG, "[INIT]
|
|
94
|
-
Log.d(TAG, "[INIT]
|
|
91
|
+
Log.d(TAG, "[INIT] TextureView created: $previewView")
|
|
92
|
+
Log.d(TAG, "[INIT] TextureView visibility: ${previewView.visibility}")
|
|
95
93
|
|
|
96
|
-
Log.d(TAG, "[INIT] Adding
|
|
94
|
+
Log.d(TAG, "[INIT] Adding TextureView to parent...")
|
|
97
95
|
addView(previewView, 0) // Add at index 0 (back)
|
|
98
|
-
Log.d(TAG, "[INIT]
|
|
96
|
+
Log.d(TAG, "[INIT] TextureView added, childCount: $childCount")
|
|
99
97
|
|
|
100
98
|
// Create overlay view for drawing rectangle
|
|
101
99
|
Log.d(TAG, "[INIT] Creating OverlayView...")
|