@simasfin/smmf-sdk 0.0.0-stage → 0.2.3
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 +84 -2
- package/android/build.gradle +51 -0
- package/android/maven/id/co/onepass/onepass-sdk/0.2.3/onepass-sdk-0.2.3.aar +0 -0
- package/android/maven/id/co/onepass/onepass-sdk/0.2.3/onepass-sdk-0.2.3.pom +52 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/id/co/onepass/expo/OnePassSignatureExpoView.kt +25 -0
- package/android/src/main/java/id/co/onepass/expo/OnepassExpoModule.kt +134 -0
- package/app.plugin.js +1 -0
- package/build/OnePassSignatureView.d.ts +19 -0
- package/build/OnePassSignatureView.d.ts.map +1 -0
- package/build/OnePassSignatureView.js +15 -0
- package/build/OnePassSignatureView.js.map +1 -0
- package/build/index.d.ts +21 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +10 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/OnePassSDK/DesignSystem.swift +139 -0
- package/ios/OnePassSDK/IconView.swift +54 -0
- package/ios/OnePassSDK/IconsaxPath.swift +216 -0
- package/ios/OnePassSDK/Liveness/FaceAnalyzer.swift +114 -0
- package/ios/OnePassSDK/Liveness/FaceLandmarkMetrics.swift +28 -0
- package/ios/OnePassSDK/Liveness/FaceSnapshotMapper.swift +37 -0
- package/ios/OnePassSDK/Liveness/LivenessGate.swift +155 -0
- package/ios/OnePassSDK/Liveness/LivenessImagePreparer.swift +44 -0
- package/ios/OnePassSDK/Liveness/LivenessViewController.swift +724 -0
- package/ios/OnePassSDK/Ocr/CoverFitCrop.swift +37 -0
- package/ios/OnePassSDK/Ocr/KtpFrameOverlayView.swift +98 -0
- package/ios/OnePassSDK/Ocr/KtpImagePreparer.swift +90 -0
- package/ios/OnePassSDK/Ocr/KtpOcrViewController.swift +703 -0
- package/ios/OnePassSDK/Ocr/KtpPhotoInspector.swift +93 -0
- package/ios/OnePassSDK/Ocr/KtpQualityGate.swift +185 -0
- package/ios/OnePassSDK/OnePass.swift +28 -0
- package/ios/OnePassSDK/Resources/hindari_pemakaian.png +0 -0
- package/ios/OnePassSDK/Resources/inter_bold.ttf +0 -0
- package/ios/OnePassSDK/Resources/inter_medium.ttf +0 -0
- package/ios/OnePassSDK/Resources/inter_regular.ttf +0 -0
- package/ios/OnePassSDK/Resources/inter_semibold.ttf +0 -0
- package/ios/OnePassSDK/Resources/ktp_illustration.webp +0 -0
- package/ios/OnePassSDK/Resources/logo_smmf.png +0 -0
- package/ios/OnePassSDK/Resources/placeholder_head.png +0 -0
- package/ios/OnePassSDK/Signature/SignaturePadView.swift +144 -0
- package/ios/OnePassSignatureExpoView.swift +22 -0
- package/ios/OnepassExpo.podspec +23 -0
- package/ios/OnepassExpoModule.swift +77 -0
- package/licenses/Iconsax-MIT.txt +26 -0
- package/licenses/Inter-OFL-1.1.txt +93 -0
- package/package.json +49 -4
- package/plugin/src/inject.js +31 -0
- package/plugin/withOnePass.js +32 -0
- package/scripts/sync-native.js +84 -0
- package/src/OnePassSignatureView.tsx +34 -0
- package/src/index.ts +44 -0
package/README.md
CHANGED
|
@@ -1,3 +1,85 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @simasfin/smmf-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Modul Expo untuk liveness wajah, foto KTP, dan kanvas tanda tangan. Android dan iOS.
|
|
4
|
+
|
|
5
|
+
> Modul ini berisi kode native, jadi butuh **development build / EAS Build**. Tidak bisa dijalankan di Expo Go.
|
|
6
|
+
|
|
7
|
+
### 1. Pasang paket
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx expo install @simasfin/smmf-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 2. Daftarkan config plugin
|
|
14
|
+
|
|
15
|
+
Di `app.json` (atau `app.config.js`):
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"expo": {
|
|
20
|
+
"plugins": ["@simasfin/smmf-sdk"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Config plugin akan otomatis menambahkan `NSCameraUsageDescription` (iOS) dan permission kamera Android bila belum ada.
|
|
26
|
+
|
|
27
|
+
Opsi tambahan (bentuk `["@simasfin/smmf-sdk", { ... }]`):
|
|
28
|
+
|
|
29
|
+
| Opsi | Wajib | Default | Keterangan |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `cameraUsageDescription` | tidak | "Kamera dibutuhkan untuk verifikasi wajah dan pengambilan foto KTP." | Teks izin kamera iOS. Tidak menimpa nilai yang sudah Anda set. |
|
|
32
|
+
|
|
33
|
+
### 3. Generate project native
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx expo prebuild
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 4. Panggil Liveness
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { startLiveness } from '@simasfin/smmf-sdk';
|
|
43
|
+
|
|
44
|
+
const result = await startLiveness({
|
|
45
|
+
primaryColorHex: '#EC1F27' // opsional
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (result.status === 'SUCCESS') {
|
|
49
|
+
const base64 = result.imageBase64; // JPEG base64, kirim ke backend Anda
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 5. Panggil Foto KTP
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { startKtpOcr } from '@simasfin/smmf-sdk';
|
|
57
|
+
|
|
58
|
+
const result = await startKtpOcr();
|
|
59
|
+
|
|
60
|
+
if (result.status === 'SUCCESS') {
|
|
61
|
+
const base64 = result.imageBase64; // JPEG base64, kirim ke backend Anda untuk OCR
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 6. Kanvas Tanda Tangan
|
|
66
|
+
|
|
67
|
+
Kanvas native yang ditaruh inline di layar Anda. Hasilnya PNG transparan seukuran kanvas, base64 polos.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { OnePassSignatureView, OnePassSignatureViewRef } from '@simasfin/smmf-sdk';
|
|
71
|
+
|
|
72
|
+
const signatureRef = useRef<OnePassSignatureViewRef>(null);
|
|
73
|
+
|
|
74
|
+
<OnePassSignatureView
|
|
75
|
+
ref={signatureRef}
|
|
76
|
+
style={{ height: 240 }}
|
|
77
|
+
penColor="#0A2730" // hex
|
|
78
|
+
strokeWidth={4} // ketebalan garis (dp/pt)
|
|
79
|
+
onBegin={() => setScrollEnabled(false)}
|
|
80
|
+
onEnd={() => setScrollEnabled(true)}
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
await signatureRef.current?.clear();
|
|
84
|
+
const base64 = await signatureRef.current?.readSignature(); // null bila kanvas masih kosong
|
|
85
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'kotlin-android'
|
|
3
|
+
|
|
4
|
+
group = 'id.co.onepass.expo'
|
|
5
|
+
version = '0.1.0'
|
|
6
|
+
|
|
7
|
+
def onepassSdkVersion = new groovy.json.JsonSlurper().parse(file('../package.json')).version
|
|
8
|
+
|
|
9
|
+
def safeExtGet(prop, fallback) {
|
|
10
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
android {
|
|
14
|
+
namespace 'id.co.onepass.expo'
|
|
15
|
+
compileSdk safeExtGet('compileSdkVersion', 34)
|
|
16
|
+
|
|
17
|
+
defaultConfig {
|
|
18
|
+
minSdkVersion safeExtGet('minSdkVersion', 24)
|
|
19
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 34)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
compileOptions {
|
|
23
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
24
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
kotlinOptions {
|
|
28
|
+
jvmTarget = '17'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
repositories {
|
|
33
|
+
maven { url "$projectDir/maven" }
|
|
34
|
+
mavenCentral()
|
|
35
|
+
google()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dependencies {
|
|
39
|
+
implementation project(':expo-modules-core')
|
|
40
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
|
41
|
+
|
|
42
|
+
api "id.co.onepass:onepass-sdk:${onepassSdkVersion}"
|
|
43
|
+
|
|
44
|
+
implementation 'androidx.appcompat:appcompat:1.7.0'
|
|
45
|
+
implementation 'androidx.camera:camera-core:1.4.1'
|
|
46
|
+
implementation 'androidx.camera:camera-camera2:1.4.1'
|
|
47
|
+
implementation 'androidx.camera:camera-lifecycle:1.4.1'
|
|
48
|
+
implementation 'androidx.camera:camera-view:1.4.1'
|
|
49
|
+
implementation 'com.google.mlkit:face-detection:16.1.7'
|
|
50
|
+
implementation 'com.google.mlkit:text-recognition:16.0.1'
|
|
51
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
|
3
|
+
<modelVersion>4.0.0</modelVersion>
|
|
4
|
+
<groupId>id.co.onepass</groupId>
|
|
5
|
+
<artifactId>onepass-sdk</artifactId>
|
|
6
|
+
<version>0.2.3</version>
|
|
7
|
+
<packaging>aar</packaging>
|
|
8
|
+
<dependencies>
|
|
9
|
+
<dependency>
|
|
10
|
+
<groupId>androidx.appcompat</groupId>
|
|
11
|
+
<artifactId>appcompat</artifactId>
|
|
12
|
+
<version>1.7.0</version>
|
|
13
|
+
<scope>runtime</scope>
|
|
14
|
+
</dependency>
|
|
15
|
+
<dependency>
|
|
16
|
+
<groupId>androidx.camera</groupId>
|
|
17
|
+
<artifactId>camera-core</artifactId>
|
|
18
|
+
<version>1.4.1</version>
|
|
19
|
+
<scope>runtime</scope>
|
|
20
|
+
</dependency>
|
|
21
|
+
<dependency>
|
|
22
|
+
<groupId>androidx.camera</groupId>
|
|
23
|
+
<artifactId>camera-camera2</artifactId>
|
|
24
|
+
<version>1.4.1</version>
|
|
25
|
+
<scope>runtime</scope>
|
|
26
|
+
</dependency>
|
|
27
|
+
<dependency>
|
|
28
|
+
<groupId>androidx.camera</groupId>
|
|
29
|
+
<artifactId>camera-lifecycle</artifactId>
|
|
30
|
+
<version>1.4.1</version>
|
|
31
|
+
<scope>runtime</scope>
|
|
32
|
+
</dependency>
|
|
33
|
+
<dependency>
|
|
34
|
+
<groupId>androidx.camera</groupId>
|
|
35
|
+
<artifactId>camera-view</artifactId>
|
|
36
|
+
<version>1.4.1</version>
|
|
37
|
+
<scope>runtime</scope>
|
|
38
|
+
</dependency>
|
|
39
|
+
<dependency>
|
|
40
|
+
<groupId>com.google.mlkit</groupId>
|
|
41
|
+
<artifactId>face-detection</artifactId>
|
|
42
|
+
<version>16.1.7</version>
|
|
43
|
+
<scope>runtime</scope>
|
|
44
|
+
</dependency>
|
|
45
|
+
<dependency>
|
|
46
|
+
<groupId>com.google.mlkit</groupId>
|
|
47
|
+
<artifactId>text-recognition</artifactId>
|
|
48
|
+
<version>16.0.1</version>
|
|
49
|
+
<scope>runtime</scope>
|
|
50
|
+
</dependency>
|
|
51
|
+
</dependencies>
|
|
52
|
+
</project>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package id.co.onepass.expo
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import expo.modules.kotlin.AppContext
|
|
5
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
6
|
+
import expo.modules.kotlin.views.ExpoView
|
|
7
|
+
import id.co.onepass.sdk.signature.SignaturePadView
|
|
8
|
+
|
|
9
|
+
class OnePassSignatureExpoView(context: Context, appContext: AppContext) :
|
|
10
|
+
ExpoView(context, appContext) {
|
|
11
|
+
|
|
12
|
+
val pad = SignaturePadView(context)
|
|
13
|
+
private val onBegin by EventDispatcher()
|
|
14
|
+
private val onEnd by EventDispatcher()
|
|
15
|
+
|
|
16
|
+
init {
|
|
17
|
+
addView(pad)
|
|
18
|
+
pad.onBegin = { onBegin(emptyMap()) }
|
|
19
|
+
pad.onEnd = { onEnd(emptyMap()) }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
23
|
+
pad.layout(0, 0, right - left, bottom - top)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
package id.co.onepass.expo
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import expo.modules.kotlin.Promise
|
|
7
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
8
|
+
import expo.modules.kotlin.functions.Queues
|
|
9
|
+
import expo.modules.kotlin.modules.Module
|
|
10
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
11
|
+
import id.co.onepass.sdk.KtpOcrContract
|
|
12
|
+
import id.co.onepass.sdk.KtpOcrInput
|
|
13
|
+
import id.co.onepass.sdk.KtpOcrStatusCode
|
|
14
|
+
import id.co.onepass.sdk.LivenessContract
|
|
15
|
+
import id.co.onepass.sdk.LivenessInput
|
|
16
|
+
import id.co.onepass.sdk.LivenessStatus
|
|
17
|
+
|
|
18
|
+
class OnepassExpoModule : Module() {
|
|
19
|
+
|
|
20
|
+
companion object {
|
|
21
|
+
private const val REQUEST_LIVENESS = 0x0F01
|
|
22
|
+
private const val REQUEST_KTP_OCR = 0x0F02
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private var pendingPromise: Promise? = null
|
|
26
|
+
private var pendingRequestCode: Int? = null
|
|
27
|
+
|
|
28
|
+
private val currentActivity: Activity
|
|
29
|
+
get() = appContext.currentActivity ?: throw Exceptions.MissingActivity()
|
|
30
|
+
|
|
31
|
+
override fun definition() = ModuleDefinition {
|
|
32
|
+
Name("OnepassExpo")
|
|
33
|
+
|
|
34
|
+
AsyncFunction("startLiveness") { options: Map<String, Any?>, promise: Promise ->
|
|
35
|
+
val input = LivenessInput(primaryColorHex = options["primaryColorHex"] as? String)
|
|
36
|
+
launch(
|
|
37
|
+
REQUEST_LIVENESS,
|
|
38
|
+
LivenessContract().createIntent(currentActivity, input),
|
|
39
|
+
promise,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
AsyncFunction("startKtpOcr") { options: Map<String, Any?>, promise: Promise ->
|
|
44
|
+
val input = KtpOcrInput(primaryColorHex = options["primaryColorHex"] as? String)
|
|
45
|
+
launch(
|
|
46
|
+
REQUEST_KTP_OCR,
|
|
47
|
+
KtpOcrContract().createIntent(currentActivity, input),
|
|
48
|
+
promise,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
View(OnePassSignatureExpoView::class) {
|
|
53
|
+
Events("onBegin", "onEnd")
|
|
54
|
+
|
|
55
|
+
Prop("penColor") { view: OnePassSignatureExpoView, color: String? ->
|
|
56
|
+
view.pad.penColor =
|
|
57
|
+
color?.let { runCatching { Color.parseColor(it) }.getOrNull() } ?: Color.BLACK
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Prop("strokeWidth") { view: OnePassSignatureExpoView, width: Float? ->
|
|
61
|
+
view.pad.strokeWidthDp = width ?: 2f
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
AsyncFunction("clear") { view: OnePassSignatureExpoView ->
|
|
65
|
+
view.pad.clear()
|
|
66
|
+
}.runOnQueue(Queues.MAIN)
|
|
67
|
+
|
|
68
|
+
AsyncFunction("readSignature") { view: OnePassSignatureExpoView ->
|
|
69
|
+
view.pad.toBase64Png()
|
|
70
|
+
}.runOnQueue(Queues.MAIN)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
OnActivityResult { _, payload ->
|
|
74
|
+
when (payload.requestCode) {
|
|
75
|
+
REQUEST_LIVENESS -> resolveLiveness(payload.resultCode, payload.data)
|
|
76
|
+
REQUEST_KTP_OCR -> resolveKtpOcr(payload.resultCode, payload.data)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private fun launch(requestCode: Int, intent: Intent, promise: Promise) {
|
|
82
|
+
if (pendingPromise != null) {
|
|
83
|
+
promise.reject(
|
|
84
|
+
"ONEPASS_BUSY",
|
|
85
|
+
"Verifikasi OnePass lain sedang berjalan",
|
|
86
|
+
null,
|
|
87
|
+
)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
pendingPromise = promise
|
|
91
|
+
pendingRequestCode = requestCode
|
|
92
|
+
currentActivity.startActivityForResult(intent, requestCode)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private fun consumePromise(requestCode: Int): Promise? {
|
|
96
|
+
if (pendingRequestCode != requestCode) return null
|
|
97
|
+
val promise = pendingPromise
|
|
98
|
+
pendingPromise = null
|
|
99
|
+
pendingRequestCode = null
|
|
100
|
+
return promise
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private fun resolveLiveness(resultCode: Int, data: Intent?) {
|
|
104
|
+
val promise = consumePromise(REQUEST_LIVENESS) ?: return
|
|
105
|
+
val result = LivenessContract().parseResult(resultCode, data)
|
|
106
|
+
promise.resolve(
|
|
107
|
+
mapOf(
|
|
108
|
+
"status" to when (result.status) {
|
|
109
|
+
LivenessStatus.SUCCESS -> "SUCCESS"
|
|
110
|
+
LivenessStatus.FAILED -> "FAILED"
|
|
111
|
+
LivenessStatus.CANCELLED -> "CANCELLED"
|
|
112
|
+
},
|
|
113
|
+
"imageBase64" to result.imageBase64,
|
|
114
|
+
"errorMessage" to result.errorMessage,
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private fun resolveKtpOcr(resultCode: Int, data: Intent?) {
|
|
120
|
+
val promise = consumePromise(REQUEST_KTP_OCR) ?: return
|
|
121
|
+
val result = KtpOcrContract().parseResult(resultCode, data)
|
|
122
|
+
promise.resolve(
|
|
123
|
+
mapOf(
|
|
124
|
+
"status" to when (result.status) {
|
|
125
|
+
KtpOcrStatusCode.SUCCESS -> "SUCCESS"
|
|
126
|
+
KtpOcrStatusCode.FAILED -> "FAILED"
|
|
127
|
+
KtpOcrStatusCode.CANCELLED -> "CANCELLED"
|
|
128
|
+
},
|
|
129
|
+
"imageBase64" to result.imageBase64,
|
|
130
|
+
"errorMessage" to result.errorMessage,
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/withOnePass');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ViewProps } from 'react-native';
|
|
3
|
+
export type OnePassSignatureViewProps = ViewProps & {
|
|
4
|
+
penColor?: string;
|
|
5
|
+
strokeWidth?: number;
|
|
6
|
+
onBegin?: () => void;
|
|
7
|
+
onEnd?: () => void;
|
|
8
|
+
};
|
|
9
|
+
export type OnePassSignatureViewRef = {
|
|
10
|
+
clear(): Promise<void>;
|
|
11
|
+
readSignature(): Promise<string | null>;
|
|
12
|
+
};
|
|
13
|
+
export declare const OnePassSignatureView: React.ForwardRefExoticComponent<ViewProps & {
|
|
14
|
+
penColor?: string;
|
|
15
|
+
strokeWidth?: number;
|
|
16
|
+
onBegin?: () => void;
|
|
17
|
+
onEnd?: () => void;
|
|
18
|
+
} & React.RefAttributes<OnePassSignatureViewRef>>;
|
|
19
|
+
//# sourceMappingURL=OnePassSignatureView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OnePassSignatureView.d.ts","sourceRoot":"","sources":["../src/OnePassSignatureView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC,CAAC;AAMF,eAAO,MAAM,oBAAoB;eAfpB,MAAM;kBACH,MAAM;cACV,MAAM,IAAI;YACZ,MAAM,IAAI;iDAyBnB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { requireNativeViewManager } from 'expo-modules-core';
|
|
3
|
+
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
4
|
+
const NativeSignatureView = requireNativeViewManager('OnepassExpo');
|
|
5
|
+
export const OnePassSignatureView = forwardRef(function OnePassSignatureView(props, ref) {
|
|
6
|
+
const nativeRef = useRef(null);
|
|
7
|
+
useImperativeHandle(ref, () => ({
|
|
8
|
+
clear: async () => {
|
|
9
|
+
await nativeRef.current?.clear();
|
|
10
|
+
},
|
|
11
|
+
readSignature: async () => (await nativeRef.current?.readSignature()) ?? null,
|
|
12
|
+
}));
|
|
13
|
+
return _jsx(NativeSignatureView, { ...props, ref: nativeRef });
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=OnePassSignatureView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OnePassSignatureView.js","sourceRoot":"","sources":["../src/OnePassSignatureView.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAc,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAevE,MAAM,mBAAmB,GAAG,wBAAwB,CAClD,aAAa,CACmF,CAAC;AAEnG,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAC5C,SAAS,oBAAoB,CAAC,KAAK,EAAE,GAAG;IACtC,MAAM,SAAS,GAAG,MAAM,CAA0B,IAAI,CAAC,CAAC;IAExD,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9B,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QACnC,CAAC;QACD,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,CAAC,IAAI,IAAI;KAC9E,CAAC,CAAC,CAAC;IAEJ,OAAO,KAAC,mBAAmB,OAAK,KAAK,EAAE,GAAG,EAAE,SAAS,GAAI,CAAC;AAC5D,CAAC,CACF,CAAC"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { OnePassSignatureView, type OnePassSignatureViewProps, type OnePassSignatureViewRef, } from './OnePassSignatureView';
|
|
2
|
+
export type CaptureStatus = 'SUCCESS' | 'FAILED' | 'CANCELLED';
|
|
3
|
+
export type LivenessOptions = {
|
|
4
|
+
primaryColorHex?: string;
|
|
5
|
+
};
|
|
6
|
+
export type LivenessResult = {
|
|
7
|
+
status: CaptureStatus;
|
|
8
|
+
imageBase64?: string;
|
|
9
|
+
errorMessage?: string;
|
|
10
|
+
};
|
|
11
|
+
export type KtpOcrOptions = {
|
|
12
|
+
primaryColorHex?: string;
|
|
13
|
+
};
|
|
14
|
+
export type KtpOcrResult = {
|
|
15
|
+
status: CaptureStatus;
|
|
16
|
+
imageBase64?: string;
|
|
17
|
+
errorMessage?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function startLiveness(options?: LivenessOptions): Promise<LivenessResult>;
|
|
20
|
+
export declare function startKtpOcr(options?: KtpOcrOptions): Promise<KtpOcrResult>;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,GAC7B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,aAAa,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AASF,wBAAgB,aAAa,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAEpF;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAE9E"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
export { OnePassSignatureView, } from './OnePassSignatureView';
|
|
3
|
+
const OnepassExpo = requireNativeModule('OnepassExpo');
|
|
4
|
+
export function startLiveness(options = {}) {
|
|
5
|
+
return OnepassExpo.startLiveness(options);
|
|
6
|
+
}
|
|
7
|
+
export function startKtpOcr(options = {}) {
|
|
8
|
+
return OnepassExpo.startKtpOcr(options);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EACL,oBAAoB,GAGrB,MAAM,wBAAwB,CAAC;AA6BhC,MAAM,WAAW,GAAG,mBAAmB,CAA0B,aAAa,CAAC,CAAC;AAEhF,MAAM,UAAU,aAAa,CAAC,UAA2B,EAAE;IACzD,OAAO,WAAW,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAyB,EAAE;IACrD,OAAO,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
enum OnePassColor {
|
|
4
|
+
static let simascore = UIColor(hex: "#EC1F27")
|
|
5
|
+
static let red500 = UIColor(hex: "#E53E3E")
|
|
6
|
+
static let greenCheckBadge = UIColor(hex: "#52BD94")
|
|
7
|
+
static let orangeWarningBadge = UIColor(hex: "#FF5630")
|
|
8
|
+
static let greenPhotoBorder = UIColor(hex: "#2DB089")
|
|
9
|
+
static let greenSpinner = UIColor(hex: "#48BB78")
|
|
10
|
+
static let gray50 = UIColor(hex: "#F7FAFC")
|
|
11
|
+
static let gray100 = UIColor(hex: "#EDF2F7")
|
|
12
|
+
static let gray200 = UIColor(hex: "#E2E8F0")
|
|
13
|
+
static let gray600 = UIColor(hex: "#4A5568")
|
|
14
|
+
static let gray800 = UIColor(hex: "#1A202C")
|
|
15
|
+
static let shutterBorder = UIColor(hex: "#808080")
|
|
16
|
+
|
|
17
|
+
static let primary500 = UIColor(hex: "#B82025")
|
|
18
|
+
static let primary600 = UIColor(hex: "#931A1E")
|
|
19
|
+
static let primary25 = UIColor(hex: "#FCEEEE")
|
|
20
|
+
static let warning50 = UIColor(hex: "#FFF7E9")
|
|
21
|
+
static let warning400 = UIColor(hex: "#FFC04D")
|
|
22
|
+
static let warning500 = UIColor(hex: "#FFB020")
|
|
23
|
+
static let error50 = UIColor(hex: "#FFEEEE")
|
|
24
|
+
static let error600 = UIColor(hex: "#E84E2C")
|
|
25
|
+
static let neutral100 = UIColor(hex: "#F2F4F7")
|
|
26
|
+
static let tertiary50 = UIColor(hex: "#FCFCFD")
|
|
27
|
+
static let tertiary500 = UIColor(hex: "#667085")
|
|
28
|
+
static let tertiary700 = UIColor(hex: "#334054")
|
|
29
|
+
static let tertiary900 = UIColor(hex: "#101828")
|
|
30
|
+
static let baseBlack = UIColor(hex: "#0A0A0B")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
enum OnePassFont {
|
|
34
|
+
private static var registered = false
|
|
35
|
+
|
|
36
|
+
static func register() {
|
|
37
|
+
guard !registered else { return }
|
|
38
|
+
registered = true
|
|
39
|
+
["inter_regular", "inter_medium", "inter_semibold", "inter_bold"].forEach { name in
|
|
40
|
+
guard
|
|
41
|
+
let url = Bundle.onePass.url(forResource: name, withExtension: "ttf"),
|
|
42
|
+
let data = try? Data(contentsOf: url),
|
|
43
|
+
let provider = CGDataProvider(data: data as CFData),
|
|
44
|
+
let font = CGFont(provider)
|
|
45
|
+
else { return }
|
|
46
|
+
CTFontManagerRegisterGraphicsFont(font, nil)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static func regular(_ size: CGFloat) -> UIFont { named("Inter-Regular", size, .regular) }
|
|
51
|
+
static func medium(_ size: CGFloat) -> UIFont { named("Inter-Medium", size, .medium) }
|
|
52
|
+
static func semibold(_ size: CGFloat) -> UIFont { named("Inter-SemiBold", size, .semibold) }
|
|
53
|
+
static func bold(_ size: CGFloat) -> UIFont { named("Inter-Bold", size, .bold) }
|
|
54
|
+
|
|
55
|
+
private static func named(_ name: String, _ size: CGFloat, _ weight: UIFont.Weight) -> UIFont {
|
|
56
|
+
register()
|
|
57
|
+
return UIFont(name: name, size: size) ?? .systemFont(ofSize: size, weight: weight)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
extension Bundle {
|
|
62
|
+
static var onePass: Bundle {
|
|
63
|
+
#if SWIFT_PACKAGE
|
|
64
|
+
return .module
|
|
65
|
+
#else
|
|
66
|
+
let container = Bundle(for: LivenessViewController.self)
|
|
67
|
+
for name in ["OnePassSDK", "OnepassExpo-OnePassSDK"] {
|
|
68
|
+
if
|
|
69
|
+
let url = container.url(forResource: name, withExtension: "bundle"),
|
|
70
|
+
let resourceBundle = Bundle(url: url)
|
|
71
|
+
{
|
|
72
|
+
return resourceBundle
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return container
|
|
76
|
+
#endif
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
extension UIColor {
|
|
81
|
+
convenience init(hex: String) {
|
|
82
|
+
var value = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
83
|
+
if value.hasPrefix("#") { value.removeFirst() }
|
|
84
|
+
var rgb: UInt64 = 0
|
|
85
|
+
Scanner(string: value).scanHexInt64(&rgb)
|
|
86
|
+
let hasAlpha = value.count == 8
|
|
87
|
+
let divisor: CGFloat = 255
|
|
88
|
+
if hasAlpha {
|
|
89
|
+
self.init(
|
|
90
|
+
red: CGFloat((rgb & 0xFF00_0000) >> 24) / divisor,
|
|
91
|
+
green: CGFloat((rgb & 0x00FF_0000) >> 16) / divisor,
|
|
92
|
+
blue: CGFloat((rgb & 0x0000_FF00) >> 8) / divisor,
|
|
93
|
+
alpha: CGFloat(rgb & 0x0000_00FF) / divisor
|
|
94
|
+
)
|
|
95
|
+
} else {
|
|
96
|
+
self.init(
|
|
97
|
+
red: CGFloat((rgb & 0xFF0000) >> 16) / divisor,
|
|
98
|
+
green: CGFloat((rgb & 0x00FF00) >> 8) / divisor,
|
|
99
|
+
blue: CGFloat(rgb & 0x0000FF) / divisor,
|
|
100
|
+
alpha: 1
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
extension UIImage {
|
|
107
|
+
static func onePass(_ name: String) -> UIImage? {
|
|
108
|
+
UIImage(named: name, in: .onePass, compatibleWith: nil)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
final class OnePassButton: UIButton {
|
|
113
|
+
enum Style {
|
|
114
|
+
case solid(UIColor)
|
|
115
|
+
case outline(border: UIColor, text: UIColor)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
init(title: String, style: Style, cornerRadius: CGFloat, font: UIFont) {
|
|
119
|
+
super.init(frame: .zero)
|
|
120
|
+
setTitle(title, for: .normal)
|
|
121
|
+
titleLabel?.font = font
|
|
122
|
+
layer.cornerRadius = cornerRadius
|
|
123
|
+
layer.masksToBounds = true
|
|
124
|
+
contentEdgeInsets = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
|
|
125
|
+
switch style {
|
|
126
|
+
case .solid(let color):
|
|
127
|
+
backgroundColor = color
|
|
128
|
+
setTitleColor(.white, for: .normal)
|
|
129
|
+
case .outline(let border, let text):
|
|
130
|
+
backgroundColor = .white
|
|
131
|
+
layer.borderWidth = 1
|
|
132
|
+
layer.borderColor = border.cgColor
|
|
133
|
+
setTitleColor(text, for: .normal)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@available(*, unavailable)
|
|
138
|
+
required init?(coder: NSCoder) { fatalError("tidak dipakai") }
|
|
139
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
final class IconView: UIView {
|
|
4
|
+
|
|
5
|
+
private static let viewportSize: CGFloat = 24
|
|
6
|
+
|
|
7
|
+
private let shape = CAShapeLayer()
|
|
8
|
+
private let segments: [IconSegment]
|
|
9
|
+
|
|
10
|
+
init(segments: [IconSegment], size: CGFloat, color: UIColor) {
|
|
11
|
+
self.segments = segments
|
|
12
|
+
super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
|
|
13
|
+
backgroundColor = .clear
|
|
14
|
+
shape.fillColor = color.cgColor
|
|
15
|
+
shape.fillRule = .nonZero
|
|
16
|
+
layer.addSublayer(shape)
|
|
17
|
+
translatesAutoresizingMaskIntoConstraints = false
|
|
18
|
+
NSLayoutConstraint.activate([
|
|
19
|
+
widthAnchor.constraint(equalToConstant: size),
|
|
20
|
+
heightAnchor.constraint(equalToConstant: size),
|
|
21
|
+
])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@available(*, unavailable)
|
|
25
|
+
required init?(coder: NSCoder) { fatalError("tidak dipakai") }
|
|
26
|
+
|
|
27
|
+
var color: UIColor = .white {
|
|
28
|
+
didSet { shape.fillColor = color.cgColor }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override func layoutSubviews() {
|
|
32
|
+
super.layoutSubviews()
|
|
33
|
+
shape.frame = bounds
|
|
34
|
+
let scale = min(bounds.width, bounds.height) / Self.viewportSize
|
|
35
|
+
let path = CGMutablePath()
|
|
36
|
+
for segment in segments {
|
|
37
|
+
switch segment {
|
|
38
|
+
case .move(let x, let y):
|
|
39
|
+
path.move(to: CGPoint(x: x * scale, y: y * scale))
|
|
40
|
+
case .line(let x, let y):
|
|
41
|
+
path.addLine(to: CGPoint(x: x * scale, y: y * scale))
|
|
42
|
+
case .curve(let x1, let y1, let x2, let y2, let x, let y):
|
|
43
|
+
path.addCurve(
|
|
44
|
+
to: CGPoint(x: x * scale, y: y * scale),
|
|
45
|
+
control1: CGPoint(x: x1 * scale, y: y1 * scale),
|
|
46
|
+
control2: CGPoint(x: x2 * scale, y: y2 * scale)
|
|
47
|
+
)
|
|
48
|
+
case .close:
|
|
49
|
+
path.closeSubpath()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
shape.path = path
|
|
53
|
+
}
|
|
54
|
+
}
|