From 6cd75723008667b17cea7f621f157910809c53b7 Mon Sep 17 00:00:00 2001 From: seshanthS Date: Wed, 10 Sep 2025 03:12:29 +0530 Subject: [PATCH 1/6] Fix: Android MRZ scan MSDK --- .../selfxyz/selfSDK/SelfMRZScannerModule.kt | 95 +++++++++++++++---- .../com/selfxyz/selfSDK/SelfOCRViewManager.kt | 89 ++++++++++++++--- .../com/selfxyz/selfSDK/ui/CameraFragment.kt | 7 +- .../src/components/MRZScannerView.tsx | 2 +- 4 files changed, 159 insertions(+), 34 deletions(-) diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt index 13f5a0b38..e84cc7513 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt @@ -22,36 +22,97 @@ 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() + scanPromise = promise + val activity = reactApplicationContext.currentActivity as? FragmentActivity + if (activity == null) { + promise.reject("E_NO_ACTIVITY", "No FragmentActivity found") + return + } + + + 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( + 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() + + } catch (e: Exception) { + android.util.Log.e("SelfMRZScannerModule", "Error in startScanning", e) + promise.reject("E_SCANNING_ERROR", e.message, e) + } } } override fun onPassportRead(mrzInfo: MRZInfo) { scanPromise?.resolve(mrzInfo.toString()) scanPromise = null + cleanup() } override fun onError(e: Exception) { scanPromise?.reject(e) scanPromise = null + cleanup() + } + + private fun generateUnusedId(root: ViewGroup): Int { + var id: Int + do { id = View.generateViewId() } while (root.findViewById(id) != null) + return id + } + + private fun cleanup() { + val activity = reactApplicationContext.currentActivity as? FragmentActivity + if (activity != null && currentFragment != null && currentContainer != null) { + activity.runOnUiThread { + try { + activity.supportFragmentManager + .beginTransaction() + .remove(currentFragment!!) + .commitNow() + + val parent = currentContainer!!.parent as? ViewGroup + parent?.removeView(currentContainer) + + android.util.Log.d("SelfMRZScannerModule", "Cleaned up fragment and container") + } catch (e: Exception) { + android.util.Log.e("SelfMRZScannerModule", "Error during cleanup", e) + } + + currentFragment = null + currentContainer = null + } + } } } diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt index 87e5d701a..cfc066813 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt @@ -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, @@ -45,9 +46,38 @@ class SelfOCRViewManager( super.receiveCommand(root, commandId, args) val reactNativeViewId = requireNotNull(args).getInt(0) - when (commandId.toInt()) { - COMMAND_CREATE -> createFragment(root, reactNativeViewId) - COMMAND_DESTROY -> destroyFragment(root, reactNativeViewId) + when (commandId) { + "create" -> { + createFragment(root, reactNativeViewId) + } + "destroy" -> { + destroyFragment(root, reactNativeViewId) + } + else -> { + android.util.Log.w("SelfOCRViewManager", "Unknown command: $commandId") + } + } + } + + // Alternative method signature for newer React Native versions + override fun receiveCommand( + root: FrameLayout, + commandId: Int, + args: ReadableArray? + ) { + super.receiveCommand(root, commandId, args) + val reactNativeViewId = requireNotNull(args).getInt(0) + + when (commandId) { + COMMAND_CREATE -> { + createFragment(root, reactNativeViewId) + } + COMMAND_DESTROY -> { + destroyFragment(root, reactNativeViewId) + } + else -> { + android.util.Log.w("SelfOCRViewManager", "Unknown command: $commandId") + } } } @@ -57,18 +87,51 @@ 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(reactNativeViewId) 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 + } + 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) + } + } } private fun destroyFragment(root: FrameLayout, reactNativeViewId: Int) { @@ -97,8 +160,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 view.measure( View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/CameraFragment.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/CameraFragment.kt index 2e156d76c..47aaabbf3 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/CameraFragment.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/CameraFragment.kt @@ -147,7 +147,7 @@ abstract class CameraFragment : androidx.fragment.app.Fragment(), ActivityCompat buildCamera(cameraPreview!!, initialLensPosition) hasCameraPermission = hasCameraPermission() - if (hasCameraPermission) { + if (!hasCameraPermission) { checkPermissions(requestedPermissions) } else { fotoapparat?.start() @@ -157,7 +157,7 @@ abstract class CameraFragment : androidx.fragment.app.Fragment(), ActivityCompat override fun onPause() { hasCameraPermission = hasCameraPermission() - if (!hasCameraPermission) { + if (hasCameraPermission) { fotoapparat?.stop() } fotoapparat = null; @@ -246,7 +246,8 @@ abstract class CameraFragment : androidx.fragment.app.Fragment(), ActivityCompat //////////////////////////////////////////////////////////////////////////////////////// protected fun hasCameraPermission(): Boolean { - return ContextCompat.checkSelfPermission(context!!, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED + val hasPermission = ContextCompat.checkSelfPermission(context!!, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED + return hasPermission } protected fun checkPermissions(permissions: ArrayList = ArrayList()) { diff --git a/packages/mobile-sdk-alpha/src/components/MRZScannerView.tsx b/packages/mobile-sdk-alpha/src/components/MRZScannerView.tsx index 7743d3f6d..296e48c6b 100644 --- a/packages/mobile-sdk-alpha/src/components/MRZScannerView.tsx +++ b/packages/mobile-sdk-alpha/src/components/MRZScannerView.tsx @@ -125,7 +125,7 @@ export const MRZScannerView: React.FC = ({ Date: Wed, 10 Sep 2025 03:13:06 +0530 Subject: [PATCH 2/6] Fix: missing res values --- .../android/src/main/res/values/styles.xml | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/packages/mobile-sdk-alpha/android/src/main/res/values/styles.xml b/packages/mobile-sdk-alpha/android/src/main/res/values/styles.xml index 757538937..a5e7b102d 100644 --- a/packages/mobile-sdk-alpha/android/src/main/res/values/styles.xml +++ b/packages/mobile-sdk-alpha/android/src/main/res/values/styles.xml @@ -21,75 +21,4 @@ - - - - - - - - - - - - - - - - - - - - From 3c51d6e343c930e1813d69791443b9a68221618d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Seshanth=2ES=F0=9F=90=BA?= <35675963+seshanthS@users.noreply.github.com> Date: Tue, 16 Sep 2025 21:07:34 +0530 Subject: [PATCH 3/6] Add qrcode NativeModule to msdk (#1054) * add qrcode module * fix test * fix test: fetchOfacTrees --- common/index.ts | 25 +- .../mobile-sdk-alpha/android/build.gradle | 5 + .../selfSDK/RNSelfPassportReaderPackage.kt | 8 +- .../selfxyz/selfSDK/SelfQRScannerModule.kt | 111 ++++++ .../selfSDK/SelfQRScannerViewManager.kt | 178 +++++++++ .../selfSDK/ui/QrCodeScannerFragment.kt | 346 ++++++++++++++++++ .../selfSDK/utils/QrCodeDetectorProcessor.kt | 156 ++++++++ .../ios/SelfSDK/SelfQRScannerModule.m | 12 + .../ios/SelfSDK/SelfQRScannerModule.swift | 60 +++ .../ios/SelfSDK/SelfQRScannerViewManager.m | 12 + .../SelfSDK/SelfQRScannerViewManager.swift | 100 +++++ .../src/adapters/react-native/scanner.ts | 30 +- .../src/components/QRCodeScannerView.tsx | 96 +++++ packages/mobile-sdk-alpha/src/index.ts | 2 + 14 files changed, 1123 insertions(+), 18 deletions(-) create mode 100644 packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt create mode 100644 packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerViewManager.kt create mode 100644 packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/QrCodeScannerFragment.kt create mode 100644 packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/utils/QrCodeDetectorProcessor.kt create mode 100644 packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.m create mode 100644 packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.swift create mode 100644 packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.m create mode 100644 packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift create mode 100644 packages/mobile-sdk-alpha/src/components/QRCodeScannerView.tsx diff --git a/common/index.ts b/common/index.ts index e24d5620b..add454242 100644 --- a/common/index.ts +++ b/common/index.ts @@ -55,10 +55,14 @@ export { SelfAppBuilder, bigIntToString, brutforceSignatureAlgorithmDsc, + buildSMT, + calculateUserIdentifierHash, findStartPubKeyIndex, formatEndpoint, formatMrz, genAndInitMockPassportData, + genMockIdDoc, + genMockIdDocAndInitDataParsing, generateCircuitInputsDSC, generateCircuitInputsRegister, generateCircuitInputsVCandDisclose, @@ -69,28 +73,17 @@ export { getLeafCscaTree, getLeafDscTree, getSKIPEM, + getSolidityPackedUserContextData, getUniversalLink, hashEndpointWithScope, initElliptic, initPassportDataParsing, parseCertificateSimple, parseDscCertificateData, - genMockIdDoc, - genMockIdDocAndInitDataParsing, - buildSMT, - calculateUserIdentifierHash, - getSolidityPackedUserContextData, stringToBigInt, } from './src/utils/index.js'; -export { - prepareAadhaarRegisterTestData, - prepareAadhaarDiscloseTestData, - prepareAadhaarRegisterData, -} from './src/utils/aadhaar/mockData.js'; -export { generateTestData, testCustomData } from './src/utils/aadhaar/utils.js'; export { createSelector } from './src/utils/aadhaar/constants.js'; - // Hash utilities export { customHasher, @@ -99,3 +92,11 @@ export { hash, packBytesAndPoseidon, } from './src/utils/hash.js'; + +export { generateTestData, testCustomData } from './src/utils/aadhaar/utils.js'; + +export { + prepareAadhaarDiscloseTestData, + prepareAadhaarRegisterData, + prepareAadhaarRegisterTestData, +} from './src/utils/aadhaar/mockData.js'; diff --git a/packages/mobile-sdk-alpha/android/build.gradle b/packages/mobile-sdk-alpha/android/build.gradle index d4694e228..72873862f 100644 --- a/packages/mobile-sdk-alpha/android/build.gradle +++ b/packages/mobile-sdk-alpha/android/build.gradle @@ -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' } } @@ -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' } diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/RNSelfPassportReaderPackage.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/RNSelfPassportReaderPackage.kt index b34874562..d73485783 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/RNSelfPassportReaderPackage.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/RNSelfPassportReaderPackage.kt @@ -25,11 +25,15 @@ class RNSelfPassportReaderPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { return listOf( RNSelfPassportReaderModule(reactContext), - SelfMRZScannerModule(reactContext) + SelfMRZScannerModule(reactContext), + SelfQRScannerModule(reactContext) ) } override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return listOf(SelfOCRViewManager(reactContext)) + return listOf( + SelfOCRViewManager(reactContext), + SelfQRScannerViewManager(reactContext) + ) } } diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt new file mode 100644 index 000000000..dc968afea --- /dev/null +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +package com.selfxyz.selfSDK + +import androidx.fragment.app.FragmentActivity +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import android.view.ViewGroup +import android.view.View +import android.widget.FrameLayout +import com.selfxyz.selfSDK.ui.QrCodeScannerFragment + +class SelfQRScannerModule(reactContext: ReactApplicationContext) : +ReactContextBaseJavaModule(reactContext), QrCodeScannerFragment.QRCodeScannerCallback { + override fun getName() = "SelfQRScannerModule" + + private var scanPromise: Promise? = null + private var currentContainer: FrameLayout? = null + private var currentFragment: QrCodeScannerFragment? = null + + @ReactMethod + fun startScanning(promise: Promise) { + scanPromise = promise + val activity = reactApplicationContext.currentActivity as? FragmentActivity + if (activity == null) { + promise.reject("E_NO_ACTIVITY", "No FragmentActivity found") + return + } + + 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( + 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 = QrCodeScannerFragment(this@SelfQRScannerModule) + + // Store references for cleanup + currentContainer = container + currentFragment = fragment + + activity.supportFragmentManager + .beginTransaction() + .replace(containerId, fragment) + .commitNow() + + } catch (e: Exception) { + android.util.Log.e("SelfQRScannerModule", "Error in startScanning", e) + promise.reject("E_SCANNING_ERROR", e.message, e) + } + } + } + + override fun onQRData(data: String) { + scanPromise?.resolve(data) + scanPromise = null + cleanup() + } + + override fun onError(e: Exception) { + scanPromise?.reject("E_QR_SCAN_ERROR", e.message, e) + scanPromise = null + cleanup() + } + + private fun cleanup() { + val activity = reactApplicationContext.currentActivity as? FragmentActivity + activity?.runOnUiThread { + currentFragment?.let { fragment -> + activity.supportFragmentManager + .beginTransaction() + .remove(fragment) + .commit() + } + currentContainer?.let { container -> + (container.parent as? ViewGroup)?.removeView(container) + } + currentContainer = null + currentFragment = null + } + } + + private fun generateUnusedId(parent: ViewGroup): Int { + var id = View.generateViewId() + while (parent.findViewById(id) != null) { + id = View.generateViewId() + } + return id + } +} diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerViewManager.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerViewManager.kt new file mode 100644 index 000000000..2dfe466b8 --- /dev/null +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerViewManager.kt @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +package com.selfxyz.selfSDK + +import android.view.Choreographer +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.fragment.app.FragmentActivity +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.annotations.ReactPropGroup +import com.facebook.react.uimanager.events.RCTEventEmitter +import com.selfxyz.selfSDK.ui.QrCodeScannerFragment + +class SelfQRScannerViewManager( + open val reactContext: ReactApplicationContext +) : ViewGroupManager(), QrCodeScannerFragment.QRCodeScannerCallback { + private var propWidth: Int? = null + private var propHeight: Int? = null + private var reactNativeViewId: Int? = null + + override fun getName() = REACT_CLASS + + /** + * Return a FrameLayout which will later hold the Fragment + */ + override fun createViewInstance(reactContext: ThemedReactContext) = + FrameLayout(reactContext) + + /** + * Map the "create" command to an integer + */ + override fun getCommandsMap() = mapOf( + "create" to COMMAND_CREATE, + "destroy" to COMMAND_DESTROY + ) + + /** + * Handle "create" command (called from JS) and call createFragment method + */ + override fun receiveCommand( + root: FrameLayout, + commandId: String, + 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) + } + } + + override fun receiveCommand( + root: FrameLayout, + commandId: Int, + args: ReadableArray? + ) { + super.receiveCommand(root, commandId, args) + val reactNativeViewId = requireNotNull(args).getInt(0) + + when (commandId) { + COMMAND_CREATE -> createFragment(root, reactNativeViewId) + COMMAND_DESTROY -> destroyFragment(root, reactNativeViewId) + } + } + + @ReactPropGroup(names = ["width", "height"], customType = "Style") + fun setStyle(view: FrameLayout, index: Int, value: Int) { + if (index == 0) propWidth = value + if (index == 1) propHeight = value + } + + /** + * Replace your React Native view with a custom fragment + */ + private fun createFragment(root: FrameLayout, reactNativeViewId: Int) { + this.reactNativeViewId = reactNativeViewId + val parentView = root.findViewById(reactNativeViewId) + setupLayout(parentView) + + val qrScannerFragment = QrCodeScannerFragment(this) + val activity = reactContext.currentActivity as FragmentActivity + activity.supportFragmentManager + .beginTransaction() + .replace(reactNativeViewId, qrScannerFragment, reactNativeViewId.toString()) + .commit() + } + + private fun destroyFragment(root: FrameLayout, reactNativeViewId: Int) { + val parentView = root.findViewById(reactNativeViewId) + setupLayout(parentView) + + val activity = reactContext.currentActivity as FragmentActivity + val qrScannerFragment = activity.supportFragmentManager.findFragmentByTag(reactNativeViewId.toString()) + qrScannerFragment?.let { + activity.supportFragmentManager + .beginTransaction() + .remove(it) + .commit() + } + } + + + private fun setupLayout(view: View) { + Choreographer.getInstance().postFrameCallback(object: Choreographer.FrameCallback { + override fun doFrame(frameTimeNanos: Long) { + manuallyLayoutChildren(view) + view.viewTreeObserver.dispatchOnGlobalLayout() + Choreographer.getInstance().postFrameCallback(this) + } + }) + } + + /** + * Layout all children properly + */ + private fun manuallyLayoutChildren(view: View) { + // propWidth and propHeight coming from react-native props + val width = requireNotNull(propWidth) + val height = requireNotNull(propHeight) + + view.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)) + + view.layout(0, 0, width, height) + } + + companion object { + private const val REACT_CLASS = "SelfQRScannerViewManager" + private const val COMMAND_CREATE = 1 + private const val COMMAND_DESTROY = 2 + private const val SUCCESS_EVENT = "onQRCodeReadResult" + private const val FAILURE_EVENT = "onQRCodeReadError" + } + + + override fun onQRData(data: String) { + val event = Arguments.createMap() + event.putString("data", data) + reactContext + .getJSModule(RCTEventEmitter::class.java) + .receiveEvent(this.reactNativeViewId!!, SUCCESS_EVENT, event) + } + + override fun onError(e: Exception) { + val event = Arguments.createMap() + event.putString("errorMessage", "Something went wrong scanning the QR Code") + event.putString("error", e.toString()) + event.putString("stackTrace", e.stackTraceToString()) + reactContext + .getJSModule(RCTEventEmitter::class.java) + .receiveEvent(this.reactNativeViewId!!, FAILURE_EVENT, event) + } + + override fun getExportedCustomBubblingEventTypeConstants(): Map { + return mapOf( + SUCCESS_EVENT to mapOf( + "phasedRegistrationNames" to mapOf( + "bubbled" to "onQRData" + ) + ), + FAILURE_EVENT to mapOf( + "phasedRegistrationNames" to mapOf( + "bubbled" to "onError" + ) + ) + ) + } +} diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/QrCodeScannerFragment.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/QrCodeScannerFragment.kt new file mode 100644 index 000000000..35018aa5c --- /dev/null +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/ui/QrCodeScannerFragment.kt @@ -0,0 +1,346 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.selfxyz.selfSDK.ui + + +import android.Manifest +import android.app.AlertDialog +import android.app.Dialog +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.selfxyz.selfSDK.utils.QrCodeDetectorProcessor +import com.selfxyz.selfSDK.mlkit.FrameMetadata +import com.selfxyz.selfSDK.ui.CameraFragment +import com.selfxyz.selfSDK.utils.MRZUtil +import com.selfxyz.selfSDK.databinding.FragmentCameraMrzBinding +import io.fotoapparat.preview.Frame +import io.fotoapparat.view.CameraView +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers + + +class QrCodeScannerFragment(callback: QRCodeScannerCallback) : CameraFragment() { + private var callback: QRCodeScannerCallback? = callback + private var frameProcessor: QrCodeDetectorProcessor? = null + private val mHandler = Handler(Looper.getMainLooper()) + var disposable = CompositeDisposable() + + private var isDecoding = false + + private var binding:FragmentCameraMrzBinding?=null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + binding = FragmentCameraMrzBinding.inflate(inflater, container, false) + return binding?.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + + override fun onResume() { + MRZUtil.cleanStorage() + frameProcessor = qrProcessor + super.onResume() + } + + + + override fun onPause() { + frameProcessor?.stop() + frameProcessor = null + + super.onPause() + } + + override fun onDestroyView() { + if (!disposable.isDisposed) { + disposable.dispose(); + } + super.onDestroyView() + } + + override fun onDetach() { + callback = null + super.onDetach() + + } + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Events from camera fragment + // + //////////////////////////////////////////////////////////////////////////////////////// + + + override val callbackFrameProcessor: io.fotoapparat.preview.FrameProcessor + get() { + val callbackFrameProcessor2 = object : io.fotoapparat.preview.FrameProcessor { + override fun process(frame: Frame) { + try { + if (!isDecoding) { + isDecoding = true + + if (frameProcessor != null) { + val subscribe = Single.fromCallable({ + frameProcessor?.process( + frame = frame, + rotation = rotation, + true, + listener = qrListener + ) + }).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ _ -> + //Don't do anything + },{error-> + isDecoding = false + Toast.makeText(requireContext(), "Error: "+error, Toast.LENGTH_SHORT).show() + }) + disposable.add(subscribe) + } + } + }catch (e:Exception){ + e.printStackTrace() + } + + } + } + return callbackFrameProcessor2 + + } + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Get camera preview + // + //////////////////////////////////////////////////////////////////////////////////////// + + override val cameraPreview: CameraView + get(){ + return binding?.cameraPreview!! + } + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Permission requested + // + //////////////////////////////////////////////////////////////////////////////////////// + + override val requestedPermissions: ArrayList + get() { + //Nothing as we don't need any other permission than camera and that's managed in the parent fragment + return ArrayList() + } + + override fun onRequestPermissionsResult(permissionsDenied: ArrayList, permissionsGranted: ArrayList) { + } + + + val qrListener = object : QrCodeDetectorProcessor.Listener { + override fun onSuccess( + results: String, + frameMetadata: FrameMetadata?, + timeRequired: Long, + bitmap: Bitmap? + ) { + isDecoding = false + if (!isAdded) { + return + } + mHandler.post { + try { + binding?.statusViewBottom?.setTextColor(resources.getColor(android.R.color.white)) + callback.onQRData(results) + frameProcessor?.stop() + + } catch (e: IllegalStateException) { + //The fragment is destroyed + } + } + } + +// override fun onCanceled(timeRequired: Long) { +// isDecoding = false +// if (!isAdded) { +// return +// } +// } + + override fun onFailure( + e: Exception, + timeRequired: Long + ) { + isDecoding = false + if (!isAdded) { + return + } + e.printStackTrace() + mHandler.post { + callback.onError(e) + } + } + + override fun onCompletedFrame(timeRequired: Long) { + isDecoding = false + if (!isAdded) { + return + } + } + + } + + protected val qrProcessor: QrCodeDetectorProcessor + get() = QrCodeDetectorProcessor() + + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Permissions + // + //////////////////////////////////////////////////////////////////////////////////////// + + private fun requestCameraPermission() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + ConfirmationDialog().show(childFragmentManager, FRAGMENT_DIALOG) + } else { + requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, + grantResults: IntArray) { + if (requestCode == REQUEST_CAMERA_PERMISSION) { + if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { + ErrorDialog.newInstance("Camera permission is required to scan QR codes") + .show(childFragmentManager, FRAGMENT_DIALOG) + } + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Dialogs UI + // + //////////////////////////////////////////////////////////////////////////////////////// + + /** + * Shows a [Toast] on the UI thread. + * + * @param text The message to show + */ + private fun showToast(text: String) { + val activity = activity + activity?.runOnUiThread { Toast.makeText(activity, text, Toast.LENGTH_SHORT).show() } + } + + /** + * Shows an error message dialog. + */ + class ErrorDialog : androidx.fragment.app.DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val activity = activity + return AlertDialog.Builder(activity) + .setMessage(requireArguments().getString(ARG_MESSAGE)) + .setPositiveButton(android.R.string.ok) { dialogInterface, i -> activity!!.finish() } + .create() + } + + companion object { + + private val ARG_MESSAGE = "message" + + fun newInstance(message: String): ErrorDialog { + val dialog = ErrorDialog() + val args = Bundle() + args.putString(ARG_MESSAGE, message) + dialog.arguments = args + return dialog + } + } + + } + + /** + * Shows OK/Cancel confirmation dialog about camera permission. + */ + class ConfirmationDialog : androidx.fragment.app.DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val parent = parentFragment + return AlertDialog.Builder(activity) + .setMessage("Camera permission is required to scan QR codes") + .setPositiveButton(android.R.string.ok) { dialog, which -> + parent!!.requestPermissions(arrayOf(Manifest.permission.CAMERA), + REQUEST_CAMERA_PERMISSION + ) + } + .setNegativeButton(android.R.string.cancel + ) { dialog, which -> + val activity = parent!!.activity + activity?.finish() + } + .create() + } + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Listener + // + //////////////////////////////////////////////////////////////////////////////////////// + + interface QRCodeScannerCallback { + fun onQRData(data: String) + fun onError(e: Exception) + } + + companion object { + + /** + * Tag for the [Log]. + */ + private val TAG = QrCodeScannerFragment::class.java.simpleName + + private val REQUEST_CAMERA_PERMISSION = 1 + private val FRAGMENT_DIALOG = "QRCodeScannerFragment" + } + + +} diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/utils/QrCodeDetectorProcessor.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/utils/QrCodeDetectorProcessor.kt new file mode 100644 index 000000000..e8b571e27 --- /dev/null +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/utils/QrCodeDetectorProcessor.kt @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.selfxyz.selfSDK.utils + + +import android.graphics.Bitmap +import android.os.AsyncTask +import android.webkit.URLUtil +import com.google.mlkit.vision.common.InputImage +import com.google.zxing.BinaryBitmap +import com.google.zxing.LuminanceSource +import com.google.zxing.RGBLuminanceSource +import com.google.zxing.Result +import com.google.zxing.common.HybridBinarizer +import com.google.zxing.qrcode.QRCodeReader +import com.selfxyz.selfSDK.mlkit.FrameMetadata +import com.selfxyz.selfSDK.utils.ImageUtil +import io.fotoapparat.preview.Frame +import java.nio.ByteBuffer +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicBoolean + + +class QrCodeDetectorProcessor { + + // Whether we should ignore process(). This is usually caused by feeding input data faster than + // the model can handle. + private val shouldThrottle = AtomicBoolean(false) + var executor: ExecutorService = Executors.newSingleThreadExecutor() + + fun canHandleNewFrame():Boolean{ + return !shouldThrottle.get() + } + + fun resetThrottle(){ + shouldThrottle.set(false) + } + + fun process( + frame: Frame, + rotation:Int, + isOriginalImageReturned:Boolean, + listener: Listener + ):Boolean { + if (shouldThrottle.get()) { + return false + } + shouldThrottle.set(true) + try{ + val frameMetadata = FrameMetadata.Builder() + .setWidth(frame.size.width) + .setHeight(frame.size.height) + .setRotation(rotation).build() + val inputImage = InputImage.fromByteArray(frame.image, + frameMetadata.width, + frameMetadata.height, + rotation, + InputImage.IMAGE_FORMAT_NV21 + ) + + var originalBitmap:Bitmap?=null + try { + originalBitmap = inputImage.bitmapInternal + if (originalBitmap == null) { + val wrap = ByteBuffer.wrap(frame.image) + originalBitmap = ImageUtil.rotateBitmap(ImageUtil.getBitmap(wrap, frameMetadata)!!, frameMetadata.rotation.toFloat()) + } + }catch (e:Exception){ + e.printStackTrace() + } + + return detectQrCodeInImage(originalBitmap!!, frameMetadata, if(isOriginalImageReturned) originalBitmap else null, listener) + }catch (e:Exception){ + e.printStackTrace() + shouldThrottle.set(false) + return false + } + } + + private fun detectQrCodeInImage( + image: Bitmap, + metadata: FrameMetadata?, + originalBitmap: Bitmap?=null, + listener: Listener + ): Boolean { + val start = System.currentTimeMillis() + executor.execute { + val result = detectInImage(image) + val timeRequired = System.currentTimeMillis() - start + println(result) + if (result != null) { + if (URLUtil.isValidUrl(result.text)) { + listener.onSuccess(result.text!!, metadata, timeRequired, originalBitmap) + } else { + listener.onFailure(Exception("Invalid URL"), timeRequired) + } + } + else { + listener.onCompletedFrame(timeRequired) + } + shouldThrottle.set(false) + } + + return true + } + + private fun detectInImage(bitmap: Bitmap): Result? { + val qRCodeDetectorReader = QRCodeReader() + val intArray = IntArray(bitmap.width * bitmap.height) + bitmap.getPixels(intArray, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height) + + val source: LuminanceSource = + RGBLuminanceSource(bitmap.width, bitmap.height, intArray) + + val binaryBitMap = BinaryBitmap(HybridBinarizer(source)) + + try { + return qRCodeDetectorReader.decode(binaryBitMap) + } + catch (e: Exception) { + // noop + println(e) + } + return null + } + + fun stop() { + } + + + interface Listener { + fun onSuccess(results: String, frameMetadata: FrameMetadata?, timeRequired: Long, bitmap: Bitmap?) + fun onFailure(e: Exception, timeRequired:Long) + fun onCompletedFrame(timeRequired: Long) + } + + companion object { + private val TAG = QrCodeDetectorProcessor::class.java.simpleName + } +} diff --git a/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.m b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.m new file mode 100644 index 000000000..c0405e97c --- /dev/null +++ b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.m @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +#import + +@interface RCT_EXTERN_MODULE(SelfQRScannerModule, NSObject) + +RCT_EXTERN_METHOD(startScanning:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) + +@end diff --git a/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.swift b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.swift new file mode 100644 index 000000000..49de220c6 --- /dev/null +++ b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerModule.swift @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +// +// SelfQRScannerModule.swift +// SelfSDK +// + +import Foundation +import React +import UIKit +import AVFoundation + +@objc(SelfQRScannerModule) +class SelfQRScannerModule: NSObject, RCTBridgeModule { + static func moduleName() -> String! { + return "SelfQRScannerModule" + } + + static func requiresMainQueueSetup() -> Bool { + return true + } + + @objc func startScanning(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { + DispatchQueue.main.async { + guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else { + reject("error", "Unable to find root view controller", nil) + return + } + + let scannerView = SelfQRScannerView(frame: rootViewController.view.bounds) + + scannerView.onQRData = { resultDict in + if let dict = resultDict, let data = dict["data"] as? String { + resolve(data) + } else { + reject("error", "Invalid QR code data", nil) + } + } + + scannerView.onError = { errorDict in + if let dict = errorDict { + let errorMessage = dict["errorMessage"] as? String ?? "QR scanning failed" + reject("error", errorMessage, nil) + } else { + reject("error", "QR scanning failed", nil) + } + } + + let modalViewController = UIViewController() + modalViewController.view = scannerView + modalViewController.modalPresentationStyle = .fullScreen + rootViewController.present(modalViewController, animated: true, completion: nil) + } + } +} + + +//TODO - check diff --git a/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.m b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.m new file mode 100644 index 000000000..f49ad559c --- /dev/null +++ b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.m @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +#import + +@interface RCT_EXTERN_MODULE(SelfQRScannerViewManager, RCTViewManager) + +RCT_EXPORT_VIEW_PROPERTY(onQRData, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock) + +@end diff --git a/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift new file mode 100644 index 000000000..e8fee7b5c --- /dev/null +++ b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +// +// SelfQRScannerViewManager.swift +// SelfSDK +// +// Created by Rémi Colin on 07/02/2025. +// + +import AVFoundation +import Foundation +import React + +@objc(SelfQRScannerViewManager) +class SelfQRScannerViewManager: RCTViewManager { + override static func requiresMainQueueSetup() -> Bool { + return true + } + + override func view() -> UIView! { + return SelfQRScannerView() + } + + override static func moduleName() -> String! { + return "SelfQRScannerView" + } +} + +class SelfQRScannerView: UIView, AVCaptureMetadataOutputObjectsDelegate { + var captureSession: AVCaptureSession? + var previewLayer: AVCaptureVideoPreviewLayer? + + // This property will hold the callback from JS + @objc var onQRData: RCTDirectEventBlock? + @objc var onError: RCTDirectEventBlock? + + override init(frame: CGRect) { + super.init(frame: frame) + initializeScanner() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + initializeScanner() + } + + func initializeScanner() { + captureSession = AVCaptureSession() + guard let videoCaptureDevice = AVCaptureDevice.default(for: .video), + let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice), + captureSession!.canAddInput(videoInput) + else { + onError?(["errorMessage": "Camera not available", "error": "CAMERA_NOT_AVAILABLE", "stackTrace": ""]) + return + } + captureSession!.addInput(videoInput) + + let metadataOutput = AVCaptureMetadataOutput() + if captureSession!.canAddOutput(metadataOutput) { + captureSession!.addOutput(metadataOutput) + metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + metadataOutput.metadataObjectTypes = [.qr] + } else { + onError?(["errorMessage": "QR code scanning not supported", "error": "QR_NOT_SUPPORTED", "stackTrace": ""]) + return + } + + previewLayer = AVCaptureVideoPreviewLayer(session: captureSession!) + previewLayer?.videoGravity = .resizeAspectFill + previewLayer?.frame = self.layer.bounds + if let previewLayer = previewLayer { + self.layer.addSublayer(previewLayer) + } + + DispatchQueue.global(qos: .background).async { + self.captureSession!.startRunning() + } + } + + func metadataOutput( + _ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], + from connection: AVCaptureConnection + ) { + if let metadataObject = metadataObjects.first, + let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject, + let stringValue = readableObject.stringValue + { + // Send the scanned QR code data to JS + onQRData?(["data": stringValue]) + captureSession?.stopRunning() + } + } + + override func layoutSubviews() { + super.layoutSubviews() + previewLayer?.frame = self.bounds + } +} diff --git a/packages/mobile-sdk-alpha/src/adapters/react-native/scanner.ts b/packages/mobile-sdk-alpha/src/adapters/react-native/scanner.ts index 73726ef72..0043d3639 100644 --- a/packages/mobile-sdk-alpha/src/adapters/react-native/scanner.ts +++ b/packages/mobile-sdk-alpha/src/adapters/react-native/scanner.ts @@ -21,7 +21,7 @@ export const reactNativeScannerAdapter: ScannerAdapter = { }; async function scanIOS(opts: ScanOpts): Promise { - const { SelfMRZScannerModule, PassportReader } = NativeModules; + const { SelfMRZScannerModule, SelfQRScannerModule, PassportReader } = NativeModules; switch (opts.mode) { case 'mrz': @@ -119,7 +119,18 @@ async function scanIOS(opts: ScanOpts): Promise { } case 'qr': - throw new Error('QR scanning not implemented yet'); + if (!SelfQRScannerModule) { + throw new Error('SelfQRScannerModule not found, check if its linked correctly'); + } + try { + const result = await SelfQRScannerModule.startScanning(); + return { + mode: 'qr', + data: result, + }; + } catch (error) { + throw new Error(`QR scanning failed: ${error}`); + } default: throw new Error(`Unsupported scan mode`); @@ -127,7 +138,7 @@ async function scanIOS(opts: ScanOpts): Promise { } async function scanAndroid(opts: ScanOpts): Promise { - const { SelfPassportReader: PassportReader, SelfMRZScannerModule } = NativeModules; + const { SelfPassportReader: PassportReader, SelfMRZScannerModule, SelfQRScannerModule } = NativeModules; if (opts.mode === 'nfc' && !PassportReader) { throw new Error('PassportReader not found, check if its linked correctly'); } @@ -209,7 +220,18 @@ async function scanAndroid(opts: ScanOpts): Promise { } case 'qr': - throw new Error('QR scanning not implemented yet'); + if (!SelfQRScannerModule) { + throw new Error('SelfQRScannerModule not found, check if its linked correctly'); + } + try { + const result = await SelfQRScannerModule.startScanning(); + return { + mode: 'qr', + data: result, + }; + } catch (error) { + throw new Error(`QR scanning failed: ${error}`); + } default: throw new Error(`Unsupported scan mode`); diff --git a/packages/mobile-sdk-alpha/src/components/QRCodeScannerView.tsx b/packages/mobile-sdk-alpha/src/components/QRCodeScannerView.tsx new file mode 100644 index 000000000..8e1aea286 --- /dev/null +++ b/packages/mobile-sdk-alpha/src/components/QRCodeScannerView.tsx @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc. +// SPDX-License-Identifier: BUSL-1.1 +// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. + +import React, { useCallback } from 'react'; +import type { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native'; +import { PixelRatio, Platform, requireNativeComponent } from 'react-native'; + +import { RCTFragment } from './RCTFragment'; + +interface NativeQRCodeScannerViewProps { + onQRData: (event: NativeSyntheticEvent<{ data: string }>) => void; + onError: ( + event: NativeSyntheticEvent<{ + error: string; + errorMessage: string; + stackTrace: string; + }>, + ) => void; + style?: StyleProp; +} + +const QRCodeNativeComponent = Platform.select({ + ios: requireNativeComponent('SelfQRScannerView'), + android: requireNativeComponent('SelfQRScannerViewManager'), +}); + +export interface QRCodeScannerViewProps { + isMounted: boolean; + onQRData: (error: Error | null, uri?: string) => void; +} + +export const QRCodeScannerView: React.FC = ({ onQRData, isMounted }) => { + const _onError = useCallback( + ( + event: NativeSyntheticEvent<{ + error: string; + errorMessage: string; + stackTrace: string; + }>, + ) => { + if (!isMounted) { + return; + } + /* eslint-disable @typescript-eslint/no-unused-vars */ + const { error, errorMessage, stackTrace } = event.nativeEvent; + const e = new Error(errorMessage); + e.stack = stackTrace; + onQRData(e); + }, + [onQRData, isMounted], + ); + + const _onQRData = useCallback( + (event: NativeSyntheticEvent<{ data: string }>) => { + if (!isMounted) { + return; + } + onQRData(null, event.nativeEvent.data); + }, + [onQRData, isMounted], + ); + + if (!QRCodeNativeComponent) { + console.error('SelfQRScannerView not registered for this platform'); + return; + } + + if (Platform.OS === 'ios') { + return ( + + ); + } else { + const Fragment = RCTFragment as React.FC & NativeQRCodeScannerViewProps>; + return ( + } + fragmentComponentName="SelfQRScannerViewManager" + isMounted={isMounted} + style={{ + height: PixelRatio.getPixelSizeForLayoutSize(800), + width: PixelRatio.getPixelSizeForLayoutSize(400), + }} + onError={_onError} + onQRData={_onQRData} + /> + ); + } +}; diff --git a/packages/mobile-sdk-alpha/src/index.ts b/packages/mobile-sdk-alpha/src/index.ts index cfd13c70f..f165cbfc5 100644 --- a/packages/mobile-sdk-alpha/src/index.ts +++ b/packages/mobile-sdk-alpha/src/index.ts @@ -67,6 +67,8 @@ export { NFCScannerScreen } from './components/screens/NFCScannerScreen'; // Screen Components export { PassportCameraScreen } from './components/screens/PassportCameraScreen'; +export { QRCodeScannerView } from './components/QRCodeScannerView'; + export { QRCodeScreen } from './components/screens/QRCodeScreen'; export { SdkEvents } from './types/events'; From fe49618a660d05afbd740423ae9e16d1ed06dd01 Mon Sep 17 00:00:00 2001 From: seshanthS Date: Wed, 17 Sep 2025 13:10:11 +0530 Subject: [PATCH 4/6] update review comments --- .../selfxyz/selfSDK/SelfMRZScannerModule.kt | 25 ++++++++++++------- .../com/selfxyz/selfSDK/SelfOCRViewManager.kt | 18 +++++++++++-- .../selfxyz/selfSDK/SelfQRScannerModule.kt | 12 ++++++++- .../SelfSDK/SelfQRScannerViewManager.swift | 17 ++++++++++--- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt index e84cc7513..8a8a8f338 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfMRZScannerModule.kt @@ -27,12 +27,16 @@ ReactContextBaseJavaModule(reactContext), CameraMLKitFragment.CameraMLKitCallbac @ReactMethod fun startScanning(promise: Promise) { - scanPromise = promise 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 { @@ -70,6 +74,7 @@ ReactContextBaseJavaModule(reactContext), CameraMLKitFragment.CameraMLKitCallbac } catch (e: Exception) { android.util.Log.e("SelfMRZScannerModule", "Error in startScanning", e) promise.reject("E_SCANNING_ERROR", e.message, e) + cleanup() } } } @@ -94,24 +99,26 @@ ReactContextBaseJavaModule(reactContext), CameraMLKitFragment.CameraMLKitCallbac private fun cleanup() { val activity = reactApplicationContext.currentActivity as? FragmentActivity - if (activity != null && currentFragment != null && currentContainer != null) { + 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(currentFragment!!) - .commitNow() + .remove(fragment) + .commitAllowingStateLoss() - val parent = currentContainer!!.parent as? ViewGroup - parent?.removeView(currentContainer) + (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) } - - currentFragment = null - currentContainer = null } } } diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt index cfc066813..57c37038a 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfOCRViewManager.kt @@ -44,7 +44,12 @@ class SelfOCRViewManager( args: ReadableArray? ) { super.receiveCommand(root, commandId, args) - val reactNativeViewId = requireNotNull(args).getInt(0) + + 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) { "create" -> { @@ -66,7 +71,12 @@ class SelfOCRViewManager( args: ReadableArray? ) { super.receiveCommand(root, commandId, args) - val reactNativeViewId = requireNotNull(args).getInt(0) + + 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 -> { @@ -100,6 +110,10 @@ class SelfOCRViewManager( private fun createFragment(root: FrameLayout, reactNativeViewId: Int) { this.reactNativeViewId = reactNativeViewId val parentView = root.findViewById(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 diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt index dc968afea..b69a01498 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt @@ -63,7 +63,17 @@ ReactContextBaseJavaModule(reactContext), QrCodeScannerFragment.QRCodeScannerCal activity.supportFragmentManager .beginTransaction() .replace(containerId, fragment) - .commitNow() + // .commitNow() + .let { transaction -> + if (activity.supportFragmentManager.isStateSaved) { + // State is saved, use safe commit + transaction.commitAllowingStateLoss() + android.util.Log.w("SelfQRScannerModule", "Fragment committed with state loss due to saved state") + } else { + // State is not saved, safe to use immediate commit + transaction.commitNow() + } + } } catch (e: Exception) { android.util.Log.e("SelfQRScannerModule", "Error in startScanning", e) diff --git a/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift index e8fee7b5c..75b70d505 100644 --- a/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift +++ b/packages/mobile-sdk-alpha/ios/SelfSDK/SelfQRScannerViewManager.swift @@ -38,12 +38,17 @@ class SelfQRScannerView: UIView, AVCaptureMetadataOutputObjectsDelegate { override init(frame: CGRect) { super.init(frame: frame) - initializeScanner() } required init?(coder: NSCoder) { super.init(coder: coder) - initializeScanner() + } + + override func didMoveToWindow() { + super.didMoveToWindow() + if window != nil && captureSession == nil { + initializeScanner() + } } func initializeScanner() { @@ -89,7 +94,13 @@ class SelfQRScannerView: UIView, AVCaptureMetadataOutputObjectsDelegate { { // Send the scanned QR code data to JS onQRData?(["data": stringValue]) - captureSession?.stopRunning() + // captureSession?.stopRunning() + if let session = captureSession { + DispatchQueue.global(qos: .background).async { + session.stopRunning() + } + } + } } From 81d890bf9d6d387642c34008d59893efa62114a3 Mon Sep 17 00:00:00 2001 From: seshanthS Date: Mon, 22 Sep 2025 16:16:01 +0530 Subject: [PATCH 5/6] improve cleanup() --- .../selfxyz/selfSDK/SelfQRScannerModule.kt | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt index b69a01498..1e6da8625 100644 --- a/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt +++ b/packages/mobile-sdk-alpha/android/src/main/java/com/selfxyz/selfSDK/SelfQRScannerModule.kt @@ -96,18 +96,22 @@ ReactContextBaseJavaModule(reactContext), QrCodeScannerFragment.QRCodeScannerCal private fun cleanup() { val activity = reactApplicationContext.currentActivity as? FragmentActivity + val fragment = currentFragment + val container = currentContainer + currentContainer = null + currentFragment = null activity?.runOnUiThread { - currentFragment?.let { fragment -> - activity.supportFragmentManager - .beginTransaction() - .remove(fragment) - .commit() - } - currentContainer?.let { container -> - (container.parent as? ViewGroup)?.removeView(container) + try { + fragment?.let { + activity.supportFragmentManager + .beginTransaction() + .remove(it) + .commitAllowingStateLoss() + } + container?.let { (it.parent as? ViewGroup)?.removeView(it) } + } catch (e: Exception) { + android.util.Log.e("SelfQRScannerModule", "Error during cleanup", e) } - currentContainer = null - currentFragment = null } } From 8b28f2863c026230e7d93cee419e7054618faaa6 Mon Sep 17 00:00:00 2001 From: seshanthS Date: Mon, 22 Sep 2025 20:57:12 +0530 Subject: [PATCH 6/6] yarn nice --- app/ios/Podfile.lock | 2 +- app/ios/Self.xcodeproj/project.pbxproj | 10 +- common/index.ts | 2 - common/src/constants/constants.ts | 4 +- common/src/utils/aadhaar/mockData.ts | 104 ++++++++++---------- common/src/utils/circuits/generateInputs.ts | 8 +- common/src/utils/circuits/registerInputs.ts | 3 +- common/src/utils/index.ts | 2 +- common/src/utils/passports/genMockIdDoc.ts | 8 +- 9 files changed, 73 insertions(+), 70 deletions(-) diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index deb7ea6b1..198b79c00 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -2572,6 +2572,6 @@ SPEC CHECKSUMS: SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb Yoga: 1259c7a8cbaccf7b4c3ddf8ee36ca11be9dee407 -PODFILE CHECKSUM: 8a63cefda7649b1efdae9ada9addff179eae4375 +PODFILE CHECKSUM: 73185dc21f929943e88451fb856faed1b6a0c8fd COCOAPODS: 1.16.2 diff --git a/app/ios/Self.xcodeproj/project.pbxproj b/app/ios/Self.xcodeproj/project.pbxproj index 9561be6dc..f0258eeae 100644 --- a/app/ios/Self.xcodeproj/project.pbxproj +++ b/app/ios/Self.xcodeproj/project.pbxproj @@ -789,7 +789,10 @@ "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -879,7 +882,10 @@ "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/common/index.ts b/common/index.ts index cd91f3248..ee7e8f047 100644 --- a/common/index.ts +++ b/common/index.ts @@ -97,8 +97,6 @@ export { export { generateTestData, testCustomData } from './src/utils/aadhaar/utils.js'; - - export { isAadhaarDocument, isMRZDocument } from './src/utils/index.js'; export { diff --git a/common/src/constants/constants.ts b/common/src/constants/constants.ts index e93723b00..4e769c0d2 100644 --- a/common/src/constants/constants.ts +++ b/common/src/constants/constants.ts @@ -136,7 +136,7 @@ export const MAX_PADDED_ECONTENT_LEN: Partial = { +export const MAX_PADDED_SIGNED_ATTR_LEN: Record<(typeof hashAlgos)[number], number> = { sha1: 128, sha224: 128, sha256: 256, @@ -144,7 +144,7 @@ export const MAX_PADDED_SIGNED_ATTR_LEN_FOR_TESTS: Record<(typeof hashAlgos)[num sha512: 256, }; -export const MAX_PADDED_SIGNED_ATTR_LEN: Record<(typeof hashAlgos)[number], number> = { +export const MAX_PADDED_SIGNED_ATTR_LEN_FOR_TESTS: Record<(typeof hashAlgos)[number], number> = { sha1: 128, sha224: 128, sha256: 256, diff --git a/common/src/utils/aadhaar/mockData.ts b/common/src/utils/aadhaar/mockData.ts index 78850d9b6..4cb80b69d 100644 --- a/common/src/utils/aadhaar/mockData.ts +++ b/common/src/utils/aadhaar/mockData.ts @@ -49,12 +49,15 @@ function computeUppercasePaddedName(name: string): number[] { .map((char) => char.charCodeAt(0)); } -export function convertByteArrayToBigInt(byteArray: Uint8Array | number[]): bigint { - let result = 0n; - for (let i = 0; i < byteArray.length; i++) { - result = result * 256n + BigInt(byteArray[i]); - } - return result; +// Helper function to compute final commitment +export function computeCommitment( + secret: bigint, + qrHash: bigint, + nullifier: bigint, + packedCommitment: bigint, + photoHash: bigint +): bigint { + return poseidon5([secret, qrHash, nullifier, packedCommitment, photoHash]); } // Helper function to compute packed commitment @@ -71,15 +74,12 @@ export function computePackedCommitment( return BigInt(packBytesAndPoseidon(packedCommitmentArgs)); } -// Helper function to compute final commitment -export function computeCommitment( - secret: bigint, - qrHash: bigint, - nullifier: bigint, - packedCommitment: bigint, - photoHash: bigint -): bigint { - return poseidon5([secret, qrHash, nullifier, packedCommitment, photoHash]); +export function convertByteArrayToBigInt(byteArray: Uint8Array | number[]): bigint { + let result = 0n; + for (let i = 0; i < byteArray.length; i++) { + result = result * 256n + BigInt(byteArray[i]); + } + return result; } interface SharedQRData { @@ -108,43 +108,6 @@ export function nullifierHash(extractedFields: ReturnType ({ ...acc, ...curr }), {}); } -export function generateCircuitInputsRegister( +export function generateCircuitInputsRegisterForTests( secret: string, passportData: PassportData, serializedDscTree: string @@ -302,7 +302,7 @@ export function generateCircuitInputsRegister( ); const [signedAttrPadded, signedAttrPaddedLen] = pad(passportMetadata.signedAttrHashFunction)( signedAttr, - MAX_PADDED_SIGNED_ATTR_LEN[passportMetadata.eContentHashFunction] + MAX_PADDED_SIGNED_ATTR_LEN_FOR_TESTS[passportMetadata.eContentHashFunction] ); const dsc_leaf = getLeafDscTree(dscParsed, passportData.csca_parsed); // TODO: WRONG diff --git a/common/src/utils/circuits/registerInputs.ts b/common/src/utils/circuits/registerInputs.ts index 12a579685..4b7ae4ccd 100644 --- a/common/src/utils/circuits/registerInputs.ts +++ b/common/src/utils/circuits/registerInputs.ts @@ -19,6 +19,7 @@ import { hashEndpointWithScope, } from '../../utils/index.js'; import type { AadhaarData, IDDocument, OfacTree } from '../../utils/types.js'; +import { prepareAadhaarDiscloseData, prepareAadhaarRegisterData } from '../aadhaar/mockData.js'; import { LeanIMT } from '@openpassport/zk-kit-lean-imt'; import { SMT } from '@openpassport/zk-kit-smt'; @@ -34,7 +35,6 @@ export function generateTEEInputsAadhaarDisclose( tree: T ) => T extends 'ofac' ? OfacTree : any ) { - const { prepareAadhaarDiscloseData } = require('../aadhaar/mockData.js'); const { scope, disclosures, endpoint, userId, userDefinedData, chainID } = selfApp; const userIdentifierHash = calculateUserIdentifierHash(chainID, userId, userDefinedData); const scope_hash = hashEndpointWithScope(endpoint, scope); @@ -90,7 +90,6 @@ export async function generateTEEInputsAadhaarRegister( publicKeys: string[], env: 'prod' | 'stg' ) { - const { prepareAadhaarRegisterData } = require('../aadhaar/mockData.js'); console.log( 'publicKeys-aadhaar', publicKeys, diff --git a/common/src/utils/index.ts b/common/src/utils/index.ts index f152e13c7..99bceda56 100644 --- a/common/src/utils/index.ts +++ b/common/src/utils/index.ts @@ -27,7 +27,6 @@ export { inferDocumentCategory, initPassportDataParsing, } from './passports/passport.js'; -export { isAadhaarDocument, isMRZDocument } from './types.js'; export { calculateUserIdentifierHash, customHasher, @@ -66,5 +65,6 @@ export { export { getCircuitNameFromPassportData } from './circuits/circuitsName.js'; export { getSKIPEM } from './csca.js'; export { initElliptic } from './certificate_parsing/elliptic.js'; +export { isAadhaarDocument, isMRZDocument } from './types.js'; export { parseCertificateSimple } from './certificate_parsing/parseCertificateSimple.js'; export { parseDscCertificateData } from './passports/passport_parsing/parseDscCertificateData.js'; diff --git a/common/src/utils/passports/genMockIdDoc.ts b/common/src/utils/passports/genMockIdDoc.ts index 0717da4a2..0b6e5f54c 100644 --- a/common/src/utils/passports/genMockIdDoc.ts +++ b/common/src/utils/passports/genMockIdDoc.ts @@ -7,6 +7,10 @@ import forge from 'node-forge'; import type { hashAlgosTypes } from '../../constants/constants.js'; import { API_URL_STAGING } from '../../constants/constants.js'; import { countries } from '../../constants/countries.js'; +import { + AADHAAR_MOCK_PRIVATE_KEY_PEM, + AADHAAR_MOCK_PUBLIC_KEY_PEM, +} from '../../mock_certificates/aadhaar/mockAadhaarCert.js'; import { convertByteArrayToBigInt, processQRData } from '../aadhaar/mockData.js'; import { extractQRDataFields } from '../aadhaar/utils.js'; import { getCurveForElliptic } from '../certificate_parsing/curves.js'; @@ -21,10 +25,6 @@ import { genDG1 } from './dg1.js'; import { formatAndConcatenateDataHashes, formatMrz, generateSignedAttr } from './format.js'; import { getMockDSC } from './getMockDSC.js'; import { initPassportDataParsing } from './passport.js'; -import { - AADHAAR_MOCK_PRIVATE_KEY_PEM, - AADHAAR_MOCK_PUBLIC_KEY_PEM, -} from '../../mock_certificates/aadhaar/mockAadhaarCert.js'; export interface IdDocInput { idType: 'mock_passport' | 'mock_id_card' | 'mock_aadhaar';