@icedevml/react-native-host-card-emulation 0.6.0 → 0.6.2

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 CHANGED
@@ -117,7 +117,7 @@ Afterwards, follow the subsections below for each platform that you need to supp
117
117
  This module provides a uniform low-level HCE API for both mobile platforms.
118
118
 
119
119
  > [!NOTE]
120
- > Raw native module's API specification is available in [packages/host-card-emulation/js/NativeHCEModule.ts](https://github.com/icedevml/react-native-host-card-emulation/blob/master/packages/host-card-emulation/js/NativeHCEModule.ts).
120
+ > Raw native module's API specification is available in [packages/host-card-emulation/js](https://github.com/icedevml/react-native-host-card-emulation/blob/master/packages/host-card-emulation/js) subdirectory.
121
121
  > Check it out in order to understand what methods you can call against the module and what are the expected parameters/return values.
122
122
 
123
123
  > [!NOTE]
@@ -128,7 +128,7 @@ This module provides a uniform low-level HCE API for both mobile platforms.
128
128
  1. Subscribe to the event stream. There is a single global event stream, so you can subscribe early and remain subscribed to events for the entire application's lifetime.
129
129
  ```typescript
130
130
  useEffect(() => {
131
- const subscription = NativeHCEModule?.onEvent(async (event: HCEModuleEvent) => {
131
+ const subscription = NativeHCEModule.onEvent(async (event: HCEModuleEvent) => {
132
132
  switch (event.type) {
133
133
  /* ... implement event handlers here ... */
134
134
  }
@@ -142,28 +142,28 @@ This module provides a uniform low-level HCE API for both mobile platforms.
142
142
  ```
143
143
  2. When user indicates that he/she wants to perform the HCE action, call the following function from the button's onClick routine:
144
144
  ```typescript
145
- await NativeHCEModule?.beginSession();
145
+ await NativeHCEModule.beginSession();
146
146
  ```
147
147
  This will emit `sessionStarted` event right after the function call, on both iOS and Android platforms. After the session is started, you can decide to start HCE emulation right away in the event handler:
148
148
  ```typescript
149
- // inside NativeHCEModule?.onEvent handler's switch
149
+ // inside NativeHCEModule.onEvent handler's switch
150
150
  case 'sessionStarted':
151
- NativeHCEModule?.setSessionAlertMessage('Tap towards the reader'); // only for iOS, no-op in Android
152
- await NativeHCEModule?.startHCE();
151
+ NativeHCEModule.setSessionAlertMessage('Tap towards the reader'); // only for iOS, no-op in Android
152
+ await NativeHCEModule.startHCE();
153
153
  break;
154
154
  ```
155
- Calling `await NativeHCEModule?.startHCE()` causes:
155
+ Calling `await NativeHCEModule.startHCE()` causes:
156
156
  * iOS: Your smartphone will start listening for C-APDUs (Command APDUs originating from reader devices) right after that function is called, which will be additionally indicated by the operating system popping out the NFC scanning user interface prompt.
157
157
  * Android: HCE commands will be forwarded to your application from that point on. No specific user interface is displayed (you have to implement it on your own, within your app).
158
158
  3. Optionally, you can handle `readerDetected` and `readerDeselected` events to enhance user's experience.
159
159
  ```typescript
160
- // inside NativeHCEModule?.onEvent handler's switch
160
+ // inside NativeHCEModule.onEvent handler's switch
161
161
  case 'readerDetected':
162
- NativeHCEModule?.setSessionAlertMessage('Reader detected'); // only for iOS, no-op in Android
162
+ NativeHCEModule.setSessionAlertMessage('Reader detected'); // only for iOS, no-op in Android
163
163
  break;
164
164
 
165
165
  case 'readerDeselected':
166
- NativeHCEModule?.setSessionAlertMessage('Lost reader'); // only for iOS, no-op in Android
166
+ NativeHCEModule.setSessionAlertMessage('Lost reader'); // only for iOS, no-op in Android
167
167
  break;
168
168
  ```
169
169
  For those events, trigger mechanisms are platform dependent:
@@ -171,16 +171,16 @@ This module provides a uniform low-level HCE API for both mobile platforms.
171
171
  * Android: The `readerDetected` event will be emitted as soon as the first matching SELECT AID command is observed. The `readerDeselected` event will be emitted if the reader is physically disconnected or a non-matching AID is selected by the reader.
