@vppos/react-native-nfc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +20 -0
  2. package/NFCSDK.podspec +40 -0
  3. package/android/build.gradle +85 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +31 -0
  6. package/android/src/main/AndroidManifestNew.xml +30 -0
  7. package/android/src/main/java/com/nfcsdk/ChipReader.kt +237 -0
  8. package/android/src/main/java/com/nfcsdk/NFCSDKModule.kt +78 -0
  9. package/android/src/main/java/com/nfcsdk/NFCSDKPackage.kt +17 -0
  10. package/android/src/main/java/com/nfcsdk/NFCScanActivity.kt +299 -0
  11. package/android/src/main/java/com/nfcsdk/utils/Dg13Parser.kt +278 -0
  12. package/android/src/main/java/com/nfcsdk/utils/FaceExtractor.kt +40 -0
  13. package/android/src/main/java/com/nfcsdk/utils/MrzUtils.kt +75 -0
  14. package/android/src/main/res/drawable/bg_nfc_bottom_sheet.xml +13 -0
  15. package/android/src/main/res/drawable/bg_nfc_sheet_handle.xml +9 -0
  16. package/android/src/main/res/layout/activity_nfc.xml +110 -0
  17. package/android/src/main/res/values/styles.xml +10 -0
  18. package/android/src/main/res/xml/nfc_tech_filter.xml +8 -0
  19. package/ios/ChipReader.swift +258 -0
  20. package/ios/NFCSDK-Bridging-Header.h +2 -0
  21. package/ios/NFCSDK.mm +9 -0
  22. package/ios/NFCSDK.swift +112 -0
  23. package/ios/NFCSDKSession.swift +5 -0
  24. package/ios/utils/DG13Parser.swift +302 -0
  25. package/ios/utils/MrzUtils.swift +49 -0
  26. package/lib/module/errors.js +9 -0
  27. package/lib/module/errors.js.map +1 -0
  28. package/lib/module/index.js +118 -0
  29. package/lib/module/index.js.map +1 -0
  30. package/lib/module/package.json +1 -0
  31. package/lib/module/types.js +2 -0
  32. package/lib/module/types.js.map +1 -0
  33. package/lib/typescript/package.json +1 -0
  34. package/lib/typescript/src/errors.d.ts +4 -0
  35. package/lib/typescript/src/errors.d.ts.map +1 -0
  36. package/lib/typescript/src/index.d.ts +80 -0
  37. package/lib/typescript/src/index.d.ts.map +1 -0
  38. package/lib/typescript/src/types.d.ts +45 -0
  39. package/lib/typescript/src/types.d.ts.map +1 -0
  40. package/package.json +155 -0
  41. package/src/errors.ts +6 -0
  42. package/src/index.tsx +141 -0
  43. package/src/types.ts +45 -0
  44. package/vendor/ios-passport-reader/CHANGELOG +362 -0
  45. package/vendor/ios-passport-reader/CODE_OF_CONDUCT.md +77 -0
  46. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/AppDelegate.swift +39 -0
  47. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/Contents.json +158 -0
  48. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-40.png +0 -0
  49. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png +0 -0
  50. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png +0 -0
  51. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png +0 -0
  52. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png +0 -0
  53. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-72.png +0 -0
  54. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png +0 -0
  55. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-76.png +0 -0
  56. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png +0 -0
  57. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png +0 -0
  58. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small-50.png +0 -0
  59. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png +0 -0
  60. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small.png +0 -0
  61. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png +0 -0
  62. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png +0 -0
  63. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon.png +0 -0
  64. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon@2x.png +0 -0
  65. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/ios-marketing.png +0 -0
  66. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png +0 -0
  67. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png +0 -0
  68. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png +0 -0
  69. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png +0 -0
  70. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/Contents.json +6 -0
  71. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/background.imageset/Contents.json +21 -0
  72. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/background.imageset/background.png +0 -0
  73. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/head.imageset/Contents.json +21 -0
  74. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Assets.xcassets/head.imageset/head.png +0 -0
  75. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Base.lproj/LaunchScreen.storyboard +25 -0
  76. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Extensions/FileManagerExt.swift +16 -0
  77. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Extensions/StringExt.swift +40 -0
  78. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Extensions/UIApplicationExt.swift +21 -0
  79. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Info.plist +77 -0
  80. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Model/PassportUtils.swift +76 -0
  81. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Model/SettingsStore.swift +107 -0
  82. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/NFCPassportReader.entitlements +10 -0
  83. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/SceneDelegate.swift +57 -0
  84. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/DetailsView.swift +197 -0
  85. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/ExportPassportView.swift +164 -0
  86. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/HelperViews/CheckBoxView.swift +48 -0
  87. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/HelperViews/ViewExt.swift +20 -0
  88. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/HelperViews/ViewModifiers.swift +41 -0
  89. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/MRZEntryView.swift +125 -0
  90. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/MRZScannerViewController.swift +90 -0
  91. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/MainView.swift +214 -0
  92. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/PassportSummaryView.swift +111 -0
  93. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/PassportView.swift +73 -0
  94. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/SettingsView.swift +63 -0
  95. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/Views/StoredPassportView.swift +152 -0
  96. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/masterList.pem +32 -0
  97. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp/readme.md +10 -0
  98. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp.xcodeproj/project.pbxproj +695 -0
  99. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp.xcodeproj/xcshareddata/xcschemes/NFCPassportReader.xcscheme +106 -0
  100. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp.xcworkspace/contents.xcworkspacedata +10 -0
  101. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  102. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderApp.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  103. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderAppTests/DataGroupParsingTests.swift +189 -0
  104. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderAppTests/Info.plist +22 -0
  105. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderAppTests/NFCPassportReaderTests.swift +260 -0
  106. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/NFCPassportReaderAppTests/PACETests.swift +112 -0
  107. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/Podfile +22 -0
  108. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/Podfile.lock +35 -0
  109. package/vendor/ios-passport-reader/Examples/Example_CocoaPods/README.md +2 -0
  110. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/AppDelegate.swift +39 -0
  111. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/Contents.json +158 -0
  112. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-40.png +0 -0
  113. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png +0 -0
  114. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png +0 -0
  115. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png +0 -0
  116. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png +0 -0
  117. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-72.png +0 -0
  118. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png +0 -0
  119. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-76.png +0 -0
  120. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png +0 -0
  121. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png +0 -0
  122. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small-50.png +0 -0
  123. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png +0 -0
  124. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small.png +0 -0
  125. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png +0 -0
  126. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png +0 -0
  127. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon.png +0 -0
  128. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/icon@2x.png +0 -0
  129. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/ios-marketing.png +0 -0
  130. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png +0 -0
  131. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png +0 -0
  132. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png +0 -0
  133. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png +0 -0
  134. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/Contents.json +6 -0
  135. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/background.imageset/Contents.json +21 -0
  136. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/background.imageset/background.png +0 -0
  137. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/head.imageset/Contents.json +21 -0
  138. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Assets.xcassets/head.imageset/head.png +0 -0
  139. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Base.lproj/LaunchScreen.storyboard +25 -0
  140. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Extensions/FileManagerExt.swift +16 -0
  141. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Extensions/StringExt.swift +40 -0
  142. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Extensions/UIApplicationExt.swift +21 -0
  143. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Info.plist +79 -0
  144. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Model/PassportUtils.swift +99 -0
  145. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Model/SettingsStore.swift +98 -0
  146. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Model/SettingsStoreCAN.swift +75 -0
  147. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/MrzScanner/LICENSE +21 -0
  148. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/MrzScanner/PreviewView.swift +34 -0
  149. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/MrzScanner/StringUtils.swift +160 -0
  150. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/MrzScanner/ViewController.swift +320 -0
  151. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/MrzScanner/VisionViewController.swift +163 -0
  152. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/NFCPassportReader.entitlements +10 -0
  153. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/SceneDelegate.swift +58 -0
  154. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/CANViews/CanKeyView.swift +251 -0
  155. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/CANViews/MRZEntryViewCanKey.swift +65 -0
  156. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/CANViews/PassportViewCAN.swift +73 -0
  157. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/DetailsView.swift +193 -0
  158. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/ExportPassportView.swift +164 -0
  159. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/HelperViews/CheckBoxView.swift +48 -0
  160. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/HelperViews/ViewExt.swift +20 -0
  161. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/HelperViews/ViewModifiers.swift +41 -0
  162. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/MRZEntryView.swift +125 -0
  163. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/MRZScannerViewController.swift +90 -0
  164. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/MainView.swift +264 -0
  165. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/PassportSummaryView.swift +111 -0
  166. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/PassportView.swift +73 -0
  167. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/SettingsView.swift +47 -0
  168. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/Views/StoredPassportView.swift +149 -0
  169. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/masterList.pem +32 -0
  170. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp/readme.md +10 -0
  171. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp.xcodeproj/project.pbxproj +683 -0
  172. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  173. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +25 -0
  174. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderApp.xcodeproj/xcshareddata/xcschemes/NFCPassportReader.xcscheme +106 -0
  175. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderAppTests/DataGroupParsingTests.swift +190 -0
  176. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderAppTests/Info.plist +22 -0
  177. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderAppTests/NFCPassportReaderTests.swift +260 -0
  178. package/vendor/ios-passport-reader/Examples/Example_SPM/NFCPassportReaderAppTests/PACETests.swift +112 -0
  179. package/vendor/ios-passport-reader/LICENSE +21 -0
  180. package/vendor/ios-passport-reader/NFCPassportReader.podspec +27 -0
  181. package/vendor/ios-passport-reader/Package.swift +29 -0
  182. package/vendor/ios-passport-reader/README.md +141 -0
  183. package/vendor/ios-passport-reader/Sources/NFCPassportReader/AES_3DES_DESEncryption.swift +377 -0
  184. package/vendor/ios-passport-reader/Sources/NFCPassportReader/BACHandler.swift +194 -0
  185. package/vendor/ios-passport-reader/Sources/NFCPassportReader/ChipAuthenticationHandler.swift +224 -0
  186. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroupHash.swift +16 -0
  187. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroupParser.swift +36 -0
  188. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/ActiveAuthenticationInfo.swift +69 -0
  189. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/COM.swift +61 -0
  190. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/CardAccess.swift +38 -0
  191. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/ChipAuthenticationInfo.swift +135 -0
  192. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/ChipAuthenticationPublicKeyInfo.swift +53 -0
  193. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup.swift +103 -0
  194. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup1.swift +111 -0
  195. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup11.swift +66 -0
  196. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup12.swift +75 -0
  197. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup14.swift +37 -0
  198. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup15.swift +46 -0
  199. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup2.swift +163 -0
  200. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroup7.swift +46 -0
  201. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/DataGroupId.swift +105 -0
  202. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/NotImplementedDG.swift +16 -0
  203. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/PACEInfo.swift +415 -0
  204. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/SOD.swift +240 -0
  205. package/vendor/ios-passport-reader/Sources/NFCPassportReader/DataGroups/SecurityInfo.swift +136 -0
  206. package/vendor/ios-passport-reader/Sources/NFCPassportReader/Errors.swift +148 -0
  207. package/vendor/ios-passport-reader/Sources/NFCPassportReader/Logging.swift +32 -0
  208. package/vendor/ios-passport-reader/Sources/NFCPassportReader/Models/FaceImageInfo.swift +161 -0
  209. package/vendor/ios-passport-reader/Sources/NFCPassportReader/NFCPassportModel.swift +540 -0
  210. package/vendor/ios-passport-reader/Sources/NFCPassportReader/NFCViewDisplayMessage.swift +60 -0
  211. package/vendor/ios-passport-reader/Sources/NFCPassportReader/OpenSSLUtils.swift +705 -0
  212. package/vendor/ios-passport-reader/Sources/NFCPassportReader/PACEHandler.swift +627 -0
  213. package/vendor/ios-passport-reader/Sources/NFCPassportReader/PassportReader.swift +387 -0
  214. package/vendor/ios-passport-reader/Sources/NFCPassportReader/Resources/PrivacyInfo.xcprivacy +14 -0
  215. package/vendor/ios-passport-reader/Sources/NFCPassportReader/ResponseAPDU.swift +25 -0
  216. package/vendor/ios-passport-reader/Sources/NFCPassportReader/SecureMessaging.swift +301 -0
  217. package/vendor/ios-passport-reader/Sources/NFCPassportReader/SecureMessagingSessionKeyGenerator.swift +156 -0
  218. package/vendor/ios-passport-reader/Sources/NFCPassportReader/SimpleASN1DumpParser.swift +173 -0
  219. package/vendor/ios-passport-reader/Sources/NFCPassportReader/TagReader.swift +374 -0
  220. package/vendor/ios-passport-reader/Sources/NFCPassportReader/Utils.swift +430 -0
  221. package/vendor/ios-passport-reader/Sources/NFCPassportReader/X509Wrapper.swift +168 -0
  222. package/vendor/ios-passport-reader/scripts/README.md +45 -0
  223. package/vendor/ios-passport-reader/scripts/extract.py +197 -0
