@smile_identity/react-native 10.1.11 → 10.1.12

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 (71) hide show
  1. package/android/build.gradle +4 -4
  2. package/android/gradle.properties +1 -1
  3. package/android/src/main/java/com/smileidentity/react/SmileIdPackage.kt +5 -0
  4. package/android/src/main/java/com/smileidentity/react/utils/DocumentCaptureResultAdapter.kt +54 -0
  5. package/android/src/main/java/com/smileidentity/react/utils/SelfieCaptureResultAdapter.kt +67 -0
  6. package/android/src/main/java/com/smileidentity/react/viewmanagers/SmileIDDocumentCaptureViewManager.kt +65 -0
  7. package/android/src/main/java/com/smileidentity/react/viewmanagers/SmileIDSmartSelfieCaptureViewManager.kt +63 -0
  8. package/android/src/main/java/com/smileidentity/react/views/SmileIDDocumentCaptureView.kt +109 -0
  9. package/android/src/main/java/com/smileidentity/react/views/SmileIDSmartSelfieCaptureView.kt +213 -0
  10. package/android/src/main/java/com/smileidentity/react/views/SmileIDView.kt +7 -2
  11. package/ios/RNSmileID.swift +1 -1
  12. package/ios/SmileId.xcodeproj/xcuserdata/japhetndhlovu.xcuserdatad/xcschemes/SmileId.xcscheme +23 -0
  13. package/ios/SmileId.xcodeproj/xcuserdata/japhetndhlovu.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  14. package/ios/View/SmileIDDocumentCaptureView.swift +82 -0
  15. package/ios/View/SmileIDSmartSelfieCaptureView.swift +108 -0
  16. package/ios/ViewManagers/SmileIDDocumentCaptureViewManager.m +7 -0
  17. package/ios/ViewManagers/SmileIDDocumentCaptureViewManager.swift +29 -0
  18. package/ios/ViewManagers/SmileIDSmartSelfieCaptureViewManager.m +7 -0
  19. package/ios/ViewManagers/SmileIDSmartSelfieCaptureViewManager.swift +36 -0
  20. package/ios/ViewModels/SmileIDProductModel.swift +2 -0
  21. package/lib/commonjs/SmileIDDocumentCaptureView.js +40 -0
  22. package/lib/commonjs/SmileIDDocumentCaptureView.js.map +1 -0
  23. package/lib/commonjs/SmileIDSmartSelfieCaptureView.js +40 -0
  24. package/lib/commonjs/SmileIDSmartSelfieCaptureView.js.map +1 -0
  25. package/lib/commonjs/index.js +14 -0
  26. package/lib/commonjs/index.js.map +1 -1
  27. package/lib/commonjs/types.js.map +1 -1
  28. package/lib/module/SmileIDDocumentCaptureView.js +30 -0
  29. package/lib/module/SmileIDDocumentCaptureView.js.map +1 -0
  30. package/lib/module/SmileIDSmartSelfieCaptureView.js +30 -0
  31. package/lib/module/SmileIDSmartSelfieCaptureView.js.map +1 -0
  32. package/lib/module/index.js +3 -1
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/types.js.map +1 -1
  35. package/lib/typescript/NativeSmileId.d.ts.map +1 -0
  36. package/lib/typescript/SmileIDBiometricKYCView.d.ts.map +1 -0
  37. package/lib/typescript/SmileIDConsentView.d.ts.map +1 -0
  38. package/lib/typescript/SmileIDDocumentCaptureView.d.ts +8 -0
  39. package/lib/typescript/SmileIDDocumentCaptureView.d.ts.map +1 -0
  40. package/lib/typescript/SmileIDDocumentVerificationView.d.ts.map +1 -0
  41. package/lib/typescript/SmileIDEnhancedDocumentVerificationView.d.ts.map +1 -0
  42. package/lib/typescript/SmileIDSmartSelfieAuthenticationView.d.ts.map +1 -0
  43. package/lib/typescript/SmileIDSmartSelfieCaptureView.d.ts +8 -0
  44. package/lib/typescript/SmileIDSmartSelfieCaptureView.d.ts.map +1 -0
  45. package/lib/typescript/SmileIDSmartSelfieEnrollmentView.d.ts.map +1 -0
  46. package/lib/typescript/{src/index.d.ts → index.d.ts} +3 -1
  47. package/lib/typescript/index.d.ts.map +1 -0
  48. package/lib/typescript/{src/types.d.ts → types.d.ts} +9 -0
  49. package/lib/typescript/types.d.ts.map +1 -0
  50. package/package.json +1 -1
  51. package/react-native-smile-id.podspec +1 -1
  52. package/src/SmileIDDocumentCaptureView.tsx +44 -0
  53. package/src/SmileIDSmartSelfieCaptureView.tsx +44 -0
  54. package/src/index.tsx +4 -0
  55. package/src/types.ts +11 -0
  56. package/lib/typescript/src/NativeSmileId.d.ts.map +0 -1
  57. package/lib/typescript/src/SmileIDBiometricKYCView.d.ts.map +0 -1
  58. package/lib/typescript/src/SmileIDConsentView.d.ts.map +0 -1
  59. package/lib/typescript/src/SmileIDDocumentVerificationView.d.ts.map +0 -1
  60. package/lib/typescript/src/SmileIDEnhancedDocumentVerificationView.d.ts.map +0 -1
  61. package/lib/typescript/src/SmileIDSmartSelfieAuthenticationView.d.ts.map +0 -1
  62. package/lib/typescript/src/SmileIDSmartSelfieEnrollmentView.d.ts.map +0 -1
  63. package/lib/typescript/src/index.d.ts.map +0 -1
  64. package/lib/typescript/src/types.d.ts.map +0 -1
  65. /package/lib/typescript/{src/NativeSmileId.d.ts → NativeSmileId.d.ts} +0 -0
  66. /package/lib/typescript/{src/SmileIDBiometricKYCView.d.ts → SmileIDBiometricKYCView.d.ts} +0 -0
  67. /package/lib/typescript/{src/SmileIDConsentView.d.ts → SmileIDConsentView.d.ts} +0 -0
  68. /package/lib/typescript/{src/SmileIDDocumentVerificationView.d.ts → SmileIDDocumentVerificationView.d.ts} +0 -0
  69. /package/lib/typescript/{src/SmileIDEnhancedDocumentVerificationView.d.ts → SmileIDEnhancedDocumentVerificationView.d.ts} +0 -0
  70. /package/lib/typescript/{src/SmileIDSmartSelfieAuthenticationView.d.ts → SmileIDSmartSelfieAuthenticationView.d.ts} +0 -0
  71. /package/lib/typescript/{src/SmileIDSmartSelfieEnrollmentView.d.ts → SmileIDSmartSelfieEnrollmentView.d.ts} +0 -0
