@finos_sdk/sdk-ekyc 1.5.0 → 1.5.1

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.
@@ -66,7 +66,10 @@ import java.io.FileInputStream
66
66
  import java.io.InputStream
67
67
  import java.util.Date
68
68
  import android.app.Activity
69
+ import android.app.Application
70
+ import android.os.Bundle
69
71
  import android.view.ViewGroup
72
+ import java.lang.ref.WeakReference
70
73
 
71
74
  @ReactModule(name = EKYCModule.NAME)
72
75
  class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
@@ -74,10 +77,71 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
74
77
  companion object {
75
78
  const val NAME = "EKYCModule"
76
79
  private const val TAG = "EKYCModule"
80
+
81
+ // Full class name của SDK activity duy nhất
82
+ private const val SDK_EKYC_ACTIVITY = "finos.sdk.ekyc.sdkui.SDKeKYCActivity"
77
83
  }
78
84
 
79
85
  override fun getName(): String = NAME
80
86
 
87
+ /**
88
+ * WeakReference tới SDK activity đang foreground.
89
+ * Được set/clear bởi ActivityLifecycleCallbacks khi SDK activity resume/pause.
90
+ * @Volatile: viết từ UI thread (lifecycle callback), đọc từ JS bridge thread (showRNExitSheet).
91
+ * WeakReference để tránh memory leak nếu activity bị destroy mà chưa clear.
92
+ */
93
+ @Volatile
94
+ private var activeSdkActivity: WeakReference<Activity>? = null
95
+
96
+ private val sdkActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
97
+ override fun onActivityResumed(activity: Activity) {
98
+ if (isSDKActivity(activity)) {
99
+ activeSdkActivity = WeakReference(activity)
100
+ Log.d(TAG, "📌 SDK Activity resumed: ${activity.javaClass.simpleName}")
101
+ }
102
+ }
103
+ override fun onActivityPaused(activity: Activity) {
104
+ if (activeSdkActivity?.get() == activity) {
105
+ activeSdkActivity = null
106
+ Log.d(TAG, "📌 SDK Activity paused: ${activity.javaClass.simpleName}")
107
+ }
108
+ }
109
+ override fun onActivityDestroyed(activity: Activity) {
110
+ if (activeSdkActivity?.get() == activity) {
111
+ activeSdkActivity = null
112
+ Log.d(TAG, "📌 SDK Activity destroyed: ${activity.javaClass.simpleName}")
113
+ }
114
+ }
115
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
116
+ override fun onActivityStarted(activity: Activity) {}
117
+ override fun onActivityStopped(activity: Activity) {}
118
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
119
+ }
120
+
121
+ private fun isSDKActivity(activity: Activity): Boolean {
122
+ return activity.javaClass.name == SDK_EKYC_ACTIVITY
123
+ }
124
+
125
+ init {
126
+ // Đăng ký lifecycle callback để track SDK activity tự động
127
+ val app = reactContext.applicationContext as? Application
128
+ app?.registerActivityLifecycleCallbacks(sdkActivityLifecycleCallbacks)
129
+ ?: Log.w(TAG, "⚠️ Could not register ActivityLifecycleCallbacks")
130
+ }
131
+
132
+ /**
133
+ * Lấy SDK activity đang active, fallback về getTrueCurrentActivity() nếu không có.
134
+ */
135
+ private fun getSDKActivity(): Activity? {
136
+ val sdk = activeSdkActivity?.get()
137
+ if (sdk != null && !sdk.isFinishing && !sdk.isDestroyed) {
138
+ Log.d(TAG, "✅ Using tracked SDK Activity: ${sdk.javaClass.simpleName}")
139
+ return sdk
140
+ }
141
+ Log.d(TAG, "⚠️ No active SDK activity tracked, falling back to getTrueCurrentActivity")
142
+ return getTrueCurrentActivity()
143
+ }
144
+
81
145
  /**
82
146
  * Reference tới BottomSheetDialog hiện đang hiển thị.
83
147
  * @Volatile: resolveExit() chạy trên JS bridge thread, đọc field này được viết bởi UI thread.
@@ -199,9 +263,8 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
199
263
 
200
264
  @ReactMethod
201
265
  fun showRNExitSheet(bundleName: String, initialProps: ReadableMap, promise: Promise) {
202
- // Fix: Move activity detection to UI thread to avoid race conditions and ensure correct window attachment
203
266
  Handler(Looper.getMainLooper()).post {
204
- val activity = getTrueCurrentActivity() ?: run {
267
+ val activity = getSDKActivity() ?: run {
205
268
  Log.e(TAG, "❌ showRNExitSheet failed: No visible activity found")
206
269
  promise.reject("NO_ACTIVITY", "No visible activity found")
207
270
  return@post
@@ -2733,6 +2796,10 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
2733
2796
  override fun onCatalystInstanceDestroy() {
2734
2797
  super.onCatalystInstanceDestroy()
2735
2798
  SDKeKYCExitHandlerManager.uiListener = null
2799
+ // Unregister lifecycle callback để tránh memory leak
2800
+ val app = reactApplicationContext.applicationContext as? Application
2801
+ app?.unregisterActivityLifecycleCallbacks(sdkActivityLifecycleCallbacks)
2802
+ activeSdkActivity = null
2736
2803
  // Dismiss sheet nếu vẫn đang hiển thị khi RN catalyst instance destroy
2737
2804
  val sheet = currentExitSheet
2738
2805
  currentExitSheet = null
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -5,7 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const react_native_1 = require("react-native");
8
- const FinosEKYCModule_1 = require("../modules/FinosEKYCModule");
8
+ // Dùng NativeModules trực tiếp để tránh circular dependency với FinosEKYCModule
9
+ // (ExitConfirmSheet được load qua ExitSheetWrapper được import bởi FinosEKYCModule)
9
10
  const BellIcon = () => (<react_native_1.View style={styles.bellWrapper}>
10
11
  <react_native_1.View style={styles.bellMount}/>
11
12
  <react_native_1.View style={styles.bellBody}/>
@@ -17,10 +18,12 @@ const ExitConfirmSheet = (props) => {
17
18
  const confirmText = props.confirmText || 'Đồng ý';
18
19
  const cancelText = props.cancelText || 'Ở lại';
19
20
  const handleConfirm = async () => {
20
- await FinosEKYCModule_1.FinosEKYC.resolveExit('CONFIRM');
21
+ var _a, _b;
22
+ await ((_b = (_a = react_native_1.NativeModules.EKYCModule) === null || _a === void 0 ? void 0 : _a.resolveExit) === null || _b === void 0 ? void 0 : _b.call(_a, 'CONFIRM'));
21
23
  };
22
24
  const handleCancel = async () => {
23
- await FinosEKYCModule_1.FinosEKYC.resolveExit('CANCEL');
25
+ var _a, _b;
26
+ await ((_b = (_a = react_native_1.NativeModules.EKYCModule) === null || _a === void 0 ? void 0 : _a.resolveExit) === null || _b === void 0 ? void 0 : _b.call(_a, 'CANCEL'));
24
27
  };
25
28
  return (<react_native_1.View style={styles.container} onLayout={(e) => {
26
29
  var _a, _b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -6,7 +6,9 @@ import {
6
6
  TouchableOpacity,
7
7
  NativeModules,
8
8
  } from 'react-native';
9
- import { FinosEKYC } from '../modules/FinosEKYCModule';
9
+
10
+ // Dùng NativeModules trực tiếp để tránh circular dependency với FinosEKYCModule
11
+ // (ExitConfirmSheet được load qua ExitSheetWrapper được import bởi FinosEKYCModule)
10
12
 
11
13
  const BellIcon = () => (
12
14
  <View style={styles.bellWrapper}>
@@ -23,11 +25,11 @@ const ExitConfirmSheet = (props: any) => {
23
25
  const cancelText = props.cancelText || 'Ở lại';
24
26
 
25
27
  const handleConfirm = async () => {
26
- await FinosEKYC.resolveExit('CONFIRM');
28
+ await NativeModules.EKYCModule?.resolveExit?.('CONFIRM');
27
29
  };
28
30
 
29
31
  const handleCancel = async () => {
30
- await FinosEKYC.resolveExit('CANCEL');
32
+ await NativeModules.EKYCModule?.resolveExit?.('CANCEL');
31
33
  };
32
34
 
33
35
  return (