@@ -0,0 +1,299 @@
1
+ package com.nfcsdk
2
+
3
+ import android.app.PendingIntent
4
+ import android.content.Intent
5
+ import android.content.IntentFilter
6
+ import android.graphics.Color
7
+ import android.graphics.drawable.ColorDrawable
8
+ import android.nfc.NfcAdapter
9
+ import android.nfc.Tag
10
+ import android.nfc.tech.IsoDep
11
+ import android.os.Build
12
+ import android.os.Bundle
13
+ import android.os.VibrationEffect
14
+ import android.os.Vibrator
15
+ import android.provider.Settings
16
+ import android.util.Log
17
+ import android.view.Gravity
18
+ import android.view.View
19
+ import android.widget.Button
20
+ import android.widget.ProgressBar
21
+ import android.widget.TextView
22
+ import android.widget.Toast
23
+ import androidx.appcompat.app.AppCompatActivity
24
+ import com.facebook.react.bridge.Arguments
25
+ import java.security.Security
26
+
27
+ class NFCScanActivity : AppCompatActivity() {
28
+
29
+ private var nfcAdapter: NfcAdapter? = null
30
+
31
+ private lateinit var progressBar: ProgressBar
32
+ private lateinit var progressPercentTextView: TextView
33
+ private lateinit var retryButton: Button
34
+
35
+ @Volatile
36
+ private var isReading = false
37
+
38
+ @Volatile
39
+ private var scanCompleted = false
40
+
41
+ // ── Lifecycle ──────────────────────────────────────────────
42
+
43
+ override fun onCreate(savedInstanceState: Bundle?) {
44
+ super.onCreate(savedInstanceState)
45
+
46
+ setContentView(R.layout.activity_nfc)
47
+ setupWindow()
48
+ setupTransition()
49
+ setupSheetAnimation()
50
+ setupViews()
51
+
52
+ Security.insertProviderAt(
53
+ org.spongycastle.jce.provider.BouncyCastleProvider(),
54
+ 1,
55
+ )
56
+
57
+ nfcAdapter = NfcAdapter.getDefaultAdapter(this)
58
+ if (nfcAdapter == null) {
59
+ Toast.makeText(this, "Thiết bị không hỗ trợ NFC", Toast.LENGTH_LONG).show()
60
+ finish()
61
+ return
62
+ }
63
+
64
+ if (nfcAdapter?.isEnabled != true) {
65
+ Toast.makeText(this, "Vui lòng bật NFC", Toast.LENGTH_LONG).show()
66
+ startActivity(Intent(Settings.ACTION_NFC_SETTINGS))
67
+ }
68
+
69
+ updateProgress(0, "Đưa CCCD sát mặt lưng điện thoại")
70
+ }
71
+
72
+ override fun onResume() {
73
+ super.onResume()
74
+
75
+ val intent = Intent(this, javaClass)
76
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
77
+
78
+ val pendingIntent =
79
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
80
+ PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
81
+ } else {
82
+ PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
83
+ }
84
+
85
+ val filters = arrayOf(IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED))
86
+ val techList = arrayOf(arrayOf(IsoDep::class.java.name))
87
+
88
+ nfcAdapter?.enableForegroundDispatch(this, pendingIntent, filters, techList)
89
+ }
90
+
91
+ override fun onPause() {
92
+ super.onPause()
93
+ nfcAdapter?.disableForegroundDispatch(this)
94
+ }
95
+
96
+ override fun onDestroy() {
97
+ super.onDestroy()
98
+ isReading = false
99
+ scanCompleted = false
100
+ }
101
+
102
+ override fun onNewIntent(intent: Intent) {
103
+ super.onNewIntent(intent)
104
+
105
+ if (isReading || scanCompleted) return
106
+
107
+ @Suppress("DEPRECATION")
108
+ val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG) ?: return
109
+
110
+ startChipRead(tag)
111
+ }
112
+
113
+ // ── Chip Reading ───────────────────────────────────────────
114
+
115
+ private fun startChipRead(tag: Tag) {
116
+ isReading = true
117
+
118
+ Thread {
119
+ try {
120
+ val citizenId = NFCSDKModule.documentNumber.trim()
121
+ if (citizenId.length < 6) {
122
+ updateProgress(-1, "CCCD không hợp lệ")
123
+ Log.e("NFCSDK", "startChipRead: invalid citizenId")
124
+ isReading = false
125
+ return@Thread
126
+ }
127
+
128
+ val result = ChipReader.read(tag, citizenId) { progress, message ->
129
+ updateProgress(progress, message)
130
+ }
131
+
132
+ vibrate(200)
133
+ scanCompleted = true
134
+ sendResult(result)
135
+
136
+ runOnUiThread {
137
+ window.decorView.postDelayed({ finish() }, 800)
138
+ }
139
+ } catch (e: ChipReadException) {
140
+ Log.e("NFCSDK", "Chip read failed: ${e.message}")
141
+ updateProgress(-1, e.message ?: "Đọc NFC thất bại")
142
+ isReading = false
143
+ } catch (e: Exception) {
144
+ Log.e("NFCSDK", "NFC read failed", e)
145
+ updateProgress(-1, "Đọc NFC thất bại")
146
+ isReading = false
147
+ }
148
+ }.start()
149
+ }
150
+
151
+ private fun sendResult(result: ChipReadResult) {
152
+ val resultMap = Arguments.createMap().apply {
153
+ putString("citizenId", result.citizenId)
154
+ putString("fullName", result.fullName)
155
+ putString("dob", result.dob)
156
+ putString("gender", result.gender)
157
+ putString("nationality", result.nationality)
158
+ putString("permanentAddress", result.permanentAddress)
159
+ putString("issueDate", result.issueDate)
160
+ putString("issuePlace", result.issuePlace)
161
+ putString("expireDate", result.expireDate)
162
+ putString("imageFromChip", result.imageFromChipBase64)
163
+ putString("frontCardBase64", "")
164
+ putString("backCardBase64", "")
165
+ putString("dg1DataB64", result.dg1DataB64)
166
+ putString("dg2DataB64", result.dg2DataB64)
167
+ putString("dg13DataB64", result.dg13DataB64)
168
+ putString("dg14DataB64", result.dg14DataB64)
169
+ putString("sodData", result.sodData)
170
+ }
171
+
172
+ NFCSDKModule.sendEvent("onNfcCompleted", resultMap)
173
+ }
174
+
175
+ // ── UI Setup ───────────────────────────────────────────────
176
+
177
+ private fun setupWindow() {
178
+ window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
179
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
180
+ window.setLayout(
181
+ android.view.WindowManager.LayoutParams.MATCH_PARENT,
182
+ android.view.WindowManager.LayoutParams.MATCH_PARENT,
183
+ )
184
+ window.setGravity(Gravity.BOTTOM)
185
+ }
186
+
187
+ private fun setupTransition() {
188
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
189
+ overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, 0, 0)
190
+ } else {
191
+ @Suppress("DEPRECATION")
192
+ overridePendingTransition(0, 0)
193
+ }
194
+ }
195
+
196
+ private fun setupSheetAnimation() {
197
+ val sheetScrim = findViewById<View>(R.id.sheetScrim)
198
+ val sheetContainer = findViewById<View>(R.id.sheetContainer)
199
+
200
+ sheetScrim.alpha = 0f
201
+ sheetContainer.post {
202
+ sheetContainer.translationY = sheetContainer.height.toFloat()
203
+ sheetScrim.animate().alpha(1f).setDuration(300).start()
204
+ sheetContainer.animate()
205
+ .translationY(0f)
206
+ .setDuration(300)
207
+ .setInterpolator(android.view.animation.DecelerateInterpolator())
208
+ .start()
209
+ }
210
+
211
+ sheetScrim.setOnClickListener {
212
+ if (!isReading) finish()
213
+ }
214
+ }
215
+
216
+ private fun setupViews() {
217
+ progressBar = findViewById(R.id.nfcProgressBar)
218
+ progressPercentTextView = findViewById(R.id.tvProgressPercent)
219
+ retryButton = findViewById(R.id.btnRetry)
220
+
221
+ retryButton.setOnClickListener {
222
+ isReading = false
223
+ scanCompleted = false
224
+ progressBar.progress = 0
225
+ retryButton.visibility = View.GONE
226
+ updateProgress(0, "Đưa CCCD sát mặt lưng điện thoại")
227
+ vibrate(120)
228
+ }
229
+ }
230
+
231
+ // ── Progress & Feedback ────────────────────────────────────
232
+
233
+ private fun updateProgress(progress: Int, message: String) {
234
+ runOnUiThread {
235
+ if (progress >= 0) {
236
+ progressBar.progress = progress
237
+ progressPercentTextView.text = "$progress%"
238
+ } else {
239
+ progressPercentTextView.text = "--%"
240
+ }
241
+
242
+ retryButton.visibility = if (progress < 0) View.VISIBLE else View.GONE
243
+ }
244
+
245
+ val params = Arguments.createMap().apply {
246
+ putInt("progress", progress)
247
+ putString("message", message)
248
+ }
249
+ NFCSDKModule.sendEvent("onNfcProgress", params)
250
+ }
251
+
252
+ private fun vibrate(durationMs: Long) {
253
+ try {
254
+ @Suppress("DEPRECATION")
255
+ val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator
256
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
257
+ vibrator.vibrate(
258
+ VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE),
259
+ )
260
+ } else {
261
+ @Suppress("DEPRECATION")
262
+ vibrator.vibrate(durationMs)
263
+ }
264
+ } catch (_: Exception) {
265
+ }
266
+ }
267
+
268
+ // ── Finish Animation ───────────────────────────────────────
269
+
270
+ override fun finish() {
271
+ val sheetScrim = findViewById<View>(R.id.sheetScrim)
272
+ val sheetContainer = findViewById<View>(R.id.sheetContainer)
273
+
274
+ if (sheetScrim != null && sheetContainer != null) {
275
+ sheetScrim.animate().alpha(0f).setDuration(250).start()
276
+ sheetContainer.animate()
277
+ .translationY(sheetContainer.height.toFloat())
278
+ .setDuration(250)
279
+ .setInterpolator(android.view.animation.AccelerateInterpolator())
280
+ .withEndAction {
281
+ super.finish()
282
+ suppressTransition()
283
+ }
284
+ .start()
285
+ } else {
286
+ super.finish()
287
+ suppressTransition()
288
+ }
289
+ }
290
+
291
+ private fun suppressTransition() {
292
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
293
+ overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, 0, 0)
294
+ } else {
295
+ @Suppress("DEPRECATION")
296
+ overridePendingTransition(0, 0)
297
+ }
298
+ }
299
+ }
@@ -0,0 +1,278 @@
1
+ package com.nfcsdk.utils
2
+
3
+ import java.nio.charset.Charset
4
+ import java.text.Normalizer
5
+ import java.text.ParseException
6
+ import java.text.SimpleDateFormat
7
+ import java.util.Date
8
+ import java.util.Locale
9
+
10
+ data class Dg13ParsedData(
11
+ val gender: String = "",
12
+ val permanentAddress: String = "",
13
+ val issueDate: String = "",
14
+ val issuePlace: String = "",
15
+ val expireDate: String = "",
16
+ val fieldMap: Map<Int, List<String>> = emptyMap(),
17
+ )
18
+
19
+ private data class Asn1Node(
20
+ val tagClass: Int,
21
+ val tagNumber: Int,
22
+ val constructed: Boolean,
23
+ val valueOffset: Int,
24
+ val valueLength: Int,
25
+ val nextOffset: Int,
26
+ )
27
+
28
+ object Dg13Parser {
29
+ private const val TAG_CLASS_UNIVERSAL = 0
30
+ private const val TAG_CLASS_APPLICATION = 1
31
+
32
+ fun parse(
33
+ dg13Bytes: ByteArray,
34
+ fallbackIssuePlace: String = "",
35
+ ): Dg13ParsedData {
36
+ if (dg13Bytes.isEmpty()) {
37
+ return Dg13ParsedData(issuePlace = fallbackIssuePlace)
38
+ }
39
+
40
+ val fields = parseStructuredFields(dg13Bytes)
41
+ ?: return Dg13ParsedData(issuePlace = fallbackIssuePlace)
42
+
43
+ val candidates = fields.values.flatten().distinct()
44
+
45
+ return Dg13ParsedData(
46
+ gender = normalizeGender(fields[0x04]?.firstOrNull().orEmpty()),
47
+ permanentAddress = fields[0x09]?.firstOrNull().orEmpty(),
48
+ issueDate = normalizeDate(fields[0x0B]?.firstOrNull().orEmpty()),
49
+ issuePlace = findIssuePlace(candidates) ?: fallbackIssuePlace,
50
+ expireDate = normalizeDate(fields[0x0C]?.firstOrNull().orEmpty()),
51
+ fieldMap = fields,
52
+ )
53
+ }
54
+
55
+ fun normalizeGender(raw: String): String {
56
+ return when (normalizeForSearch(raw)) {
57
+ "M", "MALE", "NAM" -> "Nam"
58
+ "F", "FEMALE", "NU" -> "Nữ"
59
+ else -> raw.trim()
60
+ }
61
+ }
62
+
63
+ private fun parseStructuredFields(bytes: ByteArray): Map<Int, List<String>>? {
64
+ val root = parseNode(bytes, 0) ?: return null
65
+ if (root.tagClass != TAG_CLASS_APPLICATION || root.tagNumber != 13) {
66
+ return null
67
+ }
68
+
69
+ val rootChildren = parseChildren(bytes, root)
70
+ val sequenceNode = rootChildren.firstOrNull() ?: return null
71
+ val sequenceChildren = parseChildren(bytes, sequenceNode)
72
+ val setNode = sequenceChildren.firstOrNull {
73
+ it.tagClass == TAG_CLASS_UNIVERSAL && it.tagNumber == 17
74
+ } ?: return null
75
+
76
+ val fieldNodes = parseChildren(bytes, setNode)
77
+ if (fieldNodes.isEmpty()) {
78
+ return null
79
+ }
80
+
81
+ val result = linkedMapOf<Int, List<String>>()
82
+ fieldNodes.forEach { fieldNode ->
83
+ if (!(fieldNode.tagClass == TAG_CLASS_UNIVERSAL && fieldNode.tagNumber == 16)) {
84
+ return@forEach
85
+ }
86
+
87
+ val parts = parseChildren(bytes, fieldNode)
88
+ val indexNode = parts.firstOrNull {
89
+ it.tagClass == TAG_CLASS_UNIVERSAL && it.tagNumber == 2
90
+ } ?: return@forEach
91
+
92
+ val index = parseInteger(bytes, indexNode) ?: return@forEach
93
+ val values = parts
94
+ .drop(1)
95
+ .flatMap { extractStrings(bytes, it) }
96
+ .map(::cleanValue)
97
+ .filter { it.isNotBlank() }
98
+
99
+ if (values.isNotEmpty()) {
100
+ result[index] = values
101
+ }
102
+ }
103
+
104
+ return result.takeIf { it.isNotEmpty() }
105
+ }
106
+
107
+ private fun parseChildren(bytes: ByteArray, parent: Asn1Node): List<Asn1Node> {
108
+ if (!parent.constructed) {
109
+ return emptyList()
110
+ }
111
+
112
+ val children = mutableListOf<Asn1Node>()
113
+ var offset = parent.valueOffset
114
+ val end = parent.valueOffset + parent.valueLength
115
+
116
+ while (offset < end) {
117
+ val child = parseNode(bytes, offset) ?: break
118
+ if (child.nextOffset > end) {
119
+ break
120
+ }
121
+ children.add(child)
122
+ offset = child.nextOffset
123
+ }
124
+
125
+ return children
126
+ }
127
+
128
+ private fun parseNode(bytes: ByteArray, offset: Int): Asn1Node? {
129
+ if (offset >= bytes.size) {
130
+ return null
131
+ }
132
+
133
+ var cursor = offset
134
+ val firstTagByte = bytes[cursor].toInt() and 0xFF
135
+ cursor += 1
136
+
137
+ val tagClass = (firstTagByte ushr 6) and 0x03
138
+ val constructed = (firstTagByte and 0x20) != 0
139
+ var tagNumber = firstTagByte and 0x1F
140
+
141
+ if (tagNumber == 0x1F) {
142
+ tagNumber = 0
143
+ while (cursor < bytes.size) {
144
+ val next = bytes[cursor].toInt() and 0xFF
145
+ cursor += 1
146
+ tagNumber = (tagNumber shl 7) or (next and 0x7F)
147
+ if ((next and 0x80) == 0) {
148
+ break
149
+ }
150
+ }
151
+ }
152
+
153
+ if (cursor >= bytes.size) {
154
+ return null
155
+ }
156
+
157
+ val lengthByte = bytes[cursor].toInt() and 0xFF
158
+ cursor += 1
159
+
160
+ val valueLength =
161
+ if ((lengthByte and 0x80) == 0) {
162
+ lengthByte
163
+ } else {
164
+ val count = lengthByte and 0x7F
165
+ if (count == 0 || count > 4 || cursor + count > bytes.size) {
166
+ return null
167
+ }
168
+
169
+ var length = 0
170
+ repeat(count) {
171
+ length = (length shl 8) or (bytes[cursor + it].toInt() and 0xFF)
172
+ }
173
+ cursor += count
174
+ length
175
+ }
176
+
177
+ val nextOffset = cursor + valueLength
178
+ if (nextOffset > bytes.size) {
179
+ return null
180
+ }
181
+
182
+ return Asn1Node(
183
+ tagClass = tagClass,
184
+ tagNumber = tagNumber,
185
+ constructed = constructed,
186
+ valueOffset = cursor,
187
+ valueLength = valueLength,
188
+ nextOffset = nextOffset,
189
+ )
190
+ }
191
+
192
+ private fun parseInteger(bytes: ByteArray, node: Asn1Node): Int? {
193
+ if (node.tagClass != TAG_CLASS_UNIVERSAL || node.tagNumber != 2) {
194
+ return null
195
+ }
196
+
197
+ var result = 0
198
+ for (index in 0 until node.valueLength) {
199
+ result = (result shl 8) or (bytes[node.valueOffset + index].toInt() and 0xFF)
200
+ }
201
+ return result
202
+ }
203
+
204
+ private fun extractStrings(bytes: ByteArray, node: Asn1Node): List<String> {
205
+ if (node.constructed) {
206
+ return parseChildren(bytes, node).flatMap { child ->
207
+ extractStrings(bytes, child)
208
+ }
209
+ }
210
+
211
+ if (node.tagClass != TAG_CLASS_UNIVERSAL) {
212
+ return emptyList()
213
+ }
214
+
215
+ val value = bytes.copyOfRange(node.valueOffset, node.nextOffset)
216
+ val decoded = when (node.tagNumber) {
217
+ 12, 19, 20, 22 -> decodeString(value, Charsets.UTF_8)
218
+ 30 -> decodeString(value, Charsets.UTF_16BE)
219
+ else -> ""
220
+ }
221
+
222
+ return decoded.takeIf { it.isNotBlank() }?.let(::listOf) ?: emptyList()
223
+ }
224
+
225
+ private fun decodeString(value: ByteArray, charset: Charset): String {
226
+ return value.toString(charset).trim()
227
+ }
228
+
229
+ private fun cleanValue(raw: String): String {
230
+ return raw.replace(Regex("""\s+"""), " ").trim()
231
+ }
232
+
233
+ private fun normalizeForSearch(raw: String): String {
234
+ return Normalizer.normalize(raw, Normalizer.Form.NFD)
235
+ .replace(Regex("""\p{M}+"""), "")
236
+ .uppercase(Locale.ROOT)
237
+ .trim()
238
+ }
239
+
240
+ private fun findIssuePlace(candidates: List<String>): String? {
241
+ return candidates.firstOrNull { candidate ->
242
+ val normalized = normalizeForSearch(candidate)
243
+ normalized.contains("CONG AN") || normalized.contains("CANH SAT")
244
+ }
245
+ }
246
+
247
+ private fun normalizeDate(raw: String): String {
248
+ val parsed = tryParseDate(raw) ?: return raw.trim()
249
+ return OUTPUT_DATE_FORMAT.format(parsed)
250
+ }
251
+
252
+ private fun tryParseDate(raw: String): Date? {
253
+ val clean = raw.trim()
254
+ for (pattern in DATE_PATTERNS) {
255
+ try {
256
+ return SimpleDateFormat(pattern, Locale.ROOT).apply {
257
+ isLenient = false
258
+ }.parse(clean)
259
+ } catch (_: ParseException) {
260
+ }
261
+ }
262
+
263
+ return null
264
+ }
265
+
266
+ private val OUTPUT_DATE_FORMAT =
267
+ SimpleDateFormat("dd/MM/yyyy", Locale.ROOT).apply {
268
+ isLenient = false
269
+ }
270
+
271
+ private val DATE_PATTERNS =
272
+ listOf(
273
+ "dd/MM/yyyy",
274
+ "yyyy-MM-dd",
275
+ "ddMMyyyy",
276
+ "yyyyMMdd",
277
+ )
278
+ }
@@ -0,0 +1,40 @@
1
+ package com.nfcsdk.utils
2
+
3
+ import android.util.Base64
4
+ import android.util.Log
5
+ import org.jmrtd.lds.icao.DG2File
6
+ import java.io.ByteArrayInputStream
7
+
8
+ /**
9
+ * Trích xuất ảnh khuôn mặt từ DG2 (biometric data).
10
+ */
11
+ object FaceExtractor {
12
+
13
+ private const val TAG = "NFCSDK"
14
+
15
+ /**
16
+ * Trích xuất ảnh khuôn mặt đầu tiên từ DG2 raw bytes.
17
+ * @return Base64-encoded image string, hoặc null nếu không tìm thấy.
18
+ */
19
+ fun extractBase64(dg2Bytes: ByteArray): String? {
20
+ return try {
21
+ val dg2File = DG2File(ByteArrayInputStream(dg2Bytes))
22
+ val faceInfos = dg2File.faceInfos
23
+ if (faceInfos.isEmpty()) return null
24
+
25
+ val faceImageInfos = faceInfos[0].faceImageInfos
26
+ if (faceImageInfos.isEmpty()) return null
27
+
28
+ val imageBytes = faceImageInfos[0].imageInputStream.readBytes()
29
+ if (imageBytes.isEmpty()) {
30
+ Log.e(TAG, "extractBase64: imageBytes is empty")
31
+ return null
32
+ }
33
+
34
+ Base64.encodeToString(imageBytes, Base64.NO_WRAP)
35
+ } catch (exception: Exception) {
36
+ Log.e(TAG, "DG2 face extraction failed", exception)
37
+ null
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,75 @@
1
+ package com.nfcsdk.utils
2
+
3
+ import java.util.Calendar
4
+ import java.util.Locale
5
+
6
+ /**
7
+ * Utility functions xử lý dữ liệu MRZ (Machine Readable Zone) từ DG1.
8
+ */
9
+ object MrzUtils {
10
+
11
+ /**
12
+ * Format ngày sinh từ MRZ (YYMMDD) sang DD/MM/YYYY.
13
+ * Sử dụng năm hiện tại làm ngưỡng xác định thế kỷ (19xx vs 20xx).
14
+ */
15
+ fun formatBirthDate(value: String): String {
16
+ if (value.length != 6) return value
17
+
18
+ val yy = value.substring(0, 2)
19
+ val mm = value.substring(2, 4)
20
+ val dd = value.substring(4, 6)
21
+ val currentYearShort = Calendar.getInstance().get(Calendar.YEAR) % 100
22
+ val year = if (yy.toIntOrNull() ?: 0 > currentYearShort) "19$yy" else "20$yy"
23
+
24
+ return "$dd/$mm/$year"
25
+ }
26
+
27
+ /**
28
+ * Format ngày hết hạn từ MRZ (YYMMDD) sang DD/MM/20YY.
29
+ * CCCD luôn hết hạn trong thế kỷ 21.
30
+ */
31
+ fun formatExpireDate(value: String): String {
32
+ if (value.length != 6) return value
33
+
34
+ val yy = value.substring(0, 2)
35
+ val mm = value.substring(2, 4)
36
+ val dd = value.substring(4, 6)
37
+
38
+ return "$dd/$mm/20$yy"
39
+ }
40
+
41
+ /**
42
+ * Ghép primaryIdentifier + secondaryIdentifier từ MRZ thành họ tên.
43
+ * Thay ký tự '<' bằng khoảng trắng và chuẩn hóa.
44
+ */
45
+ fun normalizeName(primary: String?, secondary: String?): String {
46
+ return listOf(primary, secondary)
47
+ .filterNotNull()
48
+ .joinToString(" ")
49
+ .replace("<", " ")
50
+ .replace(Regex("\\s+"), " ")
51
+ .trim()
52
+ }
53
+
54
+ /**
55
+ * Chuẩn hóa giới tính từ MRZ sang tiếng Việt.
56
+ */
57
+ fun normalizeGender(raw: String): String {
58
+ return when (raw.trim().uppercase(Locale.ROOT)) {
59
+ "M", "MALE", "NAM" -> "Nam"
60
+ "F", "FEMALE", "NU" -> "Nữ"
61
+ else -> "Khác"
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Chuẩn hóa quốc tịch từ mã ISO 3166 sang tiếng Việt.
67
+ */
68
+ fun normalizeNationality(raw: String?): String {
69
+ return when (raw?.uppercase(Locale.ROOT)) {
70
+ "VNM" -> "Việt Nam"
71
+ null, "" -> "Việt Nam"
72
+ else -> raw
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:shape="rectangle">
4
+ <solid android:color="#FFF7ED" />
5
+ <corners
6
+ android:topLeftRadius="28dp"
7
+ android:topRightRadius="28dp"
8
+ android:bottomLeftRadius="24dp"
9
+ android:bottomRightRadius="24dp" />
10
+ <stroke
11
+ android:width="1dp"
12
+ android:color="#FED7AA" />
13
+ </shape>