@@ -116,17 +116,17 @@ dependencies {
116
116
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
117
117
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core"
118
118
  implementation "com.smileidentity:android-sdk:$smile_id_sdk_version"
119
+ implementation "com.google.mlkit:object-detection:17.0.2"
119
120
  implementation "com.jakewharton.timber:timber"
120
121
  implementation 'androidx.appcompat:appcompat:1.7.0'
121
- implementation("androidx.navigation:navigation-compose:2.7.7")
122
+ implementation("androidx.navigation:navigation-compose:2.8.1")
123
+ implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.6")
122
124
 
123
125
  testImplementation 'junit:junit:4.13.2'
124
126
 
125
127
  androidTestImplementation 'androidx.test.ext:junit:1.2.1'
126
128
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
127
- androidTestImplementation platform('androidx.compose:compose-bom:2024.06.00')
128
- androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
129
- androidTestImplementation platform('androidx.compose:compose-bom:2024.06.00')
129
+ androidTestImplementation platform('androidx.compose:compose-bom:2024.09.02')
130
130
  androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
131
131
 
132
132
  debugImplementation 'androidx.compose.ui:ui-tooling'
@@ -3,4 +3,4 @@ SmileId_minSdkVersion=21
3
3
  SmileId_targetSdkVersion=34
4
4
  SmileId_compileSdkVersion=34
5
5
  SmileId_ndkversion=21.4.7075529
6
- SmileId_androidVersion=10.2.5
6
+ SmileId_androidVersion=10.3.1
@@ -8,15 +8,20 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
8
8
  import com.facebook.react.uimanager.ViewManager
9
9
  import com.smileidentity.react.viewmanagers.SmileIDBiometricKYCViewManager
10
10
  import com.smileidentity.react.viewmanagers.SmileIDConsentViewManager
11
+ import com.smileidentity.react.viewmanagers.SmileIDDocumentCaptureViewManager
11
12
  import com.smileidentity.react.viewmanagers.SmileIDDocumentVerificationViewManager
12
13
  import com.smileidentity.react.viewmanagers.SmileIDEnhancedDocumentVerificationViewManager
13
14
  import com.smileidentity.react.viewmanagers.SmileIDSmartSelfieAuthenticationViewManager
15
+ import com.smileidentity.react.viewmanagers.SmileIDSmartSelfieCaptureViewManager
14
16
  import com.smileidentity.react.viewmanagers.SmileIDSmartSelfieEnrollmentViewManager
17
+ import com.smileidentity.react.views.SmileIDDocumentCaptureView
15
18
 
16
19
  class SmileIdPackage : TurboReactPackage() {
17
20
 
18
21
  override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
19
22
  listOf(
23
+ SmileIDSmartSelfieCaptureViewManager(reactContext),
24
+ SmileIDDocumentCaptureViewManager(reactContext),
20
25
  SmileIDSmartSelfieEnrollmentViewManager(reactContext),
21
26
  SmileIDSmartSelfieAuthenticationViewManager(reactContext),
22
27
  SmileIDDocumentVerificationViewManager(reactContext),
@@ -0,0 +1,54 @@
1
+ package com.smileidentity.react.utils
2
+
3
+ import com.smileidentity.react.views.DocumentCaptureResult
4
+ import com.squareup.moshi.FromJson
5
+ import com.squareup.moshi.JsonAdapter
6
+ import com.squareup.moshi.JsonReader
7
+ import com.squareup.moshi.JsonWriter
8
+ import com.squareup.moshi.Moshi
9
+ import com.squareup.moshi.ToJson
10
+ import java.io.File
11
+ import java.lang.reflect.Type
12
+
13
+ class DocumentCaptureResultAdapter : JsonAdapter<DocumentCaptureResult>() {
14
+
15
+ @FromJson
16
+ override fun fromJson(reader: JsonReader): DocumentCaptureResult {
17
+ reader.beginObject()
18
+ var frontFile: File? = null
19
+ var backFile: File? = null
20
+ while (reader.hasNext()) {
21
+ when (reader.nextName()) {
22
+ "documentFrontFile" -> frontFile = reader.nextString()?.let { File(it) }
23
+ "documentBackFile" -> backFile = reader.nextString()?.let { File(it) }
24
+ else -> reader.skipValue()
25
+ }
26
+ }
27
+ reader.endObject()
28
+ return DocumentCaptureResult(frontFile, backFile)
29
+ }
30
+
31
+ @ToJson
32
+ override fun toJson(writer: JsonWriter, value: DocumentCaptureResult?) {
33
+ if (value == null) {
34
+ writer.nullValue()
35
+ return
36
+ }
37
+ writer.beginObject()
38
+ writer.name("documentFrontFile").value(value.documentFrontFile?.absolutePath)
39
+ writer.name("documentBackFile").value(value.documentBackFile?.absolutePath)
40
+ writer.endObject()
41
+ }
42
+
43
+ companion object {
44
+ val FACTORY = object : Factory {
45
+ override fun create(
46
+ type: Type,
47
+ annotations: Set<Annotation>,
48
+ moshi: Moshi
49
+ ): JsonAdapter<*>? {
50
+ return if (type == DocumentCaptureResult::class.java) DocumentCaptureResultAdapter() else null
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,67 @@
1
+ package com.smileidentity.react.utils
2
+
3
+ import com.smileidentity.react.views.SmartSelfieCaptureResult
4
+ import com.squareup.moshi.FromJson
5
+ import com.squareup.moshi.JsonAdapter
6
+ import com.squareup.moshi.JsonReader
7
+ import com.squareup.moshi.JsonWriter
8
+ import com.squareup.moshi.Moshi
9
+ import com.squareup.moshi.ToJson
10
+ import java.io.File
11
+ import java.lang.reflect.Type
12
+
13
+ class SelfieCaptureResultAdapter : JsonAdapter<SmartSelfieCaptureResult>() {
14
+
15
+ @FromJson
16
+ override fun fromJson(reader: JsonReader): SmartSelfieCaptureResult {
17
+ reader.beginObject()
18
+ var selfieFile: File? = null
19
+ var livenessFiles: List<File>? = null
20
+ while (reader.hasNext()) {
21
+ when (reader.nextName()) {
22
+ "selfieFile" -> selfieFile = reader.nextString()?.let { File(it) }
23
+ "livenessFiles" -> {
24
+ reader.beginArray()
25
+ val files = mutableListOf<File>()
26
+ while (reader.hasNext()) {
27
+ reader.nextString()?.let { files.add(File(it)) }
28
+ }
29
+ reader.endArray()
30
+ livenessFiles = files
31
+ }
32
+ else -> reader.skipValue()
33
+ }
34
+ }
35
+ reader.endObject()
36
+ return SmartSelfieCaptureResult(selfieFile, livenessFiles)
37
+ }
38
+
39
+ @ToJson
40
+ override fun toJson(writer: JsonWriter, value: SmartSelfieCaptureResult?) {
41
+ if (value == null) {
42
+ writer.nullValue()
43
+ return
44
+ }
45
+ writer.beginObject()
46
+ writer.name("selfieFile").value(value.selfieFile?.absolutePath)
47
+ writer.name("livenessFiles")
48
+ writer.beginArray()
49
+ value.livenessFiles?.forEach { file ->
50
+ writer.value(file.absolutePath)
51
+ }
52
+ writer.endArray()
53
+ writer.endObject()
54
+ }
55
+
56
+ companion object {
57
+ val FACTORY = object : Factory {
58
+ override fun create(
59
+ type: Type,
60
+ annotations: Set<Annotation>,
61
+ moshi: Moshi
62
+ ): JsonAdapter<*>? {
63
+ return if (type == SmartSelfieCaptureResult::class.java) SelfieCaptureResultAdapter() else null
64
+ }
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,65 @@
1
+ package com.smileidentity.react.viewmanagers
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.module.annotations.ReactModule
6
+ import com.facebook.react.uimanager.SimpleViewManager
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.smileidentity.react.utils.getBoolOrDefault
9
+ import com.smileidentity.react.utils.getStringOrDefault
10
+ import com.smileidentity.react.views.SmileIDDocumentCaptureView
11
+
12
+ @ReactModule(name = SmileIDDocumentCaptureViewManager.NAME)
13
+ class SmileIDDocumentCaptureViewManager(
14
+ private val reactApplicationContext: ReactApplicationContext
15
+ ) : SimpleViewManager<SmileIDDocumentCaptureView>() {
16
+ override fun getName(): String = NAME
17
+
18
+ override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> {
19
+ return mapOf(
20
+ "onSmileResult" to mapOf(
21
+ "phasedRegistrationNames" to mapOf(
22
+ "bubbled" to "onResult"
23
+ )
24
+ )
25
+ )
26
+ }
27
+
28
+ override fun getCommandsMap(): Map<String, Int> {
29
+ return mapOf("setParams" to COMMAND_SET_PARAMS)
30
+ }
31
+
32
+ override fun receiveCommand(
33
+ view: SmileIDDocumentCaptureView,
34
+ commandId: String?,
35
+ args: ReadableArray?
36
+ ) {
37
+ super.receiveCommand(view, commandId, args)
38
+ when (commandId?.toInt()) {
39
+ COMMAND_SET_PARAMS -> {
40
+ // Extract params from args and apply to view
41
+ val params = args?.getMap(0)
42
+ params?.let {
43
+ view.userId = params.getStringOrDefault("userId")
44
+ view.jobId = params.getStringOrDefault("jobId")
45
+ view.allowAgentMode = params.getBoolOrDefault("allowAgentMode", true)
46
+ view.showAttribution = params.getBoolOrDefault("showAttribution", true)
47
+ view.showInstructions = params.getBoolOrDefault("showInstructions", true)
48
+ view.showConfirmation = params.getBoolOrDefault("showConfirmation", true)
49
+ view.allowGalleryUpload = params.getBoolOrDefault("allowGalleryUpload", false)
50
+ view.front = params.getBoolOrDefault("isDocumentFrontSide", true)
51
+ view.renderContent()
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ override fun createViewInstance(p0: ThemedReactContext): SmileIDDocumentCaptureView {
58
+ return SmileIDDocumentCaptureView(reactApplicationContext)
59
+ }
60
+
61
+ companion object {
62
+ const val NAME = "SmileIDDocumentCaptureView"
63
+ const val COMMAND_SET_PARAMS = 1
64
+ }
65
+ }
@@ -0,0 +1,63 @@
1
+ package com.smileidentity.react.viewmanagers
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.module.annotations.ReactModule
6
+ import com.facebook.react.uimanager.SimpleViewManager
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.smileidentity.react.utils.getBoolOrDefault
9
+ import com.smileidentity.react.utils.getStringOrDefault
10
+ import com.smileidentity.react.views.SmileIDSmartSelfieCaptureView
11
+
12
+ @ReactModule(name = SmileIDSmartSelfieCaptureViewManager.NAME)
13
+ class SmileIDSmartSelfieCaptureViewManager(
14
+ private val reactApplicationContext: ReactApplicationContext
15
+ ) : SimpleViewManager<SmileIDSmartSelfieCaptureView>() {
16
+ override fun getName(): String = NAME
17
+
18
+ override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> {
19
+ return mapOf(
20
+ "onSmileResult" to mapOf(
21
+ "phasedRegistrationNames" to mapOf(
22
+ "bubbled" to "onResult"
23
+ )
24
+ )
25
+ )
26
+ }
27
+
28
+ override fun getCommandsMap(): Map<String, Int> {
29
+ return mapOf("setParams" to COMMAND_SET_PARAMS)
30
+ }
31
+
32
+ override fun receiveCommand(
33
+ view: SmileIDSmartSelfieCaptureView,
34
+ commandId: String?,
35
+ args: ReadableArray?
36
+ ) {
37
+ super.receiveCommand(view, commandId, args)
38
+ when (commandId?.toInt()) {
39
+ COMMAND_SET_PARAMS -> {
40
+ // Extract params from args and apply to view
41
+ val params = args?.getMap(0)
42
+ params?.let {
43
+ view.userId = params.getStringOrDefault("userId")
44
+ view.jobId = params.getStringOrDefault("jobId")
45
+ view.allowAgentMode = params.getBoolOrDefault("allowAgentMode", false)
46
+ view.showAttribution = params.getBoolOrDefault("showAttribution", true)
47
+ view.showInstructions = params.getBoolOrDefault("showInstructions", true)
48
+ view.showConfirmation = params.getBoolOrDefault("showConfirmation", true)
49
+ view.renderContent()
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ override fun createViewInstance(p0: ThemedReactContext): SmileIDSmartSelfieCaptureView {
56
+ return SmileIDSmartSelfieCaptureView(reactApplicationContext)
57
+ }
58
+
59
+ companion object {
60
+ const val NAME = "SmileIDSmartSelfieCaptureView"
61
+ const val COMMAND_SET_PARAMS = 1
62
+ }
63
+ }
@@ -0,0 +1,109 @@
1
+ package com.smileidentity.react.views
2
+
3
+ import androidx.compose.foundation.background
4
+ import androidx.compose.foundation.layout.Box
5
+ import androidx.compose.foundation.layout.WindowInsets
6
+ import androidx.compose.foundation.layout.consumeWindowInsets
7
+ import androidx.compose.foundation.layout.fillMaxSize
8
+ import androidx.compose.foundation.layout.statusBars
9
+ import androidx.compose.foundation.layout.windowInsetsPadding
10
+ import androidx.compose.runtime.Composable
11
+ import androidx.compose.runtime.CompositionLocalProvider
12
+ import androidx.compose.runtime.saveable.rememberSaveable
13
+ import androidx.compose.ui.Modifier
14
+ import androidx.compose.ui.graphics.Color
15
+ import androidx.compose.ui.res.stringResource
16
+ import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
17
+ import com.facebook.react.bridge.ReactApplicationContext
18
+ import com.smileidentity.R
19
+ import com.smileidentity.SmileID
20
+ import com.smileidentity.SmileIDOptIn
21
+ import com.smileidentity.compose.document.DocumentCaptureScreen
22
+ import com.smileidentity.compose.document.DocumentCaptureSide
23
+ import com.smileidentity.compose.theme.colorScheme
24
+ import com.smileidentity.react.utils.DocumentCaptureResultAdapter
25
+ import com.smileidentity.util.randomJobId
26
+ import com.squareup.moshi.JsonClass
27
+ import timber.log.Timber
28
+ import java.io.File
29
+
30
+ data class DocumentCaptureResult(
31
+ val documentFrontFile: File? = null,
32
+ val documentBackFile: File? = null
33
+ )
34
+
35
+ @OptIn(SmileIDOptIn::class)
36
+ class SmileIDDocumentCaptureView(context: ReactApplicationContext) : SmileIDView(context) {
37
+ var showConfirmation: Boolean = true
38
+ var front: Boolean = true
39
+ var allowGalleryUpload: Boolean = false
40
+ var idAspectRatio: Float? = null
41
+
42
+ override fun renderContent() {
43
+ composeView.apply {
44
+ val customViewModelStoreOwner = CustomViewModelStoreOwner()
45
+ setContent {
46
+ CompositionLocalProvider(LocalViewModelStoreOwner provides customViewModelStoreOwner) {
47
+ val colorScheme = SmileID.colorScheme.copy(background = Color.White)
48
+ Box(
49
+ modifier = Modifier
50
+ .background(color = colorScheme.background)
51
+ .windowInsetsPadding(WindowInsets.statusBars)
52
+ .consumeWindowInsets(WindowInsets.statusBars)
53
+ .fillMaxSize()
54
+ ) {
55
+ RenderDocumentCaptureScreen()
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ @Composable
63
+ private fun RenderDocumentCaptureScreen() {
64
+ val jobId = jobId ?: rememberSaveable { randomJobId() }
65
+ val hero = if (front) R.drawable.si_doc_v_front_hero else R.drawable.si_doc_v_back_hero
66
+ val instructionTitle = if (front) R.string.si_doc_v_instruction_title else
67
+ R.string.si_doc_v_instruction_back_title
68
+ val instructionSubTitle = if (front) R.string.si_verify_identity_instruction_subtitle else
69
+ R.string.si_doc_v_instruction_back_subtitle
70
+ val captureTitleText = if (front) R.string.si_doc_v_capture_instructions_front_title else
71
+ R.string.si_doc_v_capture_instructions_back_title
72
+ DocumentCaptureScreen(
73
+ jobId = jobId,
74
+ side = if (front) DocumentCaptureSide.Front else DocumentCaptureSide.Back,
75
+ showInstructions = showInstructions,
76
+ showAttribution = showAttribution,
77
+ allowGallerySelection = allowGalleryUpload,
78
+ showConfirmation = showConfirmation,
79
+ showSkipButton = false,
80
+ instructionsHeroImage = hero,
81
+ instructionsTitleText = stringResource(instructionTitle),
82
+ instructionsSubtitleText = stringResource(instructionSubTitle),
83
+ captureTitleText = stringResource(captureTitleText),
84
+ knownIdAspectRatio = idAspectRatio,
85
+ onConfirm = { file -> handleConfirmation(file) },
86
+ onError = { throwable -> emitFailure(throwable) },
87
+ onSkip = { }
88
+ )
89
+ }
90
+
91
+ private fun handleConfirmation(file: File) {
92
+ val newMoshi = SmileID.moshi.newBuilder()
93
+ .add(DocumentCaptureResultAdapter.FACTORY)
94
+ .build()
95
+ val result = DocumentCaptureResult(
96
+ documentFrontFile = if (front) file else null,
97
+ documentBackFile = if (!front) file else null,
98
+ )
99
+ val json = try {
100
+ newMoshi
101
+ .adapter(DocumentCaptureResult::class.java)
102
+ .toJson(result)
103
+ } catch (e: Exception) {
104
+ Timber.w(e)
105
+ "null"
106
+ }
107
+ emitSuccess(json)
108
+ }
109
+ }
@@ -0,0 +1,213 @@
1
+ package com.smileidentity.react.views
2
+
3
+ import android.graphics.BitmapFactory
4
+ import androidx.compose.foundation.background
5
+ import androidx.compose.foundation.layout.Box
6
+ import androidx.compose.foundation.layout.WindowInsets
7
+ import androidx.compose.foundation.layout.consumeWindowInsets
8
+ import androidx.compose.foundation.layout.fillMaxSize
9
+ import androidx.compose.foundation.layout.statusBars
10
+ import androidx.compose.foundation.layout.windowInsetsPadding
11
+ import androidx.compose.material3.MaterialTheme
12
+ import androidx.compose.material3.Surface
13
+ import androidx.compose.runtime.Composable
14
+ import androidx.compose.runtime.CompositionLocalProvider
15
+ import androidx.compose.runtime.getValue
16
+ import androidx.compose.runtime.mutableStateOf
17
+ import androidx.compose.runtime.remember
18
+ import androidx.compose.runtime.saveable.rememberSaveable
19
+ import androidx.compose.runtime.setValue
20
+ import androidx.compose.runtime.toMutableStateList
21
+ import androidx.compose.ui.Modifier
22
+ import androidx.compose.ui.graphics.Color
23
+ import androidx.compose.ui.graphics.asImageBitmap
24
+ import androidx.compose.ui.graphics.painter.BitmapPainter
25
+ import androidx.compose.ui.res.stringResource
26
+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
27
+ import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
28
+ import androidx.lifecycle.viewmodel.compose.viewModel
29
+ import com.facebook.react.bridge.ReactApplicationContext
30
+ import com.smileidentity.R
31
+ import com.smileidentity.SmileID
32
+ import com.smileidentity.SmileIDOptIn
33
+ import com.smileidentity.compose.components.ImageCaptureConfirmationDialog
34
+ import com.smileidentity.compose.components.LocalMetadata
35
+ import com.smileidentity.compose.selfie.SelfieCaptureScreen
36
+ import com.smileidentity.compose.selfie.SmartSelfieInstructionsScreen
37
+ import com.smileidentity.compose.theme.colorScheme
38
+ import com.smileidentity.compose.theme.typography
39
+ import com.smileidentity.models.v2.Metadata
40
+ import com.smileidentity.react.utils.SelfieCaptureResultAdapter
41
+ import com.smileidentity.results.SmileIDResult
42
+ import com.smileidentity.util.randomJobId
43
+ import com.smileidentity.util.randomUserId
44
+ import com.smileidentity.viewmodel.SelfieUiState
45
+ import com.smileidentity.viewmodel.SelfieViewModel
46
+ import com.smileidentity.viewmodel.viewModelFactory
47
+ import java.io.File
48
+
49
+ data class SmartSelfieCaptureResult(
50
+ val selfieFile: File? = null,
51
+ val livenessFiles: List<File >? = null
52
+ )
53
+
54
+ @OptIn(SmileIDOptIn::class)
55
+ class SmileIDSmartSelfieCaptureView(context: ReactApplicationContext) : SmileIDView(context) {
56
+ var showConfirmation: Boolean = true
57
+
58
+ override fun renderContent() {
59
+ composeView.apply {
60
+ val customViewModelStoreOwner = CustomViewModelStoreOwner()
61
+ setContent {
62
+ CompositionLocalProvider(LocalViewModelStoreOwner provides customViewModelStoreOwner) {
63
+ RenderSmartSelfieCaptureContent()
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ @Composable
70
+ private fun RenderSmartSelfieCaptureContent() {
71
+ val userId = randomUserId()
72
+ val jobId = randomJobId()
73
+ val metadata = LocalMetadata.current
74
+ val viewModel: SelfieViewModel =
75
+ viewModel(
76
+ factory =
77
+ viewModelFactory {
78
+ SelfieViewModel(
79
+ isEnroll = false,
80
+ userId = userId,
81
+ jobId = jobId,
82
+ allowNewEnroll = false,
83
+ skipApiSubmission = true,
84
+ metadata = metadata,
85
+ )
86
+ },
87
+ )
88
+ val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
89
+ var acknowledgedInstructions by rememberSaveable { mutableStateOf(false) }
90
+ CompositionLocalProvider(
91
+ LocalMetadata provides remember { Metadata.default().items.toMutableStateList() },
92
+ ) {
93
+ MaterialTheme(colorScheme = SmileID.colorScheme, typography = SmileID.typography) {
94
+ Surface(content = {
95
+ when {
96
+ showInstructions && !acknowledgedInstructions -> SmartSelfieInstructionsScreen(
97
+ showAttribution = showAttribution,
98
+ ) {
99
+ acknowledgedInstructions = true
100
+ }
101
+ uiState.processingState != null -> HandleProcessingState(viewModel)
102
+ uiState.selfieToConfirm != null ->
103
+ HandleSelfieConfirmation(
104
+ showConfirmation,
105
+ uiState,
106
+ viewModel,
107
+ )
108
+
109
+ else -> RenderSelfieCaptureScreen(userId, jobId, allowAgentMode ?: true, viewModel)
110
+ }
111
+ })
112
+ }
113
+ }
114
+ }
115
+
116
+ @Composable
117
+ private fun RenderSelfieCaptureScreen(
118
+ userId: String,
119
+ jobId: String,
120
+ allowAgentMode: Boolean,
121
+ viewModel: SelfieViewModel,
122
+ ) {
123
+ Box(
124
+ modifier =
125
+ Modifier
126
+ .background(color = Color.White)
127
+ .windowInsetsPadding(WindowInsets.statusBars)
128
+ .consumeWindowInsets(WindowInsets.statusBars)
129
+ .fillMaxSize(),
130
+ ) {
131
+ SelfieCaptureScreen(
132
+ userId = userId,
133
+ jobId = jobId,
134
+ allowAgentMode = allowAgentMode,
135
+ allowNewEnroll = false,
136
+ skipApiSubmission = true,
137
+ viewModel = viewModel,
138
+ )
139
+ }
140
+ }
141
+
142
+ @Composable
143
+ private fun HandleSelfieConfirmation(
144
+ showConfirmation: Boolean,
145
+ uiState: SelfieUiState,
146
+ viewModel: SelfieViewModel,
147
+ ) {
148
+ if (showConfirmation) {
149
+ ImageCaptureConfirmationDialog(
150
+ titleText = stringResource(R.string.si_smart_selfie_confirmation_dialog_title),
151
+ subtitleText =
152
+ stringResource(
153
+ R.string.si_smart_selfie_confirmation_dialog_subtitle,
154
+ ),
155
+ painter =
156
+ BitmapPainter(
157
+ BitmapFactory
158
+ .decodeFile(uiState.selfieToConfirm!!.absolutePath)
159
+ .asImageBitmap(),
160
+ ),
161
+ confirmButtonText =
162
+ stringResource(
163
+ R.string.si_smart_selfie_confirmation_dialog_confirm_button,
164
+ ),
165
+ onConfirm = {
166
+ viewModel.submitJob()
167
+ },
168
+ retakeButtonText =
169
+ stringResource(
170
+ R.string.si_smart_selfie_confirmation_dialog_retake_button,
171
+ ),
172
+ onRetake = viewModel::onSelfieRejected,
173
+ scaleFactor = 1.25f,
174
+ )
175
+ } else {
176
+ viewModel.submitJob()
177
+ }
178
+ }
179
+
180
+ @Composable
181
+ private fun HandleProcessingState(viewModel: SelfieViewModel) {
182
+ viewModel.onFinished { res ->
183
+ when (res) {
184
+ is SmileIDResult.Success -> {
185
+ val result =
186
+ SmartSelfieCaptureResult(
187
+ selfieFile = res.data.selfieFile,
188
+ livenessFiles = res.data.livenessFiles,
189
+ )
190
+ val newMoshi =
191
+ SmileID.moshi
192
+ .newBuilder()
193
+ .add(SelfieCaptureResultAdapter.FACTORY)
194
+ .build()
195
+ val json =
196
+ try {
197
+ newMoshi
198
+ .adapter(SmartSelfieCaptureResult::class.java)
199
+ .toJson(result)
200
+ } catch (e: Exception) {
201
+ emitFailure(e)
202
+ return@onFinished
203
+ }
204
+ json?.let { js ->
205
+ emitSuccess(js)
206
+ }
207
+ }
208
+
209
+ is SmileIDResult.Error -> emitFailure(res.throwable)
210
+ }
211
+ }
212
+ }
213
+ }
@@ -1,5 +1,6 @@
1
1
  package com.smileidentity.react.views
2
2
 
3
+ import android.annotation.SuppressLint
3
4
  import android.view.Choreographer
4
5
  import android.view.ViewGroup
5
6
  import android.widget.LinearLayout
@@ -10,9 +11,12 @@ import com.facebook.react.bridge.ReactApplicationContext
10
11
  import com.facebook.react.bridge.ReactContext
11
12
  import com.facebook.react.bridge.WritableMap
12
13
  import com.facebook.react.uimanager.events.RCTEventEmitter
14
+ import com.smileidentity.SmileID
13
15
  import com.smileidentity.models.JobType
16
+ import com.smileidentity.react.utils.DocumentCaptureResultAdapter
14
17
  import timber.log.Timber
15
18
 
19
+ @SuppressLint("CheckResult")
16
20
  abstract class SmileIDView(context: ReactApplicationContext) : LinearLayout(context) {
17
21
  lateinit var composeView: ComposeView
18
22
  var userId: String? = null
@@ -20,8 +24,8 @@ abstract class SmileIDView(context: ReactApplicationContext) : LinearLayout(cont
20
24
  private var jobType: JobType? = null
21
25
  var allowAgentMode: Boolean? = false
22
26
  var allowNewEnroll: Boolean? = false
23
- var showInstructions: Boolean? = true
24
- var showAttribution: Boolean? = true
27
+ var showInstructions: Boolean = true
28
+ var showAttribution: Boolean = true
25
29
  var extraPartnerParams: Map<String, String>? = null
26
30
  private var eventEmitter: RCTEventEmitter
27
31
  private var productThrowable: Throwable? = null
@@ -31,6 +35,7 @@ abstract class SmileIDView(context: ReactApplicationContext) : LinearLayout(cont
31
35
  ViewGroup.LayoutParams.WRAP_CONTENT,
32
36
  ViewGroup.LayoutParams.WRAP_CONTENT
33
37
  )
38
+
34
39
  eventEmitter = (context as ReactContext).getJSModule(RCTEventEmitter::class.java);
35
40
  setLayoutParams(layoutParams)
36
41
  orientation = VERTICAL
@@ -33,7 +33,7 @@ class RNSmileID: NSObject {
33
33
  )
34
34
  resolve(nil)
35
35
  }
36
-
36
+
37
37
  @objc(initialize:withResolver:withRejecter:)
38
38
  func initialize(useSandBox: Bool, resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
39
39
  SmileID.initialize(useSandbox: useSandBox)