172
172
  4. Receive incoming C-APDU and respond to it:
173
173
  ```typescript
174
- // inside NativeHCEModule?.onEvent handler's switch
174
+ // inside NativeHCEModule.onEvent handler's switch
175
175
  case 'received':
176
- NativeHCEModule?.setSessionAlertMessage('Keep holding the tag'); // only for iOS, no-op on Android
176
+ NativeHCEModule.setSessionAlertMessage('Keep holding the tag'); // only for iOS, no-op on Android
177
177
 
178
178
  // decode incoming C-APDU to bytes
179
179
  const capdu = Buffer.from(event.arg!, 'hex');
180
180
  console.log('Received C-APDU, capdu.toString('hex'));
181
181
 
182
182
  // for the demo purposes, we always want to respond with [0x0A] + status code 0x9000 (success)
183
- await NativeHCEModule?.respondAPDU(null, Buffer.from([0x0A, 0x90, 0x00], "hex"));
183
+ await NativeHCEModule.respondAPDU(null, Buffer.from([0x0A, 0x90, 0x00]).toString("hex"));
184
184
  break;
185
185
  ```
186
186
  You don't have to respond to the APDU right away from within the event handler, but please remember that the reader might time out if you will be lingering with the response for too long.
@@ -189,16 +189,18 @@ This module provides a uniform low-level HCE API for both mobile platforms.
189
189
 
190
190
  If you need to utilize `NFCPresentmentIntentAssertion` for enhanced user experience, call:
191
191
  ```typescript
192
- NativeHCEModule?.acquireExclusiveNFC();
192
+ await NativeHCEModule.acquireExclusiveNFC();
193
193
  ```
194
194
  This function will acquire an exclusive NFC access for 15 seconds. On system services or other applications will be able to interfere with NFC during that period. For example, the NFC background tag reading will be disabled so it would not generate any distracting notifications.
195
195
 
196
+ While both session and exclusive NFC access are active, you will receive `readerDetected` events even when the HCE emulation is not started at the time (without any explicit UI being displayed by the system). You can then make a call to `startHCE` to initiate the interaction automatically, without needing the user to click anything beforehand.
197
+
196
198
  This function will throw an exception if:
197
199
  * the presentment intent assertion was already acquired and is still active;
198
200
  * you are in the cooldown period where you are not allowed to acquire the presentment intent assertion (cooldown is 15 seconds after the previous assertion had expired);
199
201
  * the feature is not supported or the device is not eligible for whatever reason;
200
202
 
201
- Call `NativeHCEModule?.isExclusiveNFC()` to check if exclusive NFC access is still active.
203
+ Call `NativeHCEModule.isExclusiveNFC()` to check if exclusive NFC access is still active.
202
204
 
203
205
  ### Android: Handle HCE calls when the app is not running
204
206
 
@@ -216,7 +218,7 @@ for instance - your app may emulate an NDEF tag even when it's not launched on t
216
218
  }
217
219
  });
218
220
  ```
219
- 2. Register onBackgroundEvent listener in your `runBackground()` function and call to `await NativeHCEModule?.initBackgroundHCE()` at the very end, after the event listener is fully set up.
221
+ 2. Register onBackgroundEvent listener in your `runBackground()` function and call to `await NativeHCEModule.initBackgroundHCE()` at the very end, after the event listener is fully set up.
220
222
  ```typescript
221
223
  import { Buffer } from 'buffer/';
