@digitalshieldfe/react-native-backup-card-sdk 0.1.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.
@@ -0,0 +1,20 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "BackupCardSdk"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://www.ds.com.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ install_modules_dependencies(s)
20
+ end
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ds
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # react-native-backup-card-sdk
2
+
3
+ ds react-native backup-card sdk
4
+
5
+ ## Installation
6
+
7
+
8
+ ```sh
9
+ npm install react-native-backup-card-sdk
10
+ ```
11
+
12
+
13
+ ## Usage
14
+
15
+
16
+ ```js
17
+ import { multiply } from 'react-native-backup-card-sdk';
18
+
19
+ // ...
20
+
21
+ const result = multiply(3, 7);
22
+ ```
23
+
24
+
25
+ ## Contributing
26
+
27
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
28
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
+ - [Code of conduct](CODE_OF_CONDUCT.md)
30
+
31
+ ## License
32
+
33
+ MIT
34
+
35
+ ---
36
+
37
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,69 @@
1
+ buildscript {
2
+ ext.BackupCardSdk = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+
14
+ return BackupCardSdk[prop]
15
+ }
16
+
17
+ repositories {
18
+ mavenLocal()
19
+ google()
20
+ mavenCentral()
21
+ }
22
+
23
+ dependencies {
24
+ classpath "com.android.tools.build:gradle:8.7.2"
25
+ // noinspection DifferentKotlinGradleVersion
26
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
27
+ }
28
+ }
29
+
30
+
31
+ apply plugin: "com.android.library"
32
+ apply plugin: "kotlin-android"
33
+
34
+ apply plugin: "com.facebook.react"
35
+
36
+ android {
37
+ namespace "com.ziancube.reactnativebackupcardsdk"
38
+
39
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
40
+
41
+ defaultConfig {
42
+ minSdkVersion getExtOrDefault("minSdkVersion")
43
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
44
+ }
45
+
46
+ buildFeatures {
47
+ buildConfig true
48
+ }
49
+
50
+ buildTypes {
51
+ release {
52
+ minifyEnabled false
53
+ }
54
+ }
55
+
56
+ lint {
57
+ disable "GradleCompatible"
58
+ }
59
+
60
+ compileOptions {
61
+ sourceCompatibility JavaVersion.VERSION_1_8
62
+ targetCompatibility JavaVersion.VERSION_1_8
63
+ }
64
+ }
65
+
66
+ dependencies {
67
+ implementation("com.ziancube:backupcardsdk:1.0.0")
68
+ implementation("com.facebook.react:react-android")
69
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,183 @@
1
+ package com.ziancube.reactnativebackupcardsdk
2
+
3
+ import android.content.Intent
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.BaseActivityEventListener
6
+ import com.facebook.react.bridge.Promise
7
+ import com.facebook.react.bridge.ReadableArray
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.facebook.react.bridge.WritableMap
10
+ import com.facebook.react.bridge.WritableArray
11
+ import com.facebook.react.bridge.LifecycleEventListener
12
+ import com.ziancube.backupcardsdk.ApduLogger
13
+ import com.ziancube.backupcardsdk.BackupCardSdk
14
+ import com.ziancube.backupcardsdk.NfcTouchListener
15
+ import com.ziancube.backupcardsdk.CardInfo
16
+ import kotlinx.coroutines.CoroutineDispatcher
17
+ import kotlinx.coroutines.CoroutineScope
18
+ import kotlinx.coroutines.Dispatchers
19
+ import kotlinx.coroutines.SupervisorJob
20
+ import kotlinx.coroutines.cancel
21
+ import kotlinx.coroutines.launch
22
+ import kotlinx.coroutines.withContext
23
+
24
+ fun CardInfo.toWritableMap(): WritableMap {
25
+ val map = Arguments.createMap()
26
+ map.putString("serialNumber", serialNumber)
27
+ map.putInt("pinRetryCount", pinRetryCount ?: -1)
28
+ map.putBoolean("isNewCard", isNewCard ?: false)
29
+ return map
30
+ }
31
+
32
+ fun ReadableArray.toByteArray(): ByteArray {
33
+ val bytes = ByteArray(size())
34
+ for (i in 0 until size()) {
35
+ bytes[i] = getInt(i).toByte()
36
+ }
37
+ return bytes
38
+ }
39
+
40
+ fun ByteArray?.toWritableArrayOrNull(): WritableArray? {
41
+ if (this == null) return null
42
+ val array = Arguments.createArray()
43
+ for (b in this) {
44
+ array.pushInt(b.toInt() and 0xFF)
45
+ }
46
+ return array
47
+ }
48
+
49
+
50
+ class BackupCardSdkModule(val reactContext: ReactApplicationContext) :
51
+ NativeBackupCardSdkSpec(reactContext) ,LifecycleEventListener
52
+ {
53
+ private lateinit var backupCardSdk: BackupCardSdk
54
+ private val tag = "BackupCardSdkModule"
55
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
56
+ override fun invalidate() {
57
+ super.invalidate()
58
+ scope.cancel()
59
+ }
60
+ private val mActivityEventListener =
61
+ object : BaseActivityEventListener() {
62
+ override fun onNewIntent(intent: Intent) {
63
+ super.onNewIntent(intent)
64
+ backupCardSdk.handleIntent(intent)
65
+ }
66
+ }
67
+
68
+ override fun initialize() {
69
+ super.initialize()
70
+ Utils.init(reactContext)
71
+ Utils.getActivityLifecycle()
72
+ val activity = Utils.getTopActivity()
73
+ if (activity == null) {
74
+ return
75
+ }
76
+ val apduLogger =
77
+ object : ApduLogger {
78
+ override fun log(message: String, isSent: Boolean, isSuccess: Boolean) {
79
+ val params =
80
+ Arguments.createMap()
81
+ .apply {
82
+ putString("message", message)
83
+ putBoolean("isSent", isSent)
84
+ putBoolean("isSuccess", isSuccess)
85
+ }
86
+ .copy()
87
+ emitOnApduLog(params)
88
+ }
89
+ }
90
+ val nfcTouchListener =
91
+ object : NfcTouchListener {
92
+ override fun onTouch(isBackupCard: Boolean) {
93
+ val params =
94
+ Arguments.createMap()
95
+ .apply { putBoolean("isBackupCard", isBackupCard) }
96
+ .copy()
97
+ emitOnNfcTouch(params)
98
+ }
99
+ }
100
+ backupCardSdk = BackupCardSdk(activity, apduLogger, nfcTouchListener)
101
+ reactContext.addActivityEventListener(mActivityEventListener)
102
+ reactContext.addLifecycleEventListener(this)
103
+ }
104
+
105
+ companion object {
106
+ const val NAME = NativeBackupCardSdkSpec.NAME
107
+ }
108
+
109
+ override fun getCardInfo(promise: Promise) =
110
+ promise.launchSuspend(
111
+ block = { backupCardSdk.getCardInfo() },
112
+ transform = { info -> info?.toWritableMap() }
113
+ )
114
+
115
+ override fun resetCard(promise: Promise) =
116
+ promise.launchSuspend(
117
+ block = { backupCardSdk.resetCard() },
118
+ transform = { result -> result }
119
+ )
120
+
121
+ override fun activateCard(pwd: String, promise: Promise) =
122
+ promise.launchSuspend(
123
+ block = { backupCardSdk.activateCard(pwd) },
124
+ transform = { result -> result }
125
+ )
126
+
127
+ override fun changePin(oldPin: String, newPin: String, promise: Promise) =
128
+ promise.launchSuspend(
129
+ block = { backupCardSdk.changePin(oldPin, newPin) },
130
+ transform = { result -> result }
131
+ )
132
+
133
+ override fun checkSlotEmpty(slotId: Double, pwd: String, promise: Promise) =
134
+ promise.launchSuspend(
135
+ block = { backupCardSdk.checkSlotEmpty(slotId.toInt(), pwd) },
136
+ transform = { result -> result }
137
+ )
138
+
139
+ override fun writeSlot(slotIndex: Double, data: ReadableArray, pwd: String, promise: Promise) =
140
+ promise.launchSuspend(
141
+ block = { backupCardSdk.writeSlot(slotIndex.toInt(), data.toByteArray(), pwd) },
142
+ transform = { result -> result }
143
+ )
144
+
145
+ override fun readSlot(slotIndex: Double, pwd: String, promise: Promise) =
146
+ promise.launchSuspend(
147
+ block = { backupCardSdk.readSlot(slotIndex.toInt(), pwd) },
148
+ transform = { result -> result.toWritableArrayOrNull() }
149
+ )
150
+
151
+
152
+
153
+ private fun <T, R> Promise.launchSuspend(
154
+ dispatcher: CoroutineDispatcher = Dispatchers.IO,
155
+ block: suspend () -> T,
156
+ transform: (T) -> R,
157
+ errorCode: (Throwable) -> String = { "E_UNEXPECTED" }
158
+ ) {
159
+ scope.launch {
160
+ try {
161
+ val result = withContext(dispatcher) { block() }
162
+
163
+ val mapped = transform(result)
164
+
165
+ resolve(mapped)
166
+ } catch (t: Throwable) {
167
+ reject(errorCode(t), t.message, t)
168
+ }
169
+ }
170
+ }
171
+
172
+ override fun onHostResume() {
173
+ backupCardSdk.onActivityResumed()
174
+ }
175
+
176
+ override fun onHostPause() {
177
+ backupCardSdk.onActivityPaused()
178
+ }
179
+
180
+ override fun onHostDestroy() {
181
+ backupCardSdk.onActivityDestroyed()
182
+ }
183
+ }
@@ -0,0 +1,31 @@
1
+ package com.ziancube.reactnativebackupcardsdk
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class BackupCardSdkPackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == BackupCardSdkModule.NAME) {
13
+ BackupCardSdkModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
20
+ mapOf(
21
+ BackupCardSdkModule.NAME to ReactModuleInfo(
22
+ name = BackupCardSdkModule.NAME,
23
+ className = BackupCardSdkModule.NAME,
24
+ canOverrideExistingModule = false,
25
+ needsEagerInit = false,
26
+ isCxxModule = false,
27
+ isTurboModule = true
28
+ )
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,452 @@
1
+ package com.ziancube.reactnativebackupcardsdk;
2
+
3
+ import android.annotation.SuppressLint;
4
+ import android.app.Activity;
5
+ import android.app.ActivityManager;
6
+ import android.app.Application;
7
+ import android.app.Application.ActivityLifecycleCallbacks;
8
+ import android.content.Context;
9
+ import android.os.Bundle;
10
+
11
+ import androidx.annotation.NonNull;
12
+ import androidx.annotation.Nullable;
13
+
14
+ import com.facebook.react.bridge.ReactApplicationContext;
15
+
16
+ import org.jetbrains.annotations.NotNull;
17
+
18
+ import java.lang.reflect.Field;
19
+ import java.lang.reflect.InvocationTargetException;
20
+ import java.util.HashMap;
21
+ import java.util.LinkedList;
22
+ import java.util.List;
23
+ import java.util.Map;
24
+ import java.util.regex.Matcher;
25
+ import java.util.regex.Pattern;
26
+
27
+ /**
28
+ * <pre>
29
+ * author:
30
+ * ___ ___ ___ ___
31
+ * _____ / /\ /__/\ /__/| / /\
32
+ * / /::\ / /::\ \ \:\ | |:| / /:/
33
+ * / /:/\:\ ___ ___ / /:/\:\ \ \:\ | |:| /__/::\
34
+ * / /:/~/::\ /__/\ / /\ / /:/~/::\ _____\__\:\ __| |:| \__\/\:\
35
+ * /__/:/ /:/\:| \ \:\ / /:/ /__/:/ /:/\:\ /__/::::::::\ /__/\_|:|____ \ \:\
36
+ * \ \:\/:/~/:/ \ \:\ /:/ \ \:\/:/__\/ \ \:\~~\~~\/ \ \:\/:::::/ \__\:\
37
+ * \ \::/ /:/ \ \:\/:/ \ \::/ \ \:\ ~~~ \ \::/~~~~ / /:/
38
+ * \ \:\/:/ \ \::/ \ \:\ \ \:\ \ \:\ /__/:/
39
+ * \ \::/ \__\/ \ \:\ \ \:\ \ \:\ \__\/
40
+ * \__\/ \__\/ \__\/ \__\/
41
+ * blog : http://blankj.com
42
+ * time : 16/12/08
43
+ * desc : utils about initialization
44
+ * </pre>
45
+ */
46
+ public final class Utils {
47
+
48
+ @SuppressLint("StaticFieldLeak")
49
+ private static Application sApplication;
50
+
51
+ static final ActivityLifecycleImpl ACTIVITY_LIFECYCLE = new ActivityLifecycleImpl();
52
+
53
+ private Utils() {
54
+ throw new UnsupportedOperationException("u can't instantiate me...");
55
+ }
56
+
57
+ /**
58
+ * Init utils.
59
+ *
60
+ * <p>Init it in the class of Application.
61
+ *
62
+ * @param context context
63
+ */
64
+ public static void init(@NonNull final ReactApplicationContext context) {
65
+ Utils.getActivityLifecycle().setTopActivity(context.getCurrentActivity());
66
+ init((Application) context.getApplicationContext());
67
+ }
68
+
69
+ /**
70
+ * Init utils.
71
+ *
72
+ * <p>Init it in the class of Application.
73
+ *
74
+ * @param app application
75
+ */
76
+ public static void init(@NonNull final Application app) {
77
+ if (sApplication == null) {
78
+ Utils.sApplication = app;
79
+ Utils.sApplication.registerActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Return the context of Application object.
85
+ *
86
+ * @return the context of Application object
87
+ */
88
+ public static Application getApp() {
89
+ if (sApplication != null) {
90
+ return sApplication;
91
+ }
92
+ try {
93
+ @SuppressLint("PrivateApi")
94
+ Class<?> activityThread = Class.forName("android.app.ActivityThread");
95
+ Object at = activityThread.getMethod("currentActivityThread").invoke(null);
96
+ Object app = activityThread.getMethod("getApplication").invoke(at);
97
+ if (app == null) {
98
+ throw new NullPointerException("u should init first");
99
+ }
100
+ init((Application) app);
101
+ return sApplication;
102
+ } catch (NoSuchMethodException e) {
103
+ e.printStackTrace();
104
+ } catch (IllegalAccessException e) {
105
+ e.printStackTrace();
106
+ } catch (InvocationTargetException e) {
107
+ e.printStackTrace();
108
+ } catch (ClassNotFoundException e) {
109
+ e.printStackTrace();
110
+ }
111
+ throw new NullPointerException("u should init first");
112
+ }
113
+
114
+ public static ActivityLifecycleImpl getActivityLifecycle() {
115
+ return ACTIVITY_LIFECYCLE;
116
+ }
117
+
118
+ public static LinkedList<Activity> getActivityList() {
119
+ return ACTIVITY_LIFECYCLE.mActivityList;
120
+ }
121
+
122
+ @NotNull
123
+ public static Context getTopActivityOrApp() {
124
+ if (isAppForeground()) {
125
+ Activity topActivity = ACTIVITY_LIFECYCLE.getTopActivity();
126
+ return topActivity == null ? Utils.getApp() : topActivity;
127
+ } else {
128
+ return Utils.getApp();
129
+ }
130
+ }
131
+
132
+ @Nullable
133
+ public static Activity getTopActivity() {
134
+ if (!isAppForeground()) {
135
+ return null;
136
+ }
137
+ return ACTIVITY_LIFECYCLE.getTopActivity();
138
+ }
139
+
140
+ /**
141
+ * Finish all of activities.
142
+ */
143
+ public static void finishAllActivities() {
144
+ finishAllActivities(false);
145
+ }
146
+
147
+ /**
148
+ * Finish all of activities.
149
+ *
150
+ * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.
151
+ */
152
+ public static void finishAllActivities(final boolean isLoadAnim) {
153
+ List<Activity> activityList = getActivityList();
154
+ for (Activity act : activityList) {
155
+ // sActivityList remove the index activity at onActivityDestroyed
156
+ act.finish();
157
+ if (!isLoadAnim) {
158
+ act.overridePendingTransition(0, 0);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Finish all of activities except the newest activity.
165
+ */
166
+ public static void finishAllActivitiesExceptNewest() {
167
+ finishAllActivitiesExceptNewest(false);
168
+ }
169
+
170
+ /**
171
+ * Finish all of activities except the newest activity.
172
+ *
173
+ * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.
174
+ */
175
+ public static void finishAllActivitiesExceptNewest(final boolean isLoadAnim) {
176
+ List<Activity> activities = getActivityList();
177
+ for (int i = 1; i < activities.size(); i++) {
178
+ finishActivity(activities.get(i), isLoadAnim);
179
+ }
180
+ }
181
+
182
+ public static void finishActivity(@NonNull final Activity activity) {
183
+ finishActivity(activity, false);
184
+ }
185
+
186
+ public static void finishActivity(@NonNull final Activity activity, final boolean isLoadAnim) {
187
+ activity.finish();
188
+ if (!isLoadAnim) {
189
+ activity.overridePendingTransition(0, 0);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Finish the activity.
195
+ *
196
+ * @param clz The activity class.
197
+ */
198
+ public static void finishActivity(@NonNull final Class<? extends Activity> clz) {
199
+ finishActivity(clz, false);
200
+ }
201
+
202
+ /**
203
+ * Finish the activity.
204
+ *
205
+ * @param clz The activity class.
206
+ * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.
207
+ */
208
+ public static void finishActivity(
209
+ @NonNull final Class<? extends Activity> clz, final boolean isLoadAnim) {
210
+ List<Activity> activities = getActivityList();
211
+ for (Activity activity : activities) {
212
+ if (activity.getClass().equals(clz)) {
213
+ activity.finish();
214
+ if (!isLoadAnim) {
215
+ activity.overridePendingTransition(0, 0);
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Finish the activities whose type not equals the activity class.
223
+ *
224
+ * @param clz The activity class.
225
+ */
226
+ public static void finishOtherActivities(@NonNull final Class<? extends Activity> clz) {
227
+ finishOtherActivities(clz, false);
228
+ }
229
+
230
+ /**
231
+ * Finish the activities whose type not equals the activity class.
232
+ *
233
+ * @param clz The activity class.
234
+ * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.
235
+ */
236
+ public static void finishOtherActivities(
237
+ @NonNull final Class<? extends Activity> clz, final boolean isLoadAnim) {
238
+ List<Activity> activities = getActivityList();
239
+ for (Activity act : activities) {
240
+ if (!act.getClass().equals(clz)) {
241
+ finishActivity(act, isLoadAnim);
242
+ }
243
+ }
244
+ }
245
+
246
+ public static boolean isAppForeground() {
247
+ ActivityManager am =
248
+ (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);
249
+ if (am == null) {
250
+ return false;
251
+ }
252
+ List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();
253
+ if (info == null || info.size() == 0) {
254
+ return false;
255
+ }
256
+ for (ActivityManager.RunningAppProcessInfo aInfo : info) {
257
+ if (aInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
258
+ return aInfo.processName.equals(Utils.getApp().getPackageName());
259
+ }
260
+ }
261
+ return false;
262
+ }
263
+
264
+ static class ActivityLifecycleImpl implements ActivityLifecycleCallbacks {
265
+
266
+ final LinkedList<Activity> mActivityList = new LinkedList<>();
267
+ final HashMap<Object, OnAppStatusChangedListener> mStatusListenerMap = new HashMap<>();
268
+
269
+ private int mForegroundCount = 0;
270
+ private int mConfigCount = 0;
271
+
272
+ void addListener(final Object object, final OnAppStatusChangedListener listener) {
273
+ synchronized (mStatusListenerMap) {
274
+ mStatusListenerMap.put(object, listener);
275
+ }
276
+ }
277
+
278
+ void removeListener(final Object object) {
279
+ synchronized (mStatusListenerMap) {
280
+ mStatusListenerMap.remove(object);
281
+ }
282
+ }
283
+
284
+ @Override
285
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
286
+ setTopActivity(activity);
287
+ }
288
+
289
+ @Override
290
+ public void onActivityStarted(Activity activity) {
291
+ setTopActivity(activity);
292
+ if (mForegroundCount <= 0) {
293
+ postStatus(true);
294
+ }
295
+ if (mConfigCount < 0) {
296
+ ++mConfigCount;
297
+ } else {
298
+ ++mForegroundCount;
299
+ }
300
+ }
301
+
302
+ @Override
303
+ public void onActivityResumed(Activity activity) {
304
+ setTopActivity(activity);
305
+ }
306
+
307
+ @Override
308
+ public void onActivityPaused(Activity activity) {
309
+ /**/
310
+ }
311
+
312
+ @Override
313
+ public void onActivityStopped(Activity activity) {
314
+ if (activity.isChangingConfigurations()) {
315
+ --mConfigCount;
316
+ } else {
317
+ --mForegroundCount;
318
+ if (mForegroundCount <= 0) {
319
+ postStatus(false);
320
+ }
321
+ }
322
+ }
323
+
324
+ @Override
325
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
326
+ /**/
327
+ }
328
+
329
+ @Override
330
+ public void onActivityDestroyed(Activity activity) {
331
+ synchronized (mActivityList) {
332
+ mActivityList.remove(activity);
333
+ }
334
+ }
335
+
336
+ private void postStatus(final boolean isForeground) {
337
+ synchronized (mStatusListenerMap) {
338
+ if (mStatusListenerMap.isEmpty()) {
339
+ return;
340
+ }
341
+ for (OnAppStatusChangedListener onAppStatusChangedListener :
342
+ mStatusListenerMap.values()) {
343
+ if (onAppStatusChangedListener == null) {
344
+ return;
345
+ }
346
+ if (isForeground) {
347
+ onAppStatusChangedListener.onForeground();
348
+ } else {
349
+ onAppStatusChangedListener.onBackground();
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ private void setTopActivity(final Activity activity) {
356
+ if (activity == null || mActivityList == null) {
357
+ return;
358
+ }
359
+ try {
360
+ synchronized (mActivityList) {
361
+ if (mActivityList.contains(activity)) {
362
+ if (!mActivityList.isEmpty() && !activity.equals(mActivityList.getLast())) {
363
+ mActivityList.remove(activity);
364
+ mActivityList.addLast(activity);
365
+ }
366
+ } else {
367
+ mActivityList.addLast(activity);
368
+ }
369
+ }
370
+ } catch (Exception e) {
371
+ android.util.Log.w("OneKey", "setTopActivity error: " + e.getMessage());
372
+ }
373
+ }
374
+
375
+ @Nullable
376
+ Activity getTopActivity() {
377
+ synchronized (mActivityList) {
378
+ if (!mActivityList.isEmpty()) {
379
+ final Activity topActivity = mActivityList.getLast();
380
+ if (topActivity != null) {
381
+ return topActivity;
382
+ }
383
+ }
384
+ }
385
+ // using reflect to get top activity
386
+ try {
387
+ @SuppressLint("PrivateApi")
388
+ Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
389
+ Object activityThread =
390
+ activityThreadClass.getMethod("currentActivityThread").invoke(null);
391
+ Field activitiesField = activityThreadClass.getDeclaredField("mActivityList");
392
+ activitiesField.setAccessible(true);
393
+ Map activities = (Map) activitiesField.get(activityThread);
394
+ if (activities == null) {
395
+ return null;
396
+ }
397
+ for (Object activityRecord : activities.values()) {
398
+ Class activityRecordClass = activityRecord.getClass();
399
+ Field pausedField = activityRecordClass.getDeclaredField("paused");
400
+ pausedField.setAccessible(true);
401
+ if (!pausedField.getBoolean(activityRecord)) {
402
+ Field activityField = activityRecordClass.getDeclaredField("activity");
403
+ activityField.setAccessible(true);
404
+ Activity activity = (Activity) activityField.get(activityRecord);
405
+ setTopActivity(activity);
406
+ return activity;
407
+ }
408
+ }
409
+ } catch (ClassNotFoundException e) {
410
+ e.printStackTrace();
411
+ } catch (IllegalAccessException e) {
412
+ e.printStackTrace();
413
+ } catch (InvocationTargetException e) {
414
+ e.printStackTrace();
415
+ } catch (NoSuchMethodException e) {
416
+ e.printStackTrace();
417
+ } catch (NoSuchFieldException e) {
418
+ e.printStackTrace();
419
+ }
420
+ return null;
421
+ }
422
+ }
423
+
424
+ ///////////////////////////////////////////////////////////////////////////
425
+ // interface
426
+ ///////////////////////////////////////////////////////////////////////////
427
+
428
+ public interface OnAppStatusChangedListener {
429
+
430
+ void onForeground();
431
+
432
+ void onBackground();
433
+ }
434
+
435
+ public static int dp2px(final @NotNull Context context, final Float dpValue) {
436
+ final float scale = context.getResources().getDisplayMetrics().density;
437
+ return (int) (dpValue * scale + 0.5f);
438
+ }
439
+
440
+ public static int sp2px(final @NotNull Context context, final Float sp) {
441
+ final float scale = context.getResources().getDisplayMetrics().scaledDensity;
442
+ return (int) (sp * scale + 0.5f);
443
+ }
444
+
445
+ public static boolean isEmail(String email) {
446
+ if (null == email || "".equals(email)) return false;
447
+ // Pattern p = Pattern.compile("\\w+@(\\w+.)+[a-z]{2,3}"); //简单匹配
448
+ Pattern p = Pattern.compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"); // 复杂匹配
449
+ Matcher m = p.matcher(email);
450
+ return m.matches();
451
+ }
452
+ }
@@ -0,0 +1,5 @@
1
+ #import <BackupCardSdkSpec/BackupCardSdkSpec.h>
2
+
3
+ @interface BackupCardSdk : NSObject <NativeBackupCardSdkSpec>
4
+
5
+ @end
@@ -0,0 +1,21 @@
1
+ #import "BackupCardSdk.h"
2
+
3
+ @implementation BackupCardSdk
4
+ - (NSNumber *)multiply:(double)a b:(double)b {
5
+ NSNumber *result = @(a * b);
6
+
7
+ return result;
8
+ }
9
+
10
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
11
+ (const facebook::react::ObjCTurboModule::InitParams &)params
12
+ {
13
+ return std::make_shared<facebook::react::NativeBackupCardSdkSpecJSI>(params);
14
+ }
15
+
16
+ + (NSString *)moduleName
17
+ {
18
+ return @"BackupCardSdk";
19
+ }
20
+
21
+ @end
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('BackupCardSdk');
5
+ //# sourceMappingURL=NativeBackupCardSdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeBackupCardSdk.ts"],"mappings":";;AAAA,SACEA,mBAAmB,QAGd,cAAc;AAgCrB,eAAeA,mBAAmB,CAACC,YAAY,CAAO,eAAe,CAAC","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import BackupCardSdk from "./NativeBackupCardSdk.js";
4
+ export default BackupCardSdk;
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["BackupCardSdk"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,aAAa,MAAM,0BAAuB;AAGjD,eAAeA,aAAa","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,26 @@
1
+ import { type TurboModule, type CodegenTypes } from 'react-native';
2
+ export type CardInfo = {
3
+ serialNumber: string | null;
4
+ pinRetryCount: number | null;
5
+ isNewCard: boolean | null;
6
+ };
7
+ export interface Spec extends TurboModule {
8
+ getCardInfo(): Promise<CardInfo | null>;
9
+ resetCard(): Promise<Boolean>;
10
+ activateCard(pwd: string): Promise<Boolean>;
11
+ changePin(oldPin: string, newPin: string): Promise<Boolean>;
12
+ checkSlotEmpty(slotId: number, pwd: string): Promise<Boolean>;
13
+ writeSlot(slotIndex: number, data: Array<number>, pwd: string): Promise<Boolean>;
14
+ readSlot(slotIndex: number, pwd: string): Promise<Array<number> | null>;
15
+ readonly onApduLog: CodegenTypes.EventEmitter<{
16
+ message: string;
17
+ isSent: boolean;
18
+ isSuccess: boolean;
19
+ }>;
20
+ readonly onNfcTouch: CodegenTypes.EventEmitter<{
21
+ isBackupCard: boolean;
22
+ }>;
23
+ }
24
+ declare const _default: Spec;
25
+ export default _default;
26
+ //# sourceMappingURL=NativeBackupCardSdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeBackupCardSdk.d.ts","sourceRoot":"","sources":["../../../src/NativeBackupCardSdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACxC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,SAAS,CACP,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EACnB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAExE,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC;QAC5C,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,YAAY,CAAC;QAC7C,YAAY,EAAE,OAAO,CAAC;KACvB,CAAC,CAAC;CACJ;;AAED,wBAAuE"}
@@ -0,0 +1,5 @@
1
+ import BackupCardSdk from './NativeBackupCardSdk';
2
+ import type { CardInfo } from './NativeBackupCardSdk';
3
+ export type { CardInfo };
4
+ export default BackupCardSdk;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EAAE,QAAQ,EAAE,CAAC;AACzB,eAAe,aAAa,CAAC"}
package/package.json ADDED
@@ -0,0 +1,175 @@
1
+ {
2
+ "name": "@digitalshieldfe/react-native-backup-card-sdk",
3
+ "version": "0.1.0",
4
+ "description": "ds react-native backup-card sdk",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-backup-card-sdk-example",
36
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "test": "jest",
41
+ "release": "release-it --only-version",
42
+ "web": "vite",
43
+ "build:web": "vite build"
44
+ },
45
+ "keywords": [
46
+ "react-native",
47
+ "ios",
48
+ "android"
49
+ ],
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://www.ds.com.git"
53
+ },
54
+ "author": "DS <DS@example.com> (https://www.ds.com)",
55
+ "license": "MIT",
56
+ "bugs": {
57
+ "url": "https://www.ds.com/issues"
58
+ },
59
+ "homepage": "https://www.ds.com#readme",
60
+ "publishConfig": {
61
+ "registry": "https://registry.npmjs.org/"
62
+ },
63
+ "devDependencies": {
64
+ "@commitlint/config-conventional": "^20.5.0",
65
+ "@eslint/compat": "^2.0.3",
66
+ "@eslint/eslintrc": "^3.3.5",
67
+ "@eslint/js": "^10.0.1",
68
+ "@jest/globals": "^30.0.0",
69
+ "@react-native/babel-preset": "0.85.1",
70
+ "@react-native/eslint-config": "0.85.1",
71
+ "@react-native/jest-preset": "0.85.1",
72
+ "@release-it/conventional-changelog": "^10.0.6",
73
+ "@types/react": "^19.2.0",
74
+ "commitlint": "^20.5.0",
75
+ "del-cli": "^7.0.0",
76
+ "eslint": "^9.39.4",
77
+ "eslint-config-prettier": "^10.1.8",
78
+ "eslint-plugin-ft-flow": "^3.0.11",
79
+ "eslint-plugin-prettier": "^5.5.5",
80
+ "jest": "^30.3.0",
81
+ "lefthook": "^2.1.4",
82
+ "prettier": "^3.8.1",
83
+ "react": "19.2.3",
84
+ "react-native": "0.85.1",
85
+ "react-native-builder-bob": "^0.41.0",
86
+ "react-native-web": "~0.21.1",
87
+ "release-it": "^19.2.4",
88
+ "turbo": "^2.8.21",
89
+ "typescript": "^6.0.2"
90
+ },
91
+ "peerDependencies": {
92
+ "react": "*",
93
+ "react-native": "*"
94
+ },
95
+ "workspaces": [
96
+ "example"
97
+ ],
98
+ "packageManager": "yarn@4.11.0",
99
+ "react-native-builder-bob": {
100
+ "source": "src",
101
+ "output": "lib",
102
+ "targets": [
103
+ [
104
+ "module",
105
+ {
106
+ "esm": true
107
+ }
108
+ ],
109
+ [
110
+ "typescript",
111
+ {
112
+ "project": "tsconfig.build.json"
113
+ }
114
+ ]
115
+ ]
116
+ },
117
+ "codegenConfig": {
118
+ "name": "BackupCardSdkSpec",
119
+ "type": "modules",
120
+ "jsSrcsDir": "src",
121
+ "android": {
122
+ "javaPackageName": "com.ziancube.reactnativebackupcardsdk"
123
+ }
124
+ },
125
+ "prettier": {
126
+ "quoteProps": "consistent",
127
+ "singleQuote": true,
128
+ "tabWidth": 2,
129
+ "trailingComma": "es5",
130
+ "useTabs": false
131
+ },
132
+ "jest": {
133
+ "preset": "@react-native/jest-preset",
134
+ "modulePathIgnorePatterns": [
135
+ "<rootDir>/example/node_modules",
136
+ "<rootDir>/lib/"
137
+ ]
138
+ },
139
+ "commitlint": {
140
+ "extends": [
141
+ "@commitlint/config-conventional"
142
+ ]
143
+ },
144
+ "release-it": {
145
+ "git": {
146
+ "commitMessage": "chore: release ${version}",
147
+ "tagName": "v${version}"
148
+ },
149
+ "npm": {
150
+ "publish": true
151
+ },
152
+ "github": {
153
+ "release": true
154
+ },
155
+ "plugins": {
156
+ "@release-it/conventional-changelog": {
157
+ "preset": {
158
+ "name": "angular"
159
+ }
160
+ }
161
+ }
162
+ },
163
+ "create-react-native-library": {
164
+ "type": "turbo-module",
165
+ "languages": "kotlin-objc",
166
+ "tools": [
167
+ "eslint",
168
+ "jest",
169
+ "lefthook",
170
+ "release-it",
171
+ "vite"
172
+ ],
173
+ "version": "0.62.0"
174
+ }
175
+ }
@@ -0,0 +1,37 @@
1
+ import {
2
+ TurboModuleRegistry,
3
+ type TurboModule,
4
+ type CodegenTypes,
5
+ } from 'react-native';
6
+
7
+ export type CardInfo = {
8
+ serialNumber: string | null;
9
+ pinRetryCount: number | null;
10
+ isNewCard: boolean | null;
11
+ };
12
+
13
+ export interface Spec extends TurboModule {
14
+ getCardInfo(): Promise<CardInfo | null>;
15
+ resetCard(): Promise<Boolean>;
16
+ activateCard(pwd: string): Promise<Boolean>;
17
+ changePin(oldPin: string, newPin: string): Promise<Boolean>;
18
+ checkSlotEmpty(slotId: number, pwd: string): Promise<Boolean>;
19
+ writeSlot(
20
+ slotIndex: number,
21
+ data: Array<number>,
22
+ pwd: string
23
+ ): Promise<Boolean>;
24
+ readSlot(slotIndex: number, pwd: string): Promise<Array<number> | null>;
25
+
26
+ readonly onApduLog: CodegenTypes.EventEmitter<{
27
+ message: string;
28
+ isSent: boolean;
29
+ isSuccess: boolean;
30
+ }>;
31
+
32
+ readonly onNfcTouch: CodegenTypes.EventEmitter<{
33
+ isBackupCard: boolean;
34
+ }>;
35
+ }
36
+
37
+ export default TurboModuleRegistry.getEnforcing<Spec>('BackupCardSdk');
package/src/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ import BackupCardSdk from './NativeBackupCardSdk';
2
+ import type { CardInfo } from './NativeBackupCardSdk';
3
+ export type { CardInfo };
4
+ export default BackupCardSdk;