-
Notifications
You must be signed in to change notification settings - Fork 179
Hotfix/android camera msdk #1037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 5 commits
6cd7572
06ec82e
bae9785
3c51d6e
fe49618
81d890b
984e7e5
8b28f28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -96,6 +96,7 @@ android { | |||||||||||
| exclude 'META-INF/androidx.exifinterface_exifinterface.version' | ||||||||||||
| pickFirst '**/libc++_shared.so' | ||||||||||||
| pickFirst '**/libjsc.so' | ||||||||||||
| pickFirst 'META-INF/versions/9/OSGI-INF/MANIFEST.MF' | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid pickFirst on OSGI manifest; exclude instead for deterministic builds. pickFirst masks conflicts and depends on dependency order. Prefer excluding the OSGI metadata so builds are stable. - pickFirst 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'
+ exclude 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -155,4 +156,8 @@ dependencies { | |||||||||||
| implementation 'androidx.core:core-ktx:1.12.0' | ||||||||||||
|
|
||||||||||||
| implementation 'com.github.mhshams:jnbis:2.0.2' | ||||||||||||
|
|
||||||||||||
| // QR Code scanning dependencies | ||||||||||||
| implementation 'com.google.zxing:core:3.5.2' | ||||||||||||
| implementation 'com.google.zxing:android-core:3.3.0' | ||||||||||||
|
Comment on lines
+160
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drop zxing:android-core (deprecated, pre‑AndroidX); it can clash with AndroidX and add legacy support libs. If you’re decoding from CameraX frames, ZXing core is sufficient. If you need camera UI/helpers, use journeyapps’ AndroidX artifact instead. Minimal change: - implementation 'com.google.zxing:core:3.5.2'
- implementation 'com.google.zxing:android-core:3.3.0'
+ implementation 'com.google.zxing:core:3.5.2'Alternative (if you want the embedded helpers): - implementation 'com.google.zxing:core:3.5.2'
- implementation 'com.google.zxing:android-core:3.3.0'
+ implementation 'com.journeyapps:zxing-android-embedded:4.3.0'📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| } | ||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,36 +22,104 @@ ReactContextBaseJavaModule(reactContext), CameraMLKitFragment.CameraMLKitCallbac | |||||||||||||||||||||||||
| override fun getName() = "SelfMRZScannerModule" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private var scanPromise: Promise? = null | ||||||||||||||||||||||||||
| private var currentContainer: FrameLayout? = null | ||||||||||||||||||||||||||
| private var currentFragment: CameraMLKitFragment? = null | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @ReactMethod | ||||||||||||||||||||||||||
| fun startScanning(promise: Promise) { | ||||||||||||||||||||||||||
| scanPromise = promise | ||||||||||||||||||||||||||
| val activity = reactApplicationContext.currentActivity as? FragmentActivity ?: return | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| activity.runOnUiThread { | ||||||||||||||||||||||||||
| val container = FrameLayout(activity) | ||||||||||||||||||||||||||
| val containerId = View.generateViewId() | ||||||||||||||||||||||||||
| container.id = containerId | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| activity.addContentView(container, ViewGroup.LayoutParams( | ||||||||||||||||||||||||||
| ViewGroup.LayoutParams.MATCH_PARENT, | ||||||||||||||||||||||||||
| ViewGroup.LayoutParams.MATCH_PARENT | ||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| activity.supportFragmentManager | ||||||||||||||||||||||||||
| .beginTransaction() | ||||||||||||||||||||||||||
| .replace(containerId, CameraMLKitFragment(this)) | ||||||||||||||||||||||||||
| .commit() | ||||||||||||||||||||||||||
| val activity = reactApplicationContext.currentActivity as? FragmentActivity | ||||||||||||||||||||||||||
| if (currentFragment != null || currentContainer != null || scanPromise != null) { | ||||||||||||||||||||||||||
| promise.reject("E_SCAN_IN_PROGRESS", "Scanning already in progress") | ||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (activity == null) { | ||||||||||||||||||||||||||
| promise.reject("E_NO_ACTIVITY", "No FragmentActivity found") | ||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| scanPromise = promise | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| activity.runOnUiThread { | ||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||
| val container = FrameLayout(activity) | ||||||||||||||||||||||||||
| // just using view.generateViewId() doesn't work. | ||||||||||||||||||||||||||
| val containerId = generateUnusedId(activity.window.decorView as ViewGroup) | ||||||||||||||||||||||||||
| container.id = containerId | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| container.layoutParams = ViewGroup.LayoutParams( | ||||||||||||||||||||||||||
|
Comment on lines
+44
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prevent orphaned overlays on exceptions by storing container early. If an exception occurs before val container = FrameLayout(activity)
// just using view.generateViewId() doesn't work.
val containerId = generateUnusedId(activity.window.decorView as ViewGroup)
container.id = containerId
+ // Ensure we can clean up even if later steps fail
+ currentContainer = container
@@
- // Store references for cleanup
- currentContainer = container
- currentFragment = fragment
+ // Store fragment reference for cleanup
+ currentFragment = fragmentAlso applies to: 66-68 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| ViewGroup.LayoutParams.MATCH_PARENT, | ||||||||||||||||||||||||||
| ViewGroup.LayoutParams.MATCH_PARENT | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| container.isFocusable = true | ||||||||||||||||||||||||||
| container.isFocusableInTouchMode = true | ||||||||||||||||||||||||||
| container.setBackgroundColor(android.graphics.Color.BLACK) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| activity.addContentView(container, ViewGroup.LayoutParams( | ||||||||||||||||||||||||||
| ViewGroup.LayoutParams.MATCH_PARENT, | ||||||||||||||||||||||||||
| ViewGroup.LayoutParams.MATCH_PARENT | ||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| val fragment = CameraMLKitFragment(this@SelfMRZScannerModule) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Store references for cleanup | ||||||||||||||||||||||||||
| currentContainer = container | ||||||||||||||||||||||||||
| currentFragment = fragment | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| activity.supportFragmentManager | ||||||||||||||||||||||||||
| .beginTransaction() | ||||||||||||||||||||||||||
| .replace(containerId, fragment) | ||||||||||||||||||||||||||
| .commitNow() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Comment on lines
+69
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid commitNow crashes when state is saved; guard and fall back.
- activity.supportFragmentManager
- .beginTransaction()
- .replace(containerId, fragment)
- .commitNow()
+ val fm = activity.supportFragmentManager
+ val tx = fm.beginTransaction().replace(containerId, fragment)
+ if (fm.isStateSaved) {
+ tx.commitAllowingStateLoss()
+ android.util.Log.w("SelfMRZScannerModule", "Fragment committed with state loss due to saved state")
+ } else {
+ tx.commitNow()
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } catch (e: Exception) { | ||||||||||||||||||||||||||
| android.util.Log.e("SelfMRZScannerModule", "Error in startScanning", e) | ||||||||||||||||||||||||||
| promise.reject("E_SCANNING_ERROR", e.message, e) | ||||||||||||||||||||||||||
| cleanup() | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| override fun onPassportRead(mrzInfo: MRZInfo) { | ||||||||||||||||||||||||||
| scanPromise?.resolve(mrzInfo.toString()) | ||||||||||||||||||||||||||
| scanPromise = null | ||||||||||||||||||||||||||
| cleanup() | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
82
to
86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t return raw MRZ string; minimize/structure PII.
- scanPromise?.resolve(mrzInfo.toString())
+ val payload = com.facebook.react.bridge.Arguments.createMap().apply {
+ putString("documentType", mrzInfo.documentCode)
+ putString("issuingState", mrzInfo.issuingState)
+ putString("nationality", mrzInfo.nationality)
+ putString("documentNumberMasked", mrzInfo.documentNumber?.let { maskDoc(it) })
+ // Add more fields only if strictly required; avoid full MRZ dump.
+ }
+ scanPromise?.resolve(payload)Add alongside the class (helper): private fun maskDoc(v: String): String =
if (v.length <= 3) "***" else "*".repeat(v.length - 3) + v.takeLast(3)If JS currently expects a string, consider a temporary dual-path (behind a debug flag) or bumping the API contract with a clear migration note. |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| override fun onError(e: Exception) { | ||||||||||||||||||||||||||
| scanPromise?.reject(e) | ||||||||||||||||||||||||||
| scanPromise = null | ||||||||||||||||||||||||||
| cleanup() | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
53
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use explicit error code for JS contract consistency.
- override fun onError(e: Exception) {
- scanPromise?.reject(e)
+ override fun onError(e: Exception) {
+ scanPromise?.reject("E_MRZ_SCAN_ERROR", e.message, e)
scanPromise = null
cleanup()
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private fun generateUnusedId(root: ViewGroup): Int { | ||||||||||||||||||||||||||
| var id: Int | ||||||||||||||||||||||||||
| do { id = View.generateViewId() } while (root.findViewById<View>(id) != null) | ||||||||||||||||||||||||||
| return id | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private fun cleanup() { | ||||||||||||||||||||||||||
| val activity = reactApplicationContext.currentActivity as? FragmentActivity | ||||||||||||||||||||||||||
| val fragment = currentFragment | ||||||||||||||||||||||||||
| val container = currentContainer | ||||||||||||||||||||||||||
| currentFragment = null | ||||||||||||||||||||||||||
| currentContainer = null | ||||||||||||||||||||||||||
| scanPromise = null | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (activity != null && fragment != null && container != null) { | ||||||||||||||||||||||||||
| activity.runOnUiThread { | ||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||
| activity.supportFragmentManager | ||||||||||||||||||||||||||
| .beginTransaction() | ||||||||||||||||||||||||||
| .remove(fragment) | ||||||||||||||||||||||||||
| .commitAllowingStateLoss() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| (container.parent as? ViewGroup)?.removeView(container) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| android.util.Log.d("SelfMRZScannerModule", "Cleaned up fragment and container") | ||||||||||||||||||||||||||
| } catch (e: Exception) { | ||||||||||||||||||||||||||
| android.util.Log.e("SelfMRZScannerModule", "Error during cleanup", e) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -29,8 +29,9 @@ class SelfOCRViewManager( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun getName() = REACT_CLASS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun createViewInstance(reactContext: ThemedReactContext) = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FrameLayout(reactContext) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return FrameLayout(reactContext) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun getCommandsMap() = mapOf( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "create" to COMMAND_CREATE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -43,11 +44,50 @@ class SelfOCRViewManager( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args: ReadableArray? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super.receiveCommand(root, commandId, args) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val reactNativeViewId = requireNotNull(args).getInt(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| when (commandId.toInt()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMAND_CREATE -> createFragment(root, reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMAND_DESTROY -> destroyFragment(root, reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val reactNativeViewId = args?.getInt(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (reactNativeViewId == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.w("SelfOCRViewManager", "receiveCommand called with null or empty args for command: $commandId") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard ReadableArray bounds/types; current access can throw args?.getInt(0) will still crash if args exists but is empty or type ≠ number. Validate size and type before reading. Apply: - val reactNativeViewId = args?.getInt(0)
- if (reactNativeViewId == null) {
- android.util.Log.w("SelfOCRViewManager", "receiveCommand called with null or empty args for command: $commandId")
- return
- }
+ val reactNativeViewId = args?.let { arr ->
+ if (arr.size() > 0 && arr.getType(0) == ReadableType.Number) arr.getInt(0) else null
+ }
+ if (reactNativeViewId == null) {
+ android.util.Log.w("SelfOCRViewManager", "receiveCommand: missing/invalid viewId for command=$commandId; args=$args")
+ return
+ }Add import: import com.facebook.react.bridge.ReadableType🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| when (commandId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "create" -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createFragment(root, reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "destroy" -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| destroyFragment(root, reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.w("SelfOCRViewManager", "Unknown command: $commandId") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Alternative method signature for newer React Native versions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun receiveCommand( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| root: FrameLayout, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commandId: Int, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args: ReadableArray? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super.receiveCommand(root, commandId, args) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val reactNativeViewId = args?.getInt(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (reactNativeViewId == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.w("SelfOCRViewManager", "receiveCommand called with null or empty args for command: $commandId") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| when (commandId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMAND_CREATE -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createFragment(root, reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMAND_DESTROY -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| destroyFragment(root, reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.w("SelfOCRViewManager", "Unknown command: $commandId") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -57,18 +97,55 @@ class SelfOCRViewManager( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (index == 1) propHeight = value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // @ReactProp(name = "width") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // fun setWidth(view: FrameLayout, width: Int) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // propWidth = width | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // @ReactProp(name = "height") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // fun setHeight(view: FrameLayout, height: Int) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // propHeight = height | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun createFragment(root: FrameLayout, reactNativeViewId: Int) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.reactNativeViewId = reactNativeViewId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val parentView = root.findViewById<ViewGroup>(reactNativeViewId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (parentView == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.e("SelfOCRViewManager", "Parent view not found for ID: $reactNativeViewId") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setupLayout(parentView) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val activity = reactContext.currentActivity as? FragmentActivity | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (activity == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.e("SelfOCRViewManager", "No FragmentActivity found") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if activity is in a valid state | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (activity.isFinishing || activity.isDestroyed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.e("SelfOCRViewManager", "Activity is finishing or destroyed") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Null-check container before layout; bail early if missing root.findViewById may return null; setupLayout(parentView) will then NPE and the subsequent fragment transaction will still run, risking IllegalArgumentException on replace(). - val parentView = root.findViewById<ViewGroup>(reactNativeViewId)
- setupLayout(parentView)
+ val parentView = root.findViewById<ViewGroup?>(reactNativeViewId)
+ if (parentView == null) {
+ android.util.Log.e("SelfOCRViewManager", "Container view $reactNativeViewId not found in root")
+ return
+ }
+ setupLayout(parentView)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val cameraFragment = CameraMLKitFragment(this) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // val cameraFragment = MyFragment() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val activity = reactContext.currentActivity as FragmentActivity | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| activity.supportFragmentManager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .beginTransaction() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replace(reactNativeViewId, cameraFragment, reactNativeViewId.toString()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .commit() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.d("SelfOCRViewManager", "Starting fragment transaction") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Post to ensure activity is fully ready | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| activity.window.decorView.post { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!activity.isFinishing && !activity.isDestroyed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| activity.supportFragmentManager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .beginTransaction() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replace(reactNativeViewId, cameraFragment, reactNativeViewId.toString()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .commitNow() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.e("SelfOCRViewManager", "Activity no longer valid for fragment transaction") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e: Exception) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| android.util.Log.e("SelfOCRViewManager", "Fragment transaction failed", e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+132
to
+148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid commitNow after state save; check isStateSaved and use reordering commitNow() can throw IllegalStateException if state is saved. Check FragmentManager.isStateSaved and prefer commit() with setReorderingAllowed(true). - activity.window.decorView.post {
- try {
- if (!activity.isFinishing && !activity.isDestroyed) {
- activity.supportFragmentManager
- .beginTransaction()
- .replace(reactNativeViewId, cameraFragment, reactNativeViewId.toString())
- .commitNow()
- } else {
- android.util.Log.e("SelfOCRViewManager", "Activity no longer valid for fragment transaction")
- }
- } catch (e: Exception) {
- android.util.Log.e("SelfOCRViewManager", "Fragment transaction failed", e)
- }
- }
+ activity.window.decorView.post {
+ try {
+ if (!activity.isFinishing && !activity.isDestroyed) {
+ val fm = activity.supportFragmentManager
+ if (!fm.isStateSaved) {
+ fm.beginTransaction()
+ .setReorderingAllowed(true)
+ .replace(reactNativeViewId, cameraFragment, reactNativeViewId.toString())
+ .commit()
+ } else {
+ android.util.Log.w("SelfOCRViewManager", "FragmentManager state saved; aborting create for $reactNativeViewId")
+ }
+ } else {
+ android.util.Log.e("SelfOCRViewManager", "Activity no longer valid for fragment transaction")
+ }
+ } catch (e: Exception) {
+ android.util.Log.e("SelfOCRViewManager", "Fragment transaction failed", e)
+ }
+ }🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun destroyFragment(root: FrameLayout, reactNativeViewId: Int) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -97,8 +174,8 @@ class SelfOCRViewManager( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun manuallyLayoutChildren(view: View) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // propWidth and propHeight coming from react-native props | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val width = requireNotNull(propWidth) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val height = requireNotNull(propHeight) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val width = propWidth ?: 800 // Default fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val height = propHeight ?: 800 // Default fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+177
to
179
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Default 800x800 sizing may fight RN layout and cause over/under-sizing Hard defaults in native (px) can conflict with RN dp sizing, especially since RCTFragment passes PixelRatio.getPixelSizeForLayoutSize(800) on the JS side. Consider deriving from view.measuredWidth/Height when available, falling back only if zero. - val width = propWidth ?: 800 // Default fallback
- val height = propHeight ?: 800 // Default fallback
+ val measuredW = if (view.measuredWidth > 0) view.measuredWidth else null
+ val measuredH = if (view.measuredHeight > 0) view.measuredHeight else null
+ val width = propWidth ?: measuredW ?: 800
+ val height = propHeight ?: measuredH ?: 800📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| view.measure( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t export mock ID generators from the public SDK entrypoint
Shipping genMockIdDoc and genMockIdDocAndInitDataParsing on the main index increases misuse risk in production apps and widens the attack surface. Move these to a dedicated testing entrypoint.
Outside-of-hunk: create a test-only entrypoint and re-export there:
Consider also moving genAndInitMockPassportData (Line 63) for consistency.
🤖 Prompt for AI Agents