222
224
  import {
@@ -233,6 +235,7 @@ for instance - your app may emulate an NDEF tag even when it's not launched on t
233
235
  ```
234
236
  3. Cleanup your event listener in `readerDeselected` -- this is obligatory because lack of proper cleanup will result in bugs and unexpected behavior.
235
237
  ```typescript
238
+ // inside processBackgroundHCE handler's switch
236
239
  case 'readerDeselected':
237
240
  // our application was deselected or the reader field was lost
238
241
  // perform a cleanup of any resources here
@@ -240,6 +243,7 @@ for instance - your app may emulate an NDEF tag even when it's not launched on t
240
243
  ```
241
244
  4. Implement the actual APDU handler that will process the C-APDU and generate a response.
242
245
  ```typescript
246
+ // inside processBackgroundHCE handler's switch
243
247
  case 'received':
244
248
  // decode incoming C-APDU to bytes
245
249
  const capdu = Buffer.from(event.arg!, 'hex');
@@ -252,5 +256,5 @@ for instance - your app may emulate an NDEF tag even when it's not launched on t
252
256
 
253
257
  ### More resources
254
258
 
255
- * [Module's API specification](https://github.com/icedevml/react-native-host-card-emulation/blob/master/packages/host-card-emulation/js/NativeHCEModule.ts)
259
+ * [Module's API specification](https://github.com/icedevml/react-native-host-card-emulation/blob/master/packages/host-card-emulation/js)
256
260
  * [Demo App's Code (Example Library Usage)](https://github.com/icedevml/react-native-host-card-emulation/blob/master/packages/demo-ndef-app/App.tsx)
@@ -20,7 +20,6 @@ import androidx.annotation.RequiresApi;
20
20
 
21
21
  import com.facebook.react.ReactApplication;
22
22
  import com.facebook.react.ReactHost;
23
- import com.facebook.react.ReactInstanceEventListener;
24
23
  import com.facebook.react.bridge.Arguments;
25
24
  import com.facebook.react.bridge.ReactContext;
26
25
  import com.facebook.react.bridge.UiThreadUtil;
@@ -28,12 +27,13 @@ import com.facebook.react.jstasks.HeadlessJsTaskConfig;
28
27
  import com.facebook.react.jstasks.HeadlessJsTaskContext;
29
28
  import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
30
29
 
30
+ import java.util.HashMap;
31
31
  import java.util.List;
32
32
  import java.util.Locale;
33
33
  import java.util.UUID;
34
34
 
35
35
  @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
36
- public class HCEService extends HostApduService implements ReactInstanceEventListener, HCEServiceCallback {
36
+ public class HCEService extends HostApduService implements HCEServiceCallback {
37
37
  private static final String TAG = "HCEService";
38
38
 
39
39
  private boolean isForeground;
@@ -41,6 +41,13 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
41
41
  private RTNHCEAndroidModule hceModule;
42
42
  private byte[] pendingCAPDU;
43
43
  private volatile boolean needsResponse;
44
+ private final HashMap<String, Integer> taskSessionIdMap;
45
+
46
+ public HCEService() {
47
+ super();
48
+
49
+ taskSessionIdMap = new HashMap<>();
50
+ }
44
51
 
45
52
  private boolean isAppOnForeground(Context context) {
46
53
  /*
@@ -67,6 +74,7 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
67
74
  @Override
68
75
  public void onBackgroundHCEInit(String handle) {
69
76
  Log.d(TAG, "HCEService:onBackgroundHCEInit");
77
+ Log.d(TAG, "HCEService:onBackgroundHCEInit:" + handle + ":" + backgroundSessionUUID);
70
78
 
71
79
  if (handle == null && backgroundSessionUUID != null) {
72
80
  Log.d(TAG, "HCEService:onBackgroundHCEInit foreground call to background session");
@@ -83,6 +91,35 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
83
91
  }
84
92
  }
85
93
 
94
+ @Override
95
+ public void onBackgroundHCEFinish(String handle) {
96
+ Log.d(TAG, "HCEService:onBackgroundHCEFinish");
97
+ Integer taskId = taskSessionIdMap.get(handle);
98
+
99
+ if (taskId == null) {
100
+ Log.d(TAG, "HCEService:onBackgroundHCEFinish unable to resolve taskId");
101
+ throw new IllegalArgumentException();
102
+ }
103
+
104
+ ReactHost reactHost = ((ReactApplication) getApplication()).getReactHost();
105
+
106
+ if (reactHost == null) {
107
+ Log.d(TAG, "HCEService:onBackgroundHCEFinish getReactHost() returned null");
108
+ throw new IllegalArgumentException();
109
+ }
110
+
111
+ ReactContext reactContext = reactHost.getCurrentReactContext();
112
+
113
+ if (reactContext == null) {
114
+ Log.d(TAG, "HCEService:onBackgroundHCEFinish getCurrentReactContext() returned null");
115
+ throw new IllegalArgumentException();
116
+ }
117
+
118
+ HeadlessJsTaskContext headlessJsTaskContext = Companion.getInstance(reactContext);
119
+ headlessJsTaskContext.finishTask(taskId);
120
+ taskSessionIdMap.remove(handle);
121
+ }
122
+
86
123
  @Override
87
124
  public void onRespondAPDU(String handle, String rapdu) {
88
125
  Log.d(TAG, "HCEService:onRespondAPDU");
@@ -110,13 +147,13 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
110
147
  String capdu = BinaryUtils.ByteArrayToHexString(command).toUpperCase(Locale.ROOT);
111
148
 
112
149
  if (isForeground) {
113
- if (hceModule._isHCERunning() && !hceModule.isHCEBrokenConnection()) {
150
+ if (hceModule._isHCERunning() && hceModule.isHCEActiveConnection()) {
114
151
  Log.d(TAG, "HCEService:processCommandApdu foreground sendEvent received");
115
152
  needsResponse = true;
116
153
  hceModule.sendEvent("received", capdu);
117
154
  } else {
118
155
  Log.d(TAG, "HCEService:processCommandApdu foreground respond 6999");
119
- return new byte[] { (byte) 0x69, (byte) 0x99 };
156
+ return new byte[]{(byte) 0x69, (byte) 0x99};
120
157
  }
121
158
  } else {
122
159
  if (hceModule != null && hceModule.isHCEBackgroundHandlerReady()) {
@@ -174,13 +211,29 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
174
211
  } else {
175
212
  ReactContext reactContext = reactHost.getCurrentReactContext();
176
213
  Log.d(TAG, "HCEService:onCreate background");
177
- this.backgroundSessionUUID = UUID.randomUUID().toString();
214
+ final String useSessID = UUID.randomUUID().toString();
215
+ backgroundSessionUUID = useSessID;
178
216
 
179
217
  if (reactContext == null) {
180
- reactHost.addReactInstanceEventListener(this);
218
+ reactHost.addReactInstanceEventListener(evReactContext -> {
219
+ HeadlessJsTaskContext headlessJsTaskContext = Companion.getInstance(evReactContext);
220
+ headlessJsTaskContext.addTaskEventListener(new HeadlessJsTaskEventListener() {
221
+ @Override
222
+ public void onHeadlessJsTaskStart(int i) {
223
+ Log.d(TAG, "HCEService:HeadlessJsTaskEventListener:onHeadlessJsTaskStart: " + i);
224
+ }
225
+
226
+ @Override
227
+ public void onHeadlessJsTaskFinish(int i) {
228
+ Log.d(TAG, "HCEService:HeadlessJsTaskEventListener:onHeadlessJsTaskFinish: " + i);
229
+ }
230
+ });
231
+
232
+ setupRunJSTask(evReactContext, useSessID);
233
+ });
181
234
  reactHost.start();
182
235
  } else {
183
- setupRunJSTask(reactContext);
236
+ setupRunJSTask(reactContext, useSessID);
184
237
  }
185
238
  }
186
239
  }
@@ -191,37 +244,20 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
191
244
  needsResponse = false;
192
245
 
193
246
  if (isForeground) {
194
- if (this.hceModule != null && !this.hceModule.isHCEBrokenConnection()) {
247
+ if (this.hceModule != null && this.hceModule.isHCEActiveConnection()) {
195
248
  this.hceModule.sendEvent("readerDeselected", "");
196
249
  }
197
- } else {
198
- this.hceModule.sendBackgroundEvent(backgroundSessionUUID, "readerDeselected", "");
199
- }
250
+ } else if (backgroundSessionUUID != null) {
251
+ String prevBackgroundSessionUUID = backgroundSessionUUID;
252
+ backgroundSessionUUID = null;
200
253
 
201
- if (this.hceModule != null) {
202
- this.hceModule.setHCEService(null);
203
- }
204
- }
205
-
206
- @Override
207
- public void onReactContextInitialized(@NonNull ReactContext reactContext) {
208
- HeadlessJsTaskContext headlessJsTaskContext = Companion.getInstance(reactContext);
209
- headlessJsTaskContext.addTaskEventListener(new HeadlessJsTaskEventListener() {
210
- @Override
211
- public void onHeadlessJsTaskStart(int i) {
212
- Log.d(TAG, "HCEService:HeadlessJsTaskEventListener:onHeadlessJsTaskStart: " + i);
213
- }
214
-
215
- @Override
216
- public void onHeadlessJsTaskFinish(int i) {
217
- Log.d(TAG, "HCEService:HeadlessJsTaskEventListener:onHeadlessJsTaskFinish: " + i);
254
+ if (this.hceModule != null) {
255
+ this.hceModule.sendBackgroundEvent(prevBackgroundSessionUUID, "readerDeselected", "");
218
256
  }
219
- });
220
-
221
- setupRunJSTask(reactContext);
257
+ }
222
258
  }
223
259
 
224
- public void setupRunJSTask(@NonNull ReactContext reactContext) {
260
+ public void setupRunJSTask(final @NonNull ReactContext reactContext, final String useSessUUID) {
225
261
  hceModule = (RTNHCEAndroidModule) reactContext.getNativeModule("NativeHCEModule");
226
262
 
227
263
  if (hceModule == null) {
@@ -230,21 +266,19 @@ public class HCEService extends HostApduService implements ReactInstanceEventLis
230
266
 
231
267
  hceModule.setHCEService(this);
232
268
 
233
- UiThreadUtil.runOnUiThread(new Runnable() {
234
- @Override
235
- public void run() {
236
- Log.d(TAG, "HCEService:setupRunJSTask:runOnUiThread startTask");
237
- Bundle argBundle = new Bundle();
238
- argBundle.putString("handle", backgroundSessionUUID);
239
- HeadlessJsTaskContext headlessJsTaskContext = Companion.getInstance(reactContext);
240
- headlessJsTaskContext.startTask(
241
- new HeadlessJsTaskConfig(
242
- "handleBackgroundHCECall",
243
- Arguments.fromBundle(argBundle),
244
- 15000,
245
- false // not allowed in foreground
246
- ));
247
- }
269
+ UiThreadUtil.runOnUiThread(() -> {
270
+ Log.d(TAG, "HCEService:setupRunJSTask:runOnUiThread startTask");
271
+ Bundle argBundle = new Bundle();
272
+ argBundle.putString("handle", useSessUUID);
273
+ HeadlessJsTaskContext headlessJsTaskContext = Companion.getInstance(reactContext);
274
+ int taskId = headlessJsTaskContext.startTask(
275
+ new HeadlessJsTaskConfig(
276
+ "handleBackgroundHCECall",
277
+ Arguments.fromBundle(argBundle),
278
+ 15000,
279
+ false // not allowed in foreground
280
+ ));
281
+ taskSessionIdMap.put(useSessUUID, taskId);
248
282
  });
249
283
  }
250
284
 
@@ -2,5 +2,6 @@ package com.itsecrnd.rtnhceandroid;
2
2
 
3
3
  public interface HCEServiceCallback {
4
4
  void onBackgroundHCEInit(String handle);
5
+ void onBackgroundHCEFinish(String handle);
5
6
  void onRespondAPDU(String handle, String rapdu);
6
7
  }
@@ -51,19 +51,29 @@ public class RTNHCEAndroidModule extends NativeHCEModuleSpec {
51
51
 
52
52
  void sendEvent(final String type, final String arg) {
53
53
  Log.d(TAG, "RTNHCEAndroidModule:sendEvent");
54
- WritableMap map = Arguments.createMap();
55
- map.putString("type", type);
56
- map.putString("arg", arg);
57
- emitOnEvent(map);
54
+
55
+ if (this.mEventEmitterCallback != null) {
56
+ WritableMap map = Arguments.createMap();
57
+ map.putString("type", type);
58
+ map.putString("arg", arg);
59
+ emitOnEvent(map);
60
+ } else {
61
+ Log.d(TAG, "RTNHCEAndroidModule:sendEvent missing event emitter, ignore");
62
+ }
58
63
  }
59
64
 
60
65
  void sendBackgroundEvent(final String audience, final String type, final String arg) {
61
66
  Log.d(TAG, "RTNHCEAndroidModule:sendBackgroundEvent");
62
- WritableMap map = Arguments.createMap();
63
- map.putString("audience", audience);
64
- map.putString("type", type);
65
- map.putString("arg", arg);
66
- emitOnBackgroundEvent(map);
67
+
68
+ if (this.mEventEmitterCallback != null) {
69
+ WritableMap map = Arguments.createMap();
70
+ map.putString("audience", audience);
71
+ map.putString("type", type);
72
+ map.putString("arg", arg);
73
+ emitOnBackgroundEvent(map);
74
+ } else {
75
+ Log.d(TAG, "RTNHCEAndroidModule:sendBackgroundEvent missing event emitter, ignore");
76
+ }
67
77
  }
68
78
 
69
79
  boolean _isHCERunning() {
@@ -78,8 +88,8 @@ public class RTNHCEAndroidModule extends NativeHCEModuleSpec {
78
88
  return this.hceBackgroundReady;
79
89
  }
80
90
 
81
- boolean isHCEBrokenConnection() {
82
- return this.hceBrokenConnection;
91
+ boolean isHCEActiveConnection() {
92
+ return !this.hceBrokenConnection;
83
93
  }
84
94
 
85
95
  void setHCEService(HCEServiceCallback serviceCallback) {
@@ -171,6 +181,19 @@ public class RTNHCEAndroidModule extends NativeHCEModuleSpec {
171
181
  return true;
172
182
  }
173
183
 
184
+ @Override
185
+ public boolean finishBackgroundHCE(String handle) {
186
+ Log.d(TAG, "RTNHCEAndroidModule:finishBackgroundHCE");
187
+
188
+ try {
189
+ this.serviceCb.onBackgroundHCEFinish(handle);
190
+ } catch (IllegalStateException e) {
191
+ return false;
192
+ }
193
+
194
+ return true;
195
+ }
196
+
174
197
  @Override
175
198
  public void startHCE(Promise promise) {
176
199
  Log.d(TAG, "RTNHCEAndroidModule:startHCE");
@@ -74,8 +74,6 @@ export interface Spec extends TurboModule {
74
74
  */
75
75
  isSessionRunning(): boolean;
76
76
 
77
- beginBackgroundHCE(handle: string): boolean;
78
-
79
77
  /**
80
78
  * iOS: Start the card emulation.
81
79
  * Android: Enable HCE service.
@@ -88,6 +86,7 @@ export interface Spec extends TurboModule {
88
86
  stopHCE(status: HCEModuleStopReason): Promise<void>;
89
87
  /**
90
88
  * Send R-APDU (hex-encoded string) as a response to the last received C-APDU event.
89
+ * Set handle=null when calling this function from foreground app.
91
90
  */
92
91
  respondAPDU(handle: string | null, rapdu: string): Promise<void>;
93
92
  /**
@@ -96,7 +95,30 @@ export interface Spec extends TurboModule {
96
95
  */
97
96
  isHCERunning(): Promise<boolean>;
98
97
 
98
+ /**
99
+ * Android: Report that the JS headless task is ready to handle HCE interaction in the background.
100
+ * NOTE: Don't call this API directly, use wrapper hceBackground.ts:createBackgroundHCE.
101
+ * See demo app or README.md for example wrapper usage.
102
+ */
103
+ beginBackgroundHCE(handle: string): boolean;
104
+
105
+ /**
106
+ * Android: Report the background HCE interaction was finished.
107
+ * NOTE: Don't call this API directly, use wrapper hceBackground.ts:createBackgroundHCE.
108
+ * See demo app or README.md for example wrapper usage.
109
+ */
110
+ finishBackgroundHCE(handle: string): boolean;
111
+
112
+ /**
113
+ * Event handler for foreground HCE interactions.
114
+ */
99
115
  readonly onEvent: CodegenTypes.EventEmitter<HCEModuleEvent>;
116
+
117
+ /**
118
+ * Event handler for background HCE interactions.
119
+ * NOTE: Don't subscribe to that handler directly, use wrapper hceBackground.ts:createBackgroundHCE.
120
+ * See demo app or README.md for example wrapper usage.
121
+ */
100
122
  readonly onBackgroundEvent: CodegenTypes.EventEmitter<HCEModuleBackgroundEvent>;
101
123
  }
102
124
 
@@ -32,12 +32,14 @@ export const createBackgroundHCE = (handle: string) => {
32
32
  } finally {
33
33
  if (event.type === "readerDeselected") {
34
34
  subscription.remove();
35
+ NativeHCEModule.finishBackgroundHCE(handle);
35
36
  }
36
37
  }
37
38
  });
38
39
 
39
40
  if (!NativeHCEModule.beginBackgroundHCE(handle)) {
40
41
  subscription.remove();
42
+ NativeHCEModule.finishBackgroundHCE(handle);
41
43
  }
42
44
  }
43
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icedevml/react-native-host-card-emulation",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Host Card Emulation (HCE) React Native Turbo Module for iOS and Android platforms.",
5
5
  "react-native": "js/index",
6
6
  "source": "js/index",