@myazahq/kyc-sdk-react-native 2.4.0 → 2.6.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.
- package/README.md +151 -5
- package/android/build.gradle +35 -3
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaFaceDetector.kt +77 -0
- package/app.plugin.js +89 -8
- package/ios/HybridMyazaFaceDetector.swift +16 -3
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.cpp +23 -0
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.hpp +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaFaceDetectorSpec.kt +9 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.hpp +43 -0
- package/nitrogen/generated/ios/c++/HybridMyazaFaceDetectorSpecSwift.hpp +17 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec_cxx.swift +31 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.hpp +3 -0
- package/package.json +14 -2
- package/src/MyazaBiometricAuth.tsx +119 -0
- package/src/MyazaKYC.tsx +12 -0
- package/src/components/CountryField.tsx +8 -0
- package/src/components/CountryRegionPicker.tsx +184 -0
- package/src/components/DashedBorder.tsx +55 -0
- package/src/components/DialCodePicker.tsx +53 -67
- package/src/components/DialCodeRow.tsx +102 -0
- package/src/components/FramedMapPicker.tsx +122 -0
- package/src/components/GeoBadge.tsx +34 -0
- package/src/components/Icon.tsx +2 -176
- package/src/components/KycFlow.tsx +60 -32
- package/src/components/LineSkeleton.tsx +100 -0
- package/src/components/MapChrome.tsx +67 -0
- package/src/components/MapPinMarker.tsx +54 -0
- package/src/components/MapPinPicker.tsx +179 -0
- package/src/components/MilestoneTrack.tsx +155 -0
- package/src/components/MultiIdProgress.tsx +107 -0
- package/src/components/MyazaInput.tsx +39 -4
- package/src/components/MyazaSelect.tsx +2 -1
- package/src/components/OptionRow.tsx +22 -3
- package/src/components/OwnershipSlider.tsx +102 -0
- package/src/components/PhoneNumberInput.tsx +45 -4
- package/src/components/PoweredBy.tsx +5 -5
- package/src/components/PresenceBlocks.tsx +153 -0
- package/src/components/StaggerIn.tsx +63 -0
- package/src/components/StepView.tsx +43 -0
- package/src/components/StickyActions.tsx +56 -0
- package/src/components/dialCodeRows.ts +64 -0
- package/src/components/glass/FloatingSheet.tsx +73 -21
- package/src/components/glass/GlassSheet.tsx +14 -2
- package/src/components/icon-map.ts +176 -0
- package/src/components/stepHeaderMeta.tsx +45 -5
- package/src/config/addressCollection.ts +125 -0
- package/src/config/biometricOptions.ts +100 -0
- package/src/config/business.ts +26 -7
- package/src/config/businessDetailsValidity.ts +52 -0
- package/src/config/businessPrefill.ts +72 -0
- package/src/config/businessSteps.ts +20 -3
- package/src/config/consentStep.ts +19 -0
- package/src/config/keyPeople.ts +111 -7
- package/src/config/keyPeopleCorporate.ts +19 -0
- package/src/config/keyPeopleOwnerTypes.ts +12 -0
- package/src/config/keyPeopleOwners.ts +26 -0
- package/src/config/keyPeoplePrefill.ts +138 -0
- package/src/config/keyPeopleSectionDefs.ts +65 -0
- package/src/config/keyPeopleSections.ts +167 -0
- package/src/config/proofOfAddress.ts +54 -11
- package/src/config/regions.ts +24 -0
- package/src/config/registrationHint.ts +1 -1
- package/src/config/stepOrder.ts +100 -6
- package/src/config/theme.ts +8 -2
- package/src/config/uploadLimits.ts +35 -0
- package/src/config/website.ts +36 -0
- package/src/config/workflowMerge.ts +16 -5
- package/src/emrtd/activeAuth.ts +100 -0
- package/src/emrtd/extras.ts +76 -0
- package/src/emrtd/files.ts +5 -0
- package/src/emrtd/index.ts +1 -0
- package/src/emrtd/read.ts +8 -1
- package/src/emrtd/session.ts +41 -0
- package/src/emrtd/stages.ts +11 -0
- package/src/index.ts +54 -1
- package/src/lib/address-current-location.ts +171 -0
- package/src/lib/address-field-modes.ts +119 -0
- package/src/lib/address-flow.ts +172 -0
- package/src/lib/address-helpers.ts +48 -0
- package/src/lib/address-line.ts +76 -0
- package/src/lib/address-pin-move.ts +97 -0
- package/src/lib/address-step-recovery.ts +63 -0
- package/src/lib/authed-image.ts +78 -0
- package/src/lib/biometric-auth.ts +52 -0
- package/src/lib/biometric-copy.ts +51 -0
- package/src/lib/captureRing.ts +83 -0
- package/src/lib/contact-recovery.ts +44 -0
- package/src/lib/country-adoption.ts +89 -0
- package/src/lib/inferred-country.ts +65 -0
- package/src/lib/livenessLayout.ts +49 -0
- package/src/lib/map-frame.ts +159 -0
- package/src/lib/map-tiles.ts +143 -0
- package/src/lib/multi-id.ts +210 -0
- package/src/lib/poa-country-gate.ts +27 -0
- package/src/lib/resubmit.ts +130 -0
- package/src/lib/result-copy.ts +116 -0
- package/src/lib/result-wait.ts +53 -0
- package/src/lib/review-map-surface.ts +26 -0
- package/src/lib/scope.ts +31 -0
- package/src/lib/screen-corners.ts +50 -0
- package/src/lib/selfie-upload-wait.ts +79 -0
- package/src/lib/step-log.ts +24 -3
- package/src/lib/street-view-fov.ts +42 -0
- package/src/lib/use-multi-id-plan.ts +29 -0
- package/src/lib/webview-available.ts +47 -0
- package/src/liveness/useLiveness.ts +6 -1
- package/src/liveness/useModelReady.ts +65 -0
- package/src/liveness/visionCameraFaceDetector.ts +35 -0
- package/src/presence/background-math.ts +100 -0
- package/src/presence/background-store.ts +82 -0
- package/src/presence/background.ts +165 -0
- package/src/presence/foreground-service.ts +193 -0
- package/src/presence/fs.ts +51 -0
- package/src/presence/geofence.ts +41 -0
- package/src/presence/math.ts +44 -0
- package/src/presence/post.ts +40 -0
- package/src/presence/report.ts +87 -0
- package/src/presence/sampler.ts +110 -0
- package/src/presence/status.ts +92 -0
- package/src/presence/store.ts +94 -0
- package/src/presence/tier.ts +36 -0
- package/src/presence/watch-wait.ts +81 -0
- package/src/screens/AddressCountryControl.tsx +115 -0
- package/src/screens/ApplicantRoleStep.tsx +9 -5
- package/src/screens/BusinessCheckPanel.tsx +89 -0
- package/src/screens/BusinessDetailsFields.tsx +112 -0
- package/src/screens/BusinessDetailsStep.tsx +133 -145
- package/src/screens/BusinessDocumentSlot.tsx +5 -5
- package/src/screens/BusinessDocumentsStep.tsx +5 -56
- package/src/screens/BusinessKeyPeopleStep.tsx +103 -71
- package/src/screens/BusinessPickedCard.tsx +86 -0
- package/src/screens/BusinessPickedSection.tsx +51 -0
- package/src/screens/BusinessRegistryPickers.tsx +67 -0
- package/src/screens/BusinessSandboxToggle.tsx +114 -0
- package/src/screens/BusinessSearch.tsx +199 -0
- package/src/screens/BusinessSearchResults.tsx +115 -0
- package/src/screens/CompanyInfoFields.tsx +91 -51
- package/src/screens/ConsentStep.tsx +37 -17
- package/src/screens/ContactActions.tsx +54 -0
- package/src/screens/ContactDestinationField.tsx +5 -0
- package/src/screens/ContactEntryPanel.tsx +75 -0
- package/src/screens/ContactVerificationStep.tsx +43 -42
- package/src/screens/CountrySelectStep.tsx +13 -147
- package/src/screens/IdTypeStep.tsx +12 -5
- package/src/screens/KeepLinksSheet.tsx +88 -0
- package/src/screens/KeyPeopleAwaitCard.tsx +204 -0
- package/src/screens/KeyPeopleAwaitList.tsx +126 -0
- package/src/screens/KeyPeoplePending.tsx +154 -0
- package/src/screens/KeyPeopleSection.tsx +162 -0
- package/src/screens/KeyPeopleSectionsList.tsx +103 -0
- package/src/screens/KeyPeopleUboExemption.tsx +72 -0
- package/src/screens/KeyPersonCard.tsx +36 -4
- package/src/screens/KeyPersonForm.tsx +195 -36
- package/src/screens/KeyPersonKindToggle.tsx +68 -0
- package/src/screens/KeyPersonOwners.tsx +109 -0
- package/src/screens/KeyPersonRoleChips.tsx +68 -0
- package/src/screens/KeyPersonSheet.tsx +92 -108
- package/src/screens/LivenessAvatar.tsx +15 -3
- package/src/screens/LivenessStep.tsx +172 -52
- package/src/screens/NfcStep.tsx +14 -0
- package/src/screens/PoaDocumentTypeList.tsx +66 -0
- package/src/screens/ProofOfAddressParts.tsx +151 -0
- package/src/screens/ProofOfAddressStep.tsx +58 -96
- package/src/screens/SubmittedBadge.tsx +25 -0
- package/src/screens/SubmittedError.tsx +64 -0
- package/src/screens/SubmittedResult.tsx +117 -0
- package/src/screens/SubmittedStep.tsx +82 -119
- package/src/screens/SubmittedSuccess.tsx +127 -0
- package/src/screens/SubmittedWaiting.tsx +45 -0
- package/src/screens/address/AddressEntranceStep.tsx +161 -0
- package/src/screens/address/AddressIntroGate.tsx +148 -0
- package/src/screens/address/AddressMapStub.tsx +50 -0
- package/src/screens/address/AddressPinStep.tsx +178 -0
- package/src/screens/address/AddressReviewStep.tsx +162 -0
- package/src/screens/address/AddressSandboxTabs.tsx +158 -0
- package/src/screens/address/AddressSearchField.tsx +115 -0
- package/src/screens/address/AddressSearchStep.tsx +94 -0
- package/src/screens/address/CurrentLocationRow.tsx +135 -0
- package/src/screens/address/DetailsSheet.tsx +175 -0
- package/src/screens/address/DetailsSheetFields.tsx +184 -0
- package/src/screens/address/EntranceDropzone.tsx +178 -0
- package/src/screens/address/EntranceFraming.tsx +66 -0
- package/src/screens/address/EntrancePills.tsx +60 -0
- package/src/screens/address/FramedStreetView.tsx +155 -0
- package/src/screens/address/IntroDisclosures.tsx +210 -0
- package/src/screens/address/LabelDecisionRow.tsx +113 -0
- package/src/screens/address/PinSummaryRow.tsx +105 -0
- package/src/screens/address/ReviewAddressBand.tsx +111 -0
- package/src/screens/address/ReviewEntranceThumbs.tsx +64 -0
- package/src/screens/address/ReviewMapPicture.tsx +106 -0
- package/src/screens/address/SearchResults.tsx +105 -0
- package/src/screens/address/SearchScreen.tsx +176 -0
- package/src/screens/address/SkipForNow.tsx +43 -0
- package/src/screens/address/StreetViewChrome.tsx +81 -0
- package/src/screens/address/detail-values.ts +22 -0
- package/src/screens/address/fix-source.ts +27 -0
- package/src/screens/address/index.ts +8 -0
- package/src/screens/address/meta.ts +44 -0
- package/src/screens/address/use-address-flow.ts +200 -0
- package/src/screens/address/use-label-pin.ts +80 -0
- package/src/screens/address/use-pin-actions.ts +192 -0
- package/src/screens/biometric/BiometricAuthFlow.tsx +170 -0
- package/src/screens/consent/model.ts +79 -10
- package/src/screens/liveness/CaptureRing.tsx +91 -0
- package/src/screens/liveness/LivenessCamera.tsx +63 -0
- package/src/screens/liveness/LivenessHandover.tsx +43 -0
- package/src/screens/liveness/LivenessOutcome.tsx +4 -3
- package/src/screens/liveness/SelfiePreview.tsx +13 -4
- package/src/screens/liveness/ShutterFlash.tsx +25 -0
- package/src/screens/liveness/index.ts +4 -0
- package/src/screens/liveness/useSelfieUpload.ts +23 -3
- package/src/screens/nfc/NfcReadProgress.tsx +1 -0
- package/src/screens/nfc/NfcScanIllustration.tsx +7 -18
- package/src/screens/useAddressPhotoAttach.ts +77 -0
- package/src/screens/useAwaitingPeople.ts +90 -0
- package/src/screens/useBusinessDocumentAttach.ts +93 -0
- package/src/screens/usePoaAttach.ts +12 -15
- package/src/services/api-biometric.ts +37 -0
- package/src/services/api-types-biometric.ts +41 -0
- package/src/services/api-types.ts +251 -3
- package/src/services/api-verify-types.ts +90 -1
- package/src/services/api.ts +237 -3
- package/src/services/deviceMetadata.ts +1 -1
- package/src/services/errors.ts +10 -2
- package/src/services/location.ts +234 -0
- package/src/services/uploadErrors.ts +30 -0
- package/src/services/workflowGate.ts +8 -0
- package/src/specs/MyazaFaceDetector.nitro.ts +29 -0
- package/src/store/address.ts +97 -0
- package/src/store/businessCheck.ts +113 -0
- package/src/store/derive.ts +44 -4
- package/src/store/kycStore.ts +352 -16
- package/src/store/serverConfig.ts +20 -0
- package/src/store/session.ts +227 -0
- package/src/store/state.ts +289 -2
- package/src/store/submit.ts +80 -13
- package/src/store/submitApplicant.ts +5 -11
- package/src/types/business.ts +23 -2
- package/src/types/config.ts +70 -2
- package/src/types/verification.ts +13 -1
- package/src/types/workflow.ts +112 -1
- package/src/screens/KeyPeopleInviteLinks.tsx +0 -239
package/README.md
CHANGED
|
@@ -56,8 +56,11 @@ npx expo install @myazahq/kyc-sdk-react-native \
|
|
|
56
56
|
react-native-safe-area-context react-native-svg
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Add the config plugins to `app.json`. The SDK's plugin adds the iOS camera
|
|
60
|
-
|
|
59
|
+
Add the config plugins to `app.json`. The SDK's plugin adds the iOS camera and
|
|
60
|
+
location usage strings + Android `CAMERA`/`INTERNET` and foreground location
|
|
61
|
+
permissions (location backs the address-collection step's "Use my current
|
|
62
|
+
location" and presence attestation; `["@myazahq/kyc-sdk-react-native",
|
|
63
|
+
{ "location": false }]` opts out); the VisionCamera plugin wires up
|
|
61
64
|
the camera + frame processors. Make sure the **New Architecture** is on (it is by
|
|
62
65
|
default on Expo SDK 56):
|
|
63
66
|
|
|
@@ -82,8 +85,9 @@ npx expo run:ios # iOS
|
|
|
82
85
|
JAVA_HOME=/path/to/jdk-17 npx expo run:android # Android — needs JDK 17
|
|
83
86
|
```
|
|
84
87
|
|
|
85
|
-
> The SDK plugin accepts
|
|
86
|
-
> `["@myazahq/kyc-sdk-react-native", { "cameraPermission": "Your message…"
|
|
88
|
+
> The SDK plugin accepts optional custom prompts:
|
|
89
|
+
> `["@myazahq/kyc-sdk-react-native", { "cameraPermission": "Your message…",
|
|
90
|
+
> "locationPermission": "Your message…" }]`.
|
|
87
91
|
|
|
88
92
|
### Bare React Native app (no Expo prebuild)
|
|
89
93
|
|
|
@@ -100,7 +104,8 @@ npm install @myazahq/kyc-sdk-react-native \
|
|
|
100
104
|
react-native-worklets react-native-nitro-modules react-native-nitro-image \
|
|
101
105
|
react-native-safe-area-context react-native-svg \
|
|
102
106
|
expo expo-image-manipulator expo-image-picker expo-speech expo-font \
|
|
103
|
-
expo-glass-effect expo-application expo-crypto expo-device expo-localization
|
|
107
|
+
expo-glass-effect expo-application expo-crypto expo-device expo-localization \
|
|
108
|
+
expo-location
|
|
104
109
|
|
|
105
110
|
# 3. iOS pods:
|
|
106
111
|
cd ios && pod install && cd ..
|
|
@@ -113,11 +118,15 @@ prebuild):
|
|
|
113
118
|
```xml
|
|
114
119
|
<key>NSCameraUsageDescription</key>
|
|
115
120
|
<string>We use the camera to photograph your ID and capture a live selfie.</string>
|
|
121
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
122
|
+
<string>We use your location to confirm you are at the address you pin during verification.</string>
|
|
116
123
|
```
|
|
117
124
|
- **Android** — add to `android/app/src/main/AndroidManifest.xml`:
|
|
118
125
|
```xml
|
|
119
126
|
<uses-permission android:name="android.permission.CAMERA" />
|
|
120
127
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
128
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
129
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
121
130
|
```
|
|
122
131
|
- Ensure the **New Architecture** is enabled (`newArchEnabled=true` in
|
|
123
132
|
`android/gradle.properties`; `RCT_NEW_ARCH_ENABLED=1` for the iOS Podfile install),
|
|
@@ -560,3 +569,140 @@ Full documentation, configuration options, and webhook setup: **[trust.myaza.co/
|
|
|
560
569
|
## License
|
|
561
570
|
|
|
562
571
|
MIT © Flitstack Technologies Inc.
|
|
572
|
+
|
|
573
|
+
## Presence reporting (Address Intelligence)
|
|
574
|
+
|
|
575
|
+
When a workflow enables presence verification (`addressCollection.presence.enabled`),
|
|
576
|
+
the SDK stores the confirmed pin on-device at capture. Call the reporter from your
|
|
577
|
+
app on a natural moment (app open works well):
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
import { reportAddressPresence, clearPresencePin } from '@myazahq/kyc-sdk-react-native';
|
|
581
|
+
|
|
582
|
+
const result = await reportAddressPresence({
|
|
583
|
+
apiKey: 'pk_live_…',
|
|
584
|
+
externalUserId: 'user_42', // the same userId the KYC flow ran with
|
|
585
|
+
});
|
|
586
|
+
// result.reason: 'reported' | 'no_pin' | 'services_off' | 'no_fix' | 'outside_fence' | 'network_error'
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
It never throws and never blocks startup. The geofence is evaluated ON-DEVICE:
|
|
590
|
+
only the derived record (calendar day + a night flag) is transmitted, never a
|
|
591
|
+
coordinate. A fix outside the fence sends nothing (the server scores presence,
|
|
592
|
+
never absence); a mock-location fix is reported flagged. `clearPresencePin`
|
|
593
|
+
drops the stored pin (sign-out, or once the watch resolves).
|
|
594
|
+
|
|
595
|
+
### Background monitoring (OS geofencing)
|
|
596
|
+
|
|
597
|
+
The stronger tier: the OS wakes the SDK on fence crossings around the stored
|
|
598
|
+
pin, app closed or not, so dwell and nights accrue with nobody in the loop.
|
|
599
|
+
Entries stamp a timestamp; exits fold the dwell span into per-day aggregates
|
|
600
|
+
and flush them. As with the foreground tier, only the derived day records
|
|
601
|
+
ever leave the phone.
|
|
602
|
+
|
|
603
|
+
Three opt-ins, each deliberate:
|
|
604
|
+
|
|
605
|
+
1. Install the optional peer: `npx expo install expo-task-manager` (without
|
|
606
|
+
it the background tier simply does not exist — never a crash).
|
|
607
|
+
2. Declare background location via the config plugin — this is what changes
|
|
608
|
+
your app's store review posture, so it is never a default:
|
|
609
|
+
|
|
610
|
+
```json
|
|
611
|
+
["@myazahq/kyc-sdk-react-native", { "location": "always" }]
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
3. Register the task at your app's ROOT module (before the component tree —
|
|
615
|
+
a task defined inside a component never fires headlessly), then enable:
|
|
616
|
+
|
|
617
|
+
```tsx
|
|
618
|
+
// index.js
|
|
619
|
+
import { registerBackgroundPresence } from '@myazahq/kyc-sdk-react-native';
|
|
620
|
+
registerBackgroundPresence();
|
|
621
|
+
|
|
622
|
+
// later, after the KYC flow stored a pin:
|
|
623
|
+
const result = await enableBackgroundPresence({
|
|
624
|
+
apiKey: 'pk_live_…',
|
|
625
|
+
externalUserId: 'user_42',
|
|
626
|
+
});
|
|
627
|
+
// result.reason: 'enabled' | 'module_missing' | 'no_pin'
|
|
628
|
+
// | 'foreground_denied' | 'background_denied' | 'start_failed'
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
`disableBackgroundPresence()` disarms the fence. A permission refusal leaves
|
|
632
|
+
the foreground tier working exactly as before — the tiers degrade, never
|
|
633
|
+
break.
|
|
634
|
+
|
|
635
|
+
### The Android foreground service (reliability on OEM-managed phones)
|
|
636
|
+
|
|
637
|
+
A geofence alone is not reliable on Android once a manufacturer's battery
|
|
638
|
+
manager decides your app is idle: transitions are dropped, nothing says so,
|
|
639
|
+
and the watch quietly lapses to inconclusive. The phones on that list (Tecno,
|
|
640
|
+
Infinix, itel, Xiaomi, Oppo, Vivo) are the ones the market carries. A
|
|
641
|
+
foreground service, with its persistent notification, is the one thing those
|
|
642
|
+
managers leave alone — and OkHi's own integration guidance for the same
|
|
643
|
+
markets is exactly this.
|
|
644
|
+
|
|
645
|
+
Opt-in, Android only (iOS region monitoring is reliable on its own), on the
|
|
646
|
+
same `location: "always"` plugin setting, which also declares the
|
|
647
|
+
`FOREGROUND_SERVICE` and `FOREGROUND_SERVICE_LOCATION` permissions it needs.
|
|
648
|
+
`registerBackgroundPresence()` at the root already defines its task; then:
|
|
649
|
+
|
|
650
|
+
```tsx
|
|
651
|
+
const result = await enableForegroundService({
|
|
652
|
+
apiKey: 'pk_live_…',
|
|
653
|
+
externalUserId: 'user_42',
|
|
654
|
+
notification: {
|
|
655
|
+
title: 'Address verification in progress', // shown in the status bar
|
|
656
|
+
body: 'Open the app to see your progress',
|
|
657
|
+
color: '#5645F5',
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
// result.reason: 'enabled' | 'unsupported_platform' | 'module_missing' | 'no_pin'
|
|
661
|
+
// | 'foreground_denied' | 'background_denied' | 'start_failed'
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
While it runs, a low-power fix every ten minutes (or hundred metres) is
|
|
665
|
+
turned into the same enter/exit spans the geofence folds, on the same stored
|
|
666
|
+
state, so the two never double-count a stay; the queue flushes while the
|
|
667
|
+
process is alive; and a fence the OS dropped (a location toggle clears every
|
|
668
|
+
registered fence) is re-armed. `disableForegroundService()` stops it and its
|
|
669
|
+
notification. Word the notification honestly — it is on screen for days.
|
|
670
|
+
|
|
671
|
+
### Which tier is running?
|
|
672
|
+
|
|
673
|
+
Permissions get revoked in Settings and nothing tells the app. Ask:
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
import { presenceStatus, openLocationSettings } from '@myazahq/kyc-sdk-react-native';
|
|
677
|
+
|
|
678
|
+
const status = await presenceStatus('user_42');
|
|
679
|
+
// status.tier: 'background' | 'foreground' | 'none'
|
|
680
|
+
// plus pinStored, alwaysOn, locationServicesEnabled, both permission states,
|
|
681
|
+
// geofenceArmed, foregroundServiceRunning
|
|
682
|
+
if (!status.locationServicesEnabled) {
|
|
683
|
+
// The phone's location toggle is off: permission granted or not, no fix
|
|
684
|
+
// can be taken. Android deep-links to the toggle itself.
|
|
685
|
+
await openLocationSettings('services');
|
|
686
|
+
} else if (status.tier === 'none' && status.pinStored) {
|
|
687
|
+
// The road back runs through Settings — no OS allows re-prompting in-app.
|
|
688
|
+
await openLocationSettings();
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Showing the person where the check stands
|
|
693
|
+
|
|
694
|
+
Somebody kept from a feature until their address is verified should be able
|
|
695
|
+
to see the progress in your app, without a webhook relayed through your
|
|
696
|
+
backend. The status endpoint is publishable-safe and enumeration-safe (an
|
|
697
|
+
unknown user and a user with no watch answer the same `not_started` shape):
|
|
698
|
+
|
|
699
|
+
```ts
|
|
700
|
+
const res = await fetch(`${serverUrl}/api/kyc/address/presence/${externalUserId}`, {
|
|
701
|
+
headers: { Authorization: `Bearer pk_live_…` },
|
|
702
|
+
});
|
|
703
|
+
const { status, progress, tier } = await res.json();
|
|
704
|
+
// status: 'not_started' | 'in_progress' | 'verified' | 'failed' | 'inconclusive' | 'expired' | 'revoked'
|
|
705
|
+
// progress.score: 0..1 on WEIGHTED evidence (five foreground nights and three
|
|
706
|
+
// geofence nights both read 1); nightsObserved/daysObserved beside it
|
|
707
|
+
// tier: 'background' | 'foreground' | null — what is actually feeding it
|
|
708
|
+
```
|
package/android/build.gradle
CHANGED
|
@@ -109,8 +109,40 @@ dependencies {
|
|
|
109
109
|
// CameraX core — for the ImageProxy / ExperimentalGetImage APIs.
|
|
110
110
|
compileOnly 'androidx.camera:camera-core:1.3.4'
|
|
111
111
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
//
|
|
112
|
+
// ── ML Kit: models fetched on demand by default ──────────────────────────
|
|
113
|
+
//
|
|
114
|
+
// Two variants of the same API exist. The `com.google.mlkit:*` artifacts embed
|
|
115
|
+
// the models in the APK; the `com.google.android.gms:play-services-mlkit-*`
|
|
116
|
+
// ones fetch them through Google Play Services on first use.
|
|
117
|
+
//
|
|
118
|
+
// Measured on a real integrator's release APK, the bundled pair cost
|
|
119
|
+
// **18.5 MB per device** (arm64: ocr_pipeline 10.55 MB + face_detector 7.95 MB)
|
|
120
|
+
// plus the .tflite files in assets/, which ship to EVERY device because assets
|
|
121
|
+
// are not split by ABI. Unbundled, that is ~0.4 MB of shims.
|
|
122
|
+
//
|
|
123
|
+
// The trade-off is real and is why the flag exists: the unbundled variants need
|
|
124
|
+
// Google Play Services, so they do not work on Huawei or AOSP builds without
|
|
125
|
+
// GMS. An org shipping to those devices sets `myazaKycBundledMlKit = true` in
|
|
126
|
+
// its root build.gradle ext and gets today's fully-offline behaviour back.
|
|
127
|
+
//
|
|
128
|
+
// Availability is NOT inferred per frame — `isModelReady()` is checked once
|
|
129
|
+
// before the camera opens (see HybridMyazaFaceDetector.kt), because a missing
|
|
130
|
+
// model would otherwise be indistinguishable from "no face in frame".
|
|
131
|
+
if (safeExtGet('myazaKycBundledMlKit', false).toString().toBoolean()) {
|
|
132
|
+
implementation 'com.google.mlkit:face-detection:16.1.6'
|
|
133
|
+
} else {
|
|
134
|
+
implementation 'com.google.android.gms:play-services-mlkit-face-detection:17.1.0'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Text recognition is STILL BUNDLED, deliberately, and it is the larger of the
|
|
138
|
+
// two (10.55 MB vs 7.95 MB on arm64) — so this is the bigger half of the win,
|
|
139
|
+
// deferred rather than abandoned.
|
|
140
|
+
//
|
|
141
|
+
// Unbundling it needs the same isModelReady()/prepareModel() contract the face
|
|
142
|
+
// detector just gained. Without that, a missing OCR model is indistinguishable
|
|
143
|
+
// from "no text in frame": document auto-capture would simply never fire and
|
|
144
|
+
// MRZ scanning would never produce a key for the chip read, with nothing shown
|
|
145
|
+
// to the user. Shipping the dependency swap ahead of the availability handling
|
|
146
|
+
// would trade 10 MB for a silent failure on every fresh install.
|
|
115
147
|
implementation 'com.google.mlkit:text-recognition:16.0.1'
|
|
116
148
|
}
|
|
@@ -9,6 +9,9 @@ import com.google.mlkit.vision.face.Face
|
|
|
9
9
|
import com.google.mlkit.vision.face.FaceDetection
|
|
10
10
|
import com.google.mlkit.vision.face.FaceDetector
|
|
11
11
|
import com.google.mlkit.vision.face.FaceDetectorOptions
|
|
12
|
+
import com.google.android.gms.common.moduleinstall.ModuleInstall
|
|
13
|
+
import com.margelo.nitro.NitroModules
|
|
14
|
+
import com.margelo.nitro.core.Promise
|
|
12
15
|
import java.util.concurrent.CountDownLatch
|
|
13
16
|
import java.util.concurrent.TimeUnit
|
|
14
17
|
|
|
@@ -39,6 +42,70 @@ class HybridMyazaFaceDetector : HybridMyazaFaceDetectorSpec() {
|
|
|
39
42
|
.build(),
|
|
40
43
|
)
|
|
41
44
|
|
|
45
|
+
// ── Model availability ───────────────────────────────────────────────────
|
|
46
|
+
//
|
|
47
|
+
// The default build fetches ML Kit's models through Play Services instead of
|
|
48
|
+
// bundling them (android/build.gradle), which is what keeps ~18.5 MB per
|
|
49
|
+
// device out of the APK. The cost is a window where detection cannot run:
|
|
50
|
+
// first launch before the download completes, or a device with no GMS at all.
|
|
51
|
+
//
|
|
52
|
+
// `detectFace` CANNOT report that. Its only channel is FaceResult, where a
|
|
53
|
+
// missing model and an empty frame are both `faceCount: 0` — so a user on a
|
|
54
|
+
// fresh install would watch "position your face" forever with nothing to
|
|
55
|
+
// explain it. Hence a separate, explicit contract, checked before the camera
|
|
56
|
+
// opens rather than inferred per frame.
|
|
57
|
+
//
|
|
58
|
+
// Cached rather than queried live: `areModulesAvailable` is asynchronous and
|
|
59
|
+
// `isModelReady` is a synchronous Nitro call, so blocking the worklet thread
|
|
60
|
+
// to answer it would be worse than the problem. The flag starts false and is
|
|
61
|
+
// set by prepareModel(), which the SDK primes at flow start.
|
|
62
|
+
@Volatile private var modelReady = false
|
|
63
|
+
|
|
64
|
+
override fun isModelReady(): Boolean = modelReady
|
|
65
|
+
|
|
66
|
+
override fun prepareModel(): Promise<Boolean> = Promise.async {
|
|
67
|
+
if (modelReady) return@async true
|
|
68
|
+
|
|
69
|
+
val ctx = NitroModules.applicationContext
|
|
70
|
+
?: return@async false // No context — cannot ask Play Services anything.
|
|
71
|
+
|
|
72
|
+
val latch = CountDownLatch(1)
|
|
73
|
+
var ok = false
|
|
74
|
+
try {
|
|
75
|
+
val client = ModuleInstall.getClient(ctx)
|
|
76
|
+
// areModulesAvailable() answers "is it already here"; deferredInstall()
|
|
77
|
+
// asks Play Services to fetch it in the background if not. Requesting the
|
|
78
|
+
// install unconditionally is simpler AND correct — it is a no-op when the
|
|
79
|
+
// module is already present, and starting it early is the entire point.
|
|
80
|
+
client.areModulesAvailable(detector)
|
|
81
|
+
.addOnSuccessListener { response ->
|
|
82
|
+
ok = response.areModulesAvailable()
|
|
83
|
+
if (!ok) {
|
|
84
|
+
// `deferredInstall` takes the OptionalModuleApi itself, varargs.
|
|
85
|
+
// A ModuleInstallRequest belongs to `installModules`, which is the
|
|
86
|
+
// FOREGROUND install with its own progress listener — a different
|
|
87
|
+
// call, and the one this used to hand its request to, which is why
|
|
88
|
+
// the Android build stopped compiling.
|
|
89
|
+
client.deferredInstall(detector)
|
|
90
|
+
}
|
|
91
|
+
latch.countDown()
|
|
92
|
+
}
|
|
93
|
+
.addOnFailureListener {
|
|
94
|
+
// Thrown on devices without Google Play Services at all (Huawei, bare
|
|
95
|
+
// AOSP). Not an error to retry — it will never succeed on this device.
|
|
96
|
+
// Such orgs should build with `myazaKycBundledMlKit = true`.
|
|
97
|
+
ok = false
|
|
98
|
+
latch.countDown()
|
|
99
|
+
}
|
|
100
|
+
latch.await(MODEL_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
|
101
|
+
} catch (_: Throwable) {
|
|
102
|
+
ok = false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
modelReady = ok
|
|
106
|
+
ok
|
|
107
|
+
}
|
|
108
|
+
|
|
42
109
|
@ExperimentalGetImage
|
|
43
110
|
override fun detectFace(frame: HybridFrameSpec): FaceResult {
|
|
44
111
|
// Same access pattern as iOS (cast to the public NativeFrame): VisionCamera's
|
|
@@ -218,4 +285,14 @@ class HybridMyazaFaceDetector : HybridMyazaFaceDetectorSpec() {
|
|
|
218
285
|
faceG = -1.0,
|
|
219
286
|
faceB = -1.0,
|
|
220
287
|
)
|
|
288
|
+
|
|
289
|
+
private companion object {
|
|
290
|
+
/**
|
|
291
|
+
* Bound on the availability query. It is a local Play Services call, not the
|
|
292
|
+
* model download — the download runs in the background afterwards. Generous
|
|
293
|
+
* enough for a cold Play Services process, short enough that a wedged one
|
|
294
|
+
* cannot stall flow start.
|
|
295
|
+
*/
|
|
296
|
+
const val MODEL_CHECK_TIMEOUT_MS = 3_000L
|
|
297
|
+
}
|
|
221
298
|
}
|
package/app.plugin.js
CHANGED
|
@@ -37,6 +37,9 @@ const CAMERA_USAGE =
|
|
|
37
37
|
const NFC_USAGE =
|
|
38
38
|
'We read the secure chip in your passport or ID card to confirm the document is genuine.';
|
|
39
39
|
|
|
40
|
+
const LOCATION_USAGE =
|
|
41
|
+
'We use your location to confirm you are at the address you pin during verification.';
|
|
42
|
+
|
|
40
43
|
/**
|
|
41
44
|
* The eMRTD application identifier.
|
|
42
45
|
*
|
|
@@ -62,6 +65,23 @@ function withIosCameraUsage(config, customMessage) {
|
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
/**
|
|
69
|
+
* iOS location usage string, for the address-collection step's one-shot fix
|
|
70
|
+
* ("Use my current location" + the attest-presence claim). Written by DEFAULT
|
|
71
|
+
* because its ABSENCE is a crash: iOS kills an app that requests location
|
|
72
|
+
* without a usage string, and whether the step runs is decided by the org's
|
|
73
|
+
* WORKFLOW, not by anything the host app author can see at build time. An
|
|
74
|
+
* unused usage string costs nothing. `location: false` opts out.
|
|
75
|
+
*/
|
|
76
|
+
function withIosLocationUsage(config, customMessage) {
|
|
77
|
+
return withInfoPlist(config, (cfg) => {
|
|
78
|
+
if (!cfg.modResults.NSLocationWhenInUseUsageDescription) {
|
|
79
|
+
cfg.modResults.NSLocationWhenInUseUsageDescription = customMessage || LOCATION_USAGE;
|
|
80
|
+
}
|
|
81
|
+
return cfg;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
65
85
|
const AID_KEY = 'com.apple.developer.nfc.readersession.iso7816.select-identifiers';
|
|
66
86
|
const FORMATS_KEY = 'com.apple.developer.nfc.readersession.formats';
|
|
67
87
|
|
|
@@ -112,24 +132,84 @@ function withIosNfc(config, customMessage) {
|
|
|
112
132
|
});
|
|
113
133
|
}
|
|
114
134
|
|
|
115
|
-
|
|
135
|
+
/**
|
|
136
|
+
* The Android permission list for a given set of opt-ins. Pure and exported
|
|
137
|
+
* so the tests can pin what `location: 'always'` declares — a permission
|
|
138
|
+
* that changes review posture must never arrive by accident.
|
|
139
|
+
*/
|
|
140
|
+
function androidPermissionsFor({ nfc, location, backgroundLocation }) {
|
|
141
|
+
return [
|
|
142
|
+
'android.permission.CAMERA',
|
|
143
|
+
'android.permission.INTERNET',
|
|
144
|
+
...(nfc ? ['android.permission.NFC'] : []),
|
|
145
|
+
// Foreground location, for the address-collection step's one-shot fix.
|
|
146
|
+
...(location
|
|
147
|
+
? ['android.permission.ACCESS_COARSE_LOCATION', 'android.permission.ACCESS_FINE_LOCATION']
|
|
148
|
+
: []),
|
|
149
|
+
// The always-on presence tier (OkHi model, opt-in): OS geofencing needs
|
|
150
|
+
// the background grant, and Google Play reviews any app that declares
|
|
151
|
+
// it — which is why this is `location: 'always'` only, never a default.
|
|
152
|
+
// The foreground-service pair rides the same opt-in: FOREGROUND_SERVICE
|
|
153
|
+
// is a normal permission, and FOREGROUND_SERVICE_LOCATION (API 34+) is
|
|
154
|
+
// what lets expo-location's service declare the `location` type; Play
|
|
155
|
+
// reviews it under the same location declaration.
|
|
156
|
+
...(backgroundLocation
|
|
157
|
+
? [
|
|
158
|
+
'android.permission.ACCESS_BACKGROUND_LOCATION',
|
|
159
|
+
'android.permission.FOREGROUND_SERVICE',
|
|
160
|
+
'android.permission.FOREGROUND_SERVICE_LOCATION',
|
|
161
|
+
]
|
|
162
|
+
: []),
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function withAndroidPermissions(config, opts) {
|
|
116
167
|
return withAndroidManifest(config, (cfg) => {
|
|
117
|
-
AndroidConfig.Permissions.ensurePermissions(cfg.modResults,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
168
|
+
AndroidConfig.Permissions.ensurePermissions(cfg.modResults, androidPermissionsFor(opts));
|
|
169
|
+
return cfg;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** iOS side of the always-on tier: the Always usage string plus the location
|
|
174
|
+
* background mode, without which a geofence wake is refused. */
|
|
175
|
+
function withIosBackgroundLocation(config, customMessage) {
|
|
176
|
+
return withInfoPlist(config, (cfg) => {
|
|
177
|
+
if (!cfg.modResults.NSLocationAlwaysAndWhenInUseUsageDescription) {
|
|
178
|
+
cfg.modResults.NSLocationAlwaysAndWhenInUseUsageDescription =
|
|
179
|
+
customMessage || LOCATION_USAGE;
|
|
180
|
+
}
|
|
181
|
+
const modes = Array.isArray(cfg.modResults.UIBackgroundModes)
|
|
182
|
+
? cfg.modResults.UIBackgroundModes
|
|
183
|
+
: [];
|
|
184
|
+
if (!modes.includes('location')) modes.push('location');
|
|
185
|
+
cfg.modResults.UIBackgroundModes = modes;
|
|
122
186
|
return cfg;
|
|
123
187
|
});
|
|
124
188
|
}
|
|
125
189
|
|
|
126
190
|
/**
|
|
127
191
|
* @param {object} config
|
|
128
|
-
* @param {{ cameraPermission?: string, nfc?: boolean, nfcPermission?: string
|
|
192
|
+
* @param {{ cameraPermission?: string, nfc?: boolean, nfcPermission?: string,
|
|
193
|
+
* location?: boolean | 'always', locationPermission?: string }} [props]
|
|
129
194
|
*/
|
|
130
195
|
function withMyazaKyc(config, props = {}) {
|
|
196
|
+
const location = props.location !== false;
|
|
197
|
+
// `location: 'always'` opts into the background presence tier. Explicit on
|
|
198
|
+
// purpose: it changes the app's Play/App Store review posture, so it must
|
|
199
|
+
// never arrive as a side effect of a default.
|
|
200
|
+
const backgroundLocation = props.location === 'always';
|
|
131
201
|
config = withIosCameraUsage(config, props.cameraPermission);
|
|
132
|
-
config = withAndroidPermissions(config, {
|
|
202
|
+
config = withAndroidPermissions(config, {
|
|
203
|
+
nfc: props.nfc === true,
|
|
204
|
+
location,
|
|
205
|
+
backgroundLocation,
|
|
206
|
+
});
|
|
207
|
+
if (location) {
|
|
208
|
+
config = withIosLocationUsage(config, props.locationPermission);
|
|
209
|
+
}
|
|
210
|
+
if (backgroundLocation) {
|
|
211
|
+
config = withIosBackgroundLocation(config, props.locationPermission);
|
|
212
|
+
}
|
|
133
213
|
if (props.nfc === true) {
|
|
134
214
|
config = withIosNfc(config, props.nfcPermission);
|
|
135
215
|
}
|
|
@@ -141,4 +221,5 @@ module.exports = createRunOncePlugin(withMyazaKyc, pkg.name, pkg.version);
|
|
|
141
221
|
// consumers' app.json entries are unaffected.
|
|
142
222
|
module.exports.applyNfcInfoPlist = applyNfcInfoPlist;
|
|
143
223
|
module.exports.applyNfcEntitlements = applyNfcEntitlements;
|
|
224
|
+
module.exports.androidPermissionsFor = androidPermissionsFor;
|
|
144
225
|
module.exports.EMRTD_AID = EMRTD_AID;
|
|
@@ -28,6 +28,14 @@ import NitroModules
|
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
29
|
|
|
30
30
|
final class HybridMyazaFaceDetector: HybridMyazaFaceDetectorSpec {
|
|
31
|
+
// Apple Vision ships with the OS — there is no model to fetch, no Play
|
|
32
|
+
// Services equivalent, and no window in which detection is unavailable. These
|
|
33
|
+
// exist only because Android fetches ML Kit's models on demand; iOS answers
|
|
34
|
+
// "ready" unconditionally so the shared TS flow needs no platform branch.
|
|
35
|
+
func isModelReady() throws -> Bool { true }
|
|
36
|
+
|
|
37
|
+
func prepareModel() throws -> Promise<Bool> { Promise.resolved(withResult: true) }
|
|
38
|
+
|
|
31
39
|
func detectFace(frame: any HybridFrameSpec) throws -> FaceResult {
|
|
32
40
|
guard
|
|
33
41
|
let nativeFrame = frame as? NativeFrame,
|
|
@@ -295,8 +303,13 @@ final class HybridMyazaFaceDetector: HybridMyazaFaceDetectorSpec {
|
|
|
295
303
|
return min(max(v, 0.0), 1.0)
|
|
296
304
|
}
|
|
297
305
|
|
|
298
|
-
/// Smile from outer-lip width relative to the face. Input band
|
|
299
|
-
///
|
|
306
|
+
/// Smile from outer-lip width relative to the face box. Input band
|
|
307
|
+
/// (0.36→0.46): the shared detector passes above 0.5, so the pass line sits
|
|
308
|
+
/// at a lip width of 0.41 of the box. It was 0.40→0.50 (pass at 0.45), which
|
|
309
|
+
/// on device needed a full grin — a modest, natural smile never crossed it.
|
|
310
|
+
/// ML Kit on Android returns a classifier probability instead, so this band
|
|
311
|
+
/// is the iOS half of one shared threshold: tune it here, not in
|
|
312
|
+
/// detectSmile, or Android loosens with it.
|
|
300
313
|
private static func smileProbability(_ landmarks: VNFaceLandmarks2D?) -> Double {
|
|
301
314
|
guard let pts = landmarks?.outerLips?.normalizedPoints, pts.count >= 4 else {
|
|
302
315
|
return 0.0
|
|
@@ -307,7 +320,7 @@ final class HybridMyazaFaceDetector: HybridMyazaFaceDetectorSpec {
|
|
|
307
320
|
minX = min(minX, Double(p.x)); maxX = max(maxX, Double(p.x))
|
|
308
321
|
}
|
|
309
322
|
let width = maxX - minX
|
|
310
|
-
let score = (width - 0.
|
|
323
|
+
let score = (width - 0.36) / (0.46 - 0.36)
|
|
311
324
|
return min(max(score, 0.0), 1.0)
|
|
312
325
|
}
|
|
313
326
|
|
|
@@ -14,6 +14,8 @@ namespace margelo::nitro::camera { class HybridFrameSpec; }
|
|
|
14
14
|
|
|
15
15
|
#include "FaceResult.hpp"
|
|
16
16
|
#include "JFaceResult.hpp"
|
|
17
|
+
#include <NitroModules/Promise.hpp>
|
|
18
|
+
#include <NitroModules/JPromise.hpp>
|
|
17
19
|
#include <memory>
|
|
18
20
|
#include <VisionCamera/HybridFrameSpec.hpp>
|
|
19
21
|
#include <VisionCamera/JHybridFrameSpec.hpp>
|
|
@@ -56,5 +58,26 @@ namespace margelo::nitro::myazakyc {
|
|
|
56
58
|
auto __result = method(_javaPart, std::dynamic_pointer_cast<margelo::nitro::camera::JHybridFrameSpec>(frame)->getJavaPart());
|
|
57
59
|
return __result->toCpp();
|
|
58
60
|
}
|
|
61
|
+
bool JHybridMyazaFaceDetectorSpec::isModelReady() {
|
|
62
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean()>("isModelReady");
|
|
63
|
+
auto __result = method(_javaPart);
|
|
64
|
+
return static_cast<bool>(__result);
|
|
65
|
+
}
|
|
66
|
+
std::shared_ptr<Promise<bool>> JHybridMyazaFaceDetectorSpec::prepareModel() {
|
|
67
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("prepareModel");
|
|
68
|
+
auto __result = method(_javaPart);
|
|
69
|
+
return [&]() {
|
|
70
|
+
auto __promise = Promise<bool>::create();
|
|
71
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
|
|
72
|
+
auto __result = jni::static_ref_cast<jni::JBoolean>(__boxedResult);
|
|
73
|
+
__promise->resolve(static_cast<bool>(__result->value()));
|
|
74
|
+
});
|
|
75
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
76
|
+
jni::JniException __jniError(__throwable);
|
|
77
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
78
|
+
});
|
|
79
|
+
return __promise;
|
|
80
|
+
}();
|
|
81
|
+
}
|
|
59
82
|
|
|
60
83
|
} // namespace margelo::nitro::myazakyc
|
|
@@ -55,6 +55,8 @@ namespace margelo::nitro::myazakyc {
|
|
|
55
55
|
public:
|
|
56
56
|
// Methods
|
|
57
57
|
FaceResult detectFace(const std::shared_ptr<margelo::nitro::camera::HybridFrameSpec>& frame) override;
|
|
58
|
+
bool isModelReady() override;
|
|
59
|
+
std::shared_ptr<Promise<bool>> prepareModel() override;
|
|
58
60
|
|
|
59
61
|
private:
|
|
60
62
|
jni::global_ref<JHybridMyazaFaceDetectorSpec::JavaPart> _javaPart;
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaFaceDetectorSpec.kt
CHANGED
|
@@ -11,6 +11,7 @@ import androidx.annotation.Keep
|
|
|
11
11
|
import com.facebook.jni.HybridData
|
|
12
12
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
13
13
|
import com.margelo.nitro.camera.HybridFrameSpec
|
|
14
|
+
import com.margelo.nitro.core.Promise
|
|
14
15
|
import com.margelo.nitro.core.HybridObject
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -32,6 +33,14 @@ abstract class HybridMyazaFaceDetectorSpec: HybridObject() {
|
|
|
32
33
|
@DoNotStrip
|
|
33
34
|
@Keep
|
|
34
35
|
abstract fun detectFace(frame: com.margelo.nitro.camera.HybridFrameSpec): FaceResult
|
|
36
|
+
|
|
37
|
+
@DoNotStrip
|
|
38
|
+
@Keep
|
|
39
|
+
abstract fun isModelReady(): Boolean
|
|
40
|
+
|
|
41
|
+
@DoNotStrip
|
|
42
|
+
@Keep
|
|
43
|
+
abstract fun prepareModel(): Promise<Boolean>
|
|
35
44
|
|
|
36
45
|
// Default implementation of `HybridObject.toString()`
|
|
37
46
|
override fun toString(): String {
|
|
@@ -84,6 +84,14 @@ namespace margelo::nitro::myazakyc::bridge::swift {
|
|
|
84
84
|
return margelo::nitro::camera::bridge::swift::get_std__shared_ptr_HybridFrameSpec_(cppType);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// pragma MARK: std::function<void(bool /* result */)>
|
|
88
|
+
Func_void_bool create_Func_void_bool(void* NON_NULL swiftClosureWrapper) noexcept {
|
|
89
|
+
auto swiftClosure = KycSdkReactNative::Func_void_bool::fromUnsafe(swiftClosureWrapper);
|
|
90
|
+
return [swiftClosure = std::move(swiftClosure)](bool result) mutable -> void {
|
|
91
|
+
swiftClosure.call(result);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
// pragma MARK: std::shared_ptr<HybridMyazaFaceDetectorSpec>
|
|
88
96
|
std::shared_ptr<HybridMyazaFaceDetectorSpec> create_std__shared_ptr_HybridMyazaFaceDetectorSpec_(void* NON_NULL swiftUnsafePointer) noexcept {
|
|
89
97
|
KycSdkReactNative::HybridMyazaFaceDetectorSpec_cxx swiftPart = KycSdkReactNative::HybridMyazaFaceDetectorSpec_cxx::fromUnsafe(swiftUnsafePointer);
|
|
@@ -303,6 +303,40 @@ namespace margelo::nitro::myazakyc::bridge::swift {
|
|
|
303
303
|
using std__weak_ptr_margelo__nitro__camera__HybridFrameSpec_ = std::weak_ptr<margelo::nitro::camera::HybridFrameSpec>;
|
|
304
304
|
inline std__weak_ptr_margelo__nitro__camera__HybridFrameSpec_ weakify_std__shared_ptr_margelo__nitro__camera__HybridFrameSpec_(const std::shared_ptr<margelo::nitro::camera::HybridFrameSpec>& strong) noexcept { return strong; }
|
|
305
305
|
|
|
306
|
+
// pragma MARK: std::shared_ptr<Promise<bool>>
|
|
307
|
+
/**
|
|
308
|
+
* Specialized version of `std::shared_ptr<Promise<bool>>`.
|
|
309
|
+
*/
|
|
310
|
+
using std__shared_ptr_Promise_bool__ = std::shared_ptr<Promise<bool>>;
|
|
311
|
+
inline std::shared_ptr<Promise<bool>> create_std__shared_ptr_Promise_bool__() noexcept {
|
|
312
|
+
return Promise<bool>::create();
|
|
313
|
+
}
|
|
314
|
+
inline PromiseHolder<bool> wrap_std__shared_ptr_Promise_bool__(std::shared_ptr<Promise<bool>> promise) noexcept {
|
|
315
|
+
return PromiseHolder<bool>(std::move(promise));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// pragma MARK: std::function<void(bool /* result */)>
|
|
319
|
+
/**
|
|
320
|
+
* Specialized version of `std::function<void(bool)>`.
|
|
321
|
+
*/
|
|
322
|
+
using Func_void_bool = std::function<void(bool /* result */)>;
|
|
323
|
+
/**
|
|
324
|
+
* Wrapper class for a `std::function<void(bool / * result * /)>`, this can be used from Swift.
|
|
325
|
+
*/
|
|
326
|
+
class Func_void_bool_Wrapper final {
|
|
327
|
+
public:
|
|
328
|
+
explicit Func_void_bool_Wrapper(std::function<void(bool /* result */)>&& func): _function(std::make_unique<std::function<void(bool /* result */)>>(std::move(func))) {}
|
|
329
|
+
inline void call(bool result) const noexcept {
|
|
330
|
+
_function->operator()(result);
|
|
331
|
+
}
|
|
332
|
+
private:
|
|
333
|
+
std::unique_ptr<std::function<void(bool /* result */)>> _function;
|
|
334
|
+
} SWIFT_NONCOPYABLE;
|
|
335
|
+
Func_void_bool create_Func_void_bool(void* NON_NULL swiftClosureWrapper) noexcept;
|
|
336
|
+
inline Func_void_bool_Wrapper wrap_Func_void_bool(Func_void_bool value) noexcept {
|
|
337
|
+
return Func_void_bool_Wrapper(std::move(value));
|
|
338
|
+
}
|
|
339
|
+
|
|
306
340
|
// pragma MARK: std::shared_ptr<HybridMyazaFaceDetectorSpec>
|
|
307
341
|
/**
|
|
308
342
|
* Specialized version of `std::shared_ptr<HybridMyazaFaceDetectorSpec>`.
|
|
@@ -324,6 +358,15 @@ namespace margelo::nitro::myazakyc::bridge::swift {
|
|
|
324
358
|
return Result<FaceResult>::withError(error);
|
|
325
359
|
}
|
|
326
360
|
|
|
361
|
+
// pragma MARK: Result<std::shared_ptr<Promise<bool>>>
|
|
362
|
+
using Result_std__shared_ptr_Promise_bool___ = Result<std::shared_ptr<Promise<bool>>>;
|
|
363
|
+
inline Result_std__shared_ptr_Promise_bool___ create_Result_std__shared_ptr_Promise_bool___(const std::shared_ptr<Promise<bool>>& value) noexcept {
|
|
364
|
+
return Result<std::shared_ptr<Promise<bool>>>::withValue(value);
|
|
365
|
+
}
|
|
366
|
+
inline Result_std__shared_ptr_Promise_bool___ create_Result_std__shared_ptr_Promise_bool___(const std::exception_ptr& error) noexcept {
|
|
367
|
+
return Result<std::shared_ptr<Promise<bool>>>::withError(error);
|
|
368
|
+
}
|
|
369
|
+
|
|
327
370
|
// pragma MARK: std::shared_ptr<HybridMyazaRectDetectorSpec>
|
|
328
371
|
/**
|
|
329
372
|
* Specialized version of `std::shared_ptr<HybridMyazaRectDetectorSpec>`.
|