@icedevml/react-native-host-card-emulation 0.9.1 → 1.0.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.
- package/Package.swift +87 -0
- package/README.md +216 -152
- package/RTNHCEModuleIOS.podspec +1 -1
- package/android/build.gradle +5 -17
- package/android/src/main/java/com/itsecrnd/rtnhceandroid/BinaryUtils.java +11 -5
- package/android/src/main/java/com/itsecrnd/rtnhceandroid/HCEManager.java +558 -0
- package/android/src/main/java/com/itsecrnd/rtnhceandroid/HCEService.java +22 -299
- package/android/src/main/java/com/itsecrnd/rtnhceandroid/RTNHCEAndroidModule.java +232 -166
- package/android/src/main/java/com/itsecrnd/rtnhceandroid/RTNHCEAndroidPackage.java +5 -4
- package/ios/RTNHCEModuleIOS.mm +69 -48
- package/ios/RTNHCEModuleIOSImpl.swift +429 -100
- package/js/NativeHCEModule.ts +48 -130
- package/js/background.ts +168 -0
- package/js/index.ts +49 -0
- package/js/internal.ts +218 -0
- package/js/session.ts +192 -0
- package/js/types.ts +179 -0
- package/package.json +19 -3
- package/react-native-spm-prefix.h +10 -0
- package/android/src/main/AndroidMainfest.xml +0 -3
- package/android/src/main/java/com/itsecrnd/rtnhceandroid/HCEServiceCallback.java +0 -7
- package/js/hceBackground.ts +0 -68
|
@@ -1,274 +1,340 @@
|
|
|
1
1
|
package com.itsecrnd.rtnhceandroid;
|
|
2
2
|
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.ComponentName;
|
|
3
5
|
import android.content.pm.PackageManager;
|
|
4
|
-
import android.
|
|
6
|
+
import android.nfc.NfcAdapter;
|
|
7
|
+
import android.nfc.cardemulation.CardEmulation;
|
|
5
8
|
import android.util.Log;
|
|
6
9
|
|
|
7
10
|
import androidx.annotation.NonNull;
|
|
8
|
-
import androidx.annotation.
|
|
11
|
+
import androidx.annotation.Nullable;
|
|
9
12
|
|
|
10
13
|
import com.facebook.react.bridge.Arguments;
|
|
14
|
+
import com.facebook.react.bridge.LifecycleEventListener;
|
|
11
15
|
import com.facebook.react.bridge.Promise;
|
|
12
16
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
17
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
13
18
|
import com.facebook.react.bridge.WritableMap;
|
|
19
|
+
import com.facebook.react.common.LifecycleState;
|
|
20
|
+
import com.facebook.react.jstasks.HeadlessJsTaskContext;
|
|
21
|
+
import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
|
|
14
22
|
|
|
15
23
|
public class RTNHCEAndroidModule extends NativeHCEModuleSpec {
|
|
16
24
|
public static final String TAG = "RTNHCEAndroidModule";
|
|
17
25
|
public static final String NAME = "NativeHCEModule";
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
private volatile boolean hceBrokenConnection;
|
|
27
|
+
static final class BackgroundHandlerDeclaration {
|
|
28
|
+
final boolean registered;
|
|
29
|
+
final boolean allowWhenLocked;
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
BackgroundHandlerDeclaration(boolean registered, boolean allowWhenLocked) {
|
|
32
|
+
this.registered = registered;
|
|
33
|
+
this.allowWhenLocked = allowWhenLocked;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
// null until the JS side declares whether it has a background handler
|
|
38
|
+
private volatile BackgroundHandlerDeclaration backgroundHandlerDeclaration;
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
// the activity for which HCEService is the preferred service, main thread only
|
|
41
|
+
@Nullable
|
|
42
|
+
private Activity preferredServiceActivity;
|
|
43
|
+
|
|
44
|
+
private final HeadlessJsTaskEventListener headlessJsTaskEventListener = new HeadlessJsTaskEventListener() {
|
|
45
|
+
@Override
|
|
46
|
+
public void onHeadlessJsTaskStart(int taskId) {
|
|
47
|
+
// nothing to do
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
public void onHeadlessJsTaskFinish(int taskId) {
|
|
52
|
+
HCEManager.getInstance().onHeadlessTaskFinished(RTNHCEAndroidModule.this, taskId);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
Android only allows setting the preferred service for a resumed activity
|
|
58
|
+
and the preference has to be removed once the activity is paused.
|
|
59
|
+
*/
|
|
60
|
+
private final LifecycleEventListener lifecycleEventListener = new LifecycleEventListener() {
|
|
61
|
+
@Override
|
|
62
|
+
public void onHostResume() {
|
|
63
|
+
updatePreferredService(true);
|
|
64
|
+
}
|
|
30
65
|
|
|
31
|
-
|
|
66
|
+
@Override
|
|
67
|
+
public void onHostPause() {
|
|
68
|
+
updatePreferredService(false);
|
|
69
|
+
}
|
|
32
70
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
71
|
+
@Override
|
|
72
|
+
public void onHostDestroy() {
|
|
73
|
+
updatePreferredService(false);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
RTNHCEAndroidModule(ReactApplicationContext context) {
|
|
78
|
+
super(context);
|
|
79
|
+
HeadlessJsTaskContext.getInstance(context).addTaskEventListener(headlessJsTaskEventListener);
|
|
80
|
+
context.addLifecycleEventListener(lifecycleEventListener);
|
|
38
81
|
}
|
|
39
82
|
|
|
40
83
|
@Override
|
|
41
84
|
public void invalidate() {
|
|
42
|
-
Log.d(TAG, "
|
|
85
|
+
Log.d(TAG, "invalidate");
|
|
86
|
+
ReactApplicationContext context = getReactApplicationContext();
|
|
87
|
+
HeadlessJsTaskContext.getInstance(context).removeTaskEventListener(headlessJsTaskEventListener);
|
|
88
|
+
context.removeLifecycleEventListener(lifecycleEventListener);
|
|
89
|
+
HCEManager.getInstance().onModuleInvalidated(this);
|
|
90
|
+
UiThreadUtil.runOnUiThread(() -> updatePreferredService(false));
|
|
91
|
+
super.invalidate();
|
|
43
92
|
}
|
|
44
93
|
|
|
45
94
|
@Override
|
|
46
95
|
@NonNull
|
|
47
96
|
public String getName() {
|
|
48
|
-
|
|
49
|
-
return RTNHCEAndroidModule.NAME;
|
|
97
|
+
return NAME;
|
|
50
98
|
}
|
|
51
99
|
|
|
52
|
-
|
|
53
|
-
|
|
100
|
+
ReactApplicationContext getContext() {
|
|
101
|
+
return getReactApplicationContext();
|
|
102
|
+
}
|
|
54
103
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
map.putString("arg", arg);
|
|
59
|
-
emitOnEvent(map);
|
|
60
|
-
} else {
|
|
61
|
-
Log.d(TAG, "RTNHCEAndroidModule:sendEvent missing event emitter, ignore");
|
|
62
|
-
}
|
|
104
|
+
@Nullable
|
|
105
|
+
BackgroundHandlerDeclaration getBackgroundHandlerDeclaration() {
|
|
106
|
+
return backgroundHandlerDeclaration;
|
|
63
107
|
}
|
|
64
108
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} else {
|
|
75
|
-
Log.d(TAG, "RTNHCEAndroidModule:sendBackgroundEvent missing event emitter, ignore");
|
|
109
|
+
@Nullable
|
|
110
|
+
private CardEmulation getCardEmulation() {
|
|
111
|
+
try {
|
|
112
|
+
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(getReactApplicationContext());
|
|
113
|
+
return adapter != null ? CardEmulation.getInstance(adapter) : null;
|
|
114
|
+
} catch (RuntimeException e) {
|
|
115
|
+
// UnsupportedOperationException if the device doesn't support HCE
|
|
116
|
+
Log.w(TAG, "getCardEmulation: CardEmulation is not available", e);
|
|
117
|
+
return null;
|
|
76
118
|
}
|
|
77
119
|
}
|
|
78
120
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Make HCEService the preferred service while the app's activity is resumed and the foreground session
|
|
123
|
+
* has running emulation, so that it wins the routing of AIDs that other apps also have registered.
|
|
124
|
+
*/
|
|
125
|
+
// main thread
|
|
126
|
+
private void updatePreferredService(boolean hostResumed) {
|
|
127
|
+
Activity activity = hostResumed ? getReactApplicationContext().getCurrentActivity() : null;
|
|
128
|
+
boolean wanted = activity != null && HCEManager.getInstance().hasRunningEmulation(this);
|
|
82
129
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
130
|
+
if (wanted && activity == preferredServiceActivity) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
86
133
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
134
|
+
CardEmulation cardEmulation = null;
|
|
135
|
+
|
|
136
|
+
if (preferredServiceActivity != null) {
|
|
137
|
+
cardEmulation = getCardEmulation();
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
if (cardEmulation != null) {
|
|
141
|
+
cardEmulation.unsetPreferredService(preferredServiceActivity);
|
|
142
|
+
}
|
|
143
|
+
} catch (RuntimeException e) {
|
|
144
|
+
// IllegalArgumentException if the activity isn't resumed anymore, the preference is gone anyway
|
|
145
|
+
Log.w(TAG, "updatePreferredService: unsetPreferredService() failed", e);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
preferredServiceActivity = null;
|
|
149
|
+
}
|
|
90
150
|
|
|
91
|
-
|
|
92
|
-
|
|
151
|
+
if (!wanted) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (cardEmulation == null) {
|
|
156
|
+
cardEmulation = getCardEmulation();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (cardEmulation == null) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
ComponentName service = new ComponentName(getReactApplicationContext(), HCEService.class);
|
|
165
|
+
|
|
166
|
+
if (cardEmulation.setPreferredService(activity, service)) {
|
|
167
|
+
preferredServiceActivity = activity;
|
|
168
|
+
} else {
|
|
169
|
+
Log.w(TAG, "updatePreferredService: setPreferredService() refused, is HCEService declared in the manifest?");
|
|
170
|
+
}
|
|
171
|
+
} catch (RuntimeException e) {
|
|
172
|
+
Log.w(TAG, "updatePreferredService: setPreferredService() failed", e);
|
|
173
|
+
}
|
|
93
174
|
}
|
|
94
175
|
|
|
95
|
-
void
|
|
96
|
-
|
|
176
|
+
private void refreshPreferredService() {
|
|
177
|
+
UiThreadUtil.runOnUiThread(() -> updatePreferredService(
|
|
178
|
+
getReactApplicationContext().getLifecycleState() == LifecycleState.RESUMED));
|
|
179
|
+
}
|
|
97
180
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this.hceBackgroundReady = false;
|
|
101
|
-
this.hceBrokenConnection = false;
|
|
181
|
+
void emitEvent(String sessionId, String type) {
|
|
182
|
+
emitEvent(sessionId, type, 0, "", false, "");
|
|
102
183
|
}
|
|
103
184
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return false;
|
|
185
|
+
void emitEvent(String sessionId, String type, long apduId, String command, boolean deviceLocked, String reason) {
|
|
186
|
+
if (this.mEventEmitterCallback == null) {
|
|
187
|
+
Log.w(TAG, "emitEvent: missing event emitter, dropping " + type);
|
|
188
|
+
return;
|
|
109
189
|
}
|
|
110
190
|
|
|
191
|
+
WritableMap map = Arguments.createMap();
|
|
192
|
+
map.putString("sessionId", sessionId);
|
|
193
|
+
map.putString("type", type);
|
|
194
|
+
map.putDouble("apduId", (double) apduId);
|
|
195
|
+
map.putString("command", command);
|
|
196
|
+
map.putBoolean("deviceLocked", deviceLocked);
|
|
197
|
+
map.putString("reason", reason);
|
|
198
|
+
emitOnEvent(map);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private boolean isHCESupported() {
|
|
111
202
|
PackageManager pm = getReactApplicationContext().getPackageManager();
|
|
112
203
|
return pm.hasSystemFeature(PackageManager.FEATURE_NFC)
|
|
113
204
|
&& pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION);
|
|
114
205
|
}
|
|
115
206
|
|
|
116
207
|
@Override
|
|
117
|
-
public void
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
208
|
+
public void getCapabilities(Promise promise) {
|
|
209
|
+
boolean hceSupported = isHCESupported();
|
|
210
|
+
boolean nfcEnabled = false;
|
|
121
211
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
212
|
+
try {
|
|
213
|
+
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(getReactApplicationContext());
|
|
214
|
+
nfcEnabled = adapter != null && adapter.isEnabled();
|
|
215
|
+
} catch (RuntimeException e) {
|
|
216
|
+
Log.w(TAG, "getCapabilities: unable to check NFC adapter state", e);
|
|
217
|
+
}
|
|
127
218
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
219
|
+
WritableMap map = Arguments.createMap();
|
|
220
|
+
map.putString("platform", "android");
|
|
221
|
+
map.putBoolean("hceSupported", hceSupported);
|
|
222
|
+
map.putBoolean("nfcEnabled", nfcEnabled);
|
|
223
|
+
map.putBoolean("eligible", hceSupported);
|
|
224
|
+
map.putBoolean("backgroundSupported", hceSupported);
|
|
225
|
+
map.putBoolean("presentmentSupported", false);
|
|
226
|
+
promise.resolve(map);
|
|
133
227
|
}
|
|
134
228
|
|
|
135
229
|
@Override
|
|
136
|
-
public void beginSession(Promise promise) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!isPlatformSupported()) {
|
|
140
|
-
promise.reject("err_platform_unsupported", "Unsupported Android version or missing NFC/HCE feature.");
|
|
230
|
+
public void beginSession(String sessionId, boolean allowWhenLocked, Promise promise) {
|
|
231
|
+
if (!isHCESupported()) {
|
|
232
|
+
promise.reject("err_platform_unsupported", "The device doesn't support NFC host card emulation.");
|
|
141
233
|
return;
|
|
142
234
|
}
|
|
143
235
|
|
|
144
|
-
|
|
145
|
-
this
|
|
146
|
-
|
|
147
|
-
promise.
|
|
148
|
-
|
|
149
|
-
promise.reject("err_card_session_exists", "Session already exists.");
|
|
236
|
+
try {
|
|
237
|
+
HCEManager.getInstance().beginSession(this, sessionId, allowWhenLocked);
|
|
238
|
+
} catch (IllegalStateException e) {
|
|
239
|
+
promise.reject("err_session_exists", "A session already exists.");
|
|
240
|
+
return;
|
|
150
241
|
}
|
|
151
|
-
}
|
|
152
242
|
|
|
153
|
-
|
|
154
|
-
public void setSessionAlertMessage(String message) {
|
|
155
|
-
Log.d(TAG, "RTNHCEAndroidModule:setSessionAlertMessage");
|
|
156
|
-
// unsupported on Android, no-op
|
|
243
|
+
promise.resolve(null);
|
|
157
244
|
}
|
|
158
245
|
|
|
159
246
|
@Override
|
|
160
|
-
public void invalidateSession() {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.hceRunning = false;
|
|
164
|
-
this.sessionRunning = false;
|
|
165
|
-
this.hceBrokenConnection = true;
|
|
166
|
-
sendEvent("sessionInvalidated", "userInvalidated");
|
|
167
|
-
}
|
|
247
|
+
public void invalidateSession(String sessionId) {
|
|
248
|
+
HCEManager.getInstance().invalidateSession(this, sessionId);
|
|
249
|
+
refreshPreferredService();
|
|
168
250
|
}
|
|
169
251
|
|
|
170
252
|
@Override
|
|
171
|
-
public
|
|
172
|
-
|
|
173
|
-
return this.sessionRunning;
|
|
253
|
+
public void setSessionAlertMessage(String sessionId, String message) {
|
|
254
|
+
// unsupported on Android, no-op
|
|
174
255
|
}
|
|
175
256
|
|
|
176
257
|
@Override
|
|
177
|
-
public
|
|
178
|
-
Log.d(TAG, "RTNHCEAndroidModule:beginBackgroundHCE");
|
|
179
|
-
|
|
258
|
+
public void startEmulation(String sessionId, Promise promise) {
|
|
180
259
|
try {
|
|
181
|
-
|
|
260
|
+
HCEManager.getInstance().startEmulation(this, sessionId);
|
|
182
261
|
} catch (IllegalStateException e) {
|
|
183
|
-
|
|
262
|
+
promise.reject("err_no_session", "The session is not active.");
|
|
263
|
+
return;
|
|
184
264
|
}
|
|
185
265
|
|
|
186
|
-
|
|
187
|
-
|
|
266
|
+
refreshPreferredService();
|
|
267
|
+
promise.resolve(null);
|
|
188
268
|
}
|
|
189
269
|
|
|
190
270
|
@Override
|
|
191
|
-
public
|
|
192
|
-
Log.d(TAG, "RTNHCEAndroidModule:finishBackgroundHCE");
|
|
193
|
-
|
|
271
|
+
public void stopEmulation(String sessionId, String status, Promise promise) {
|
|
194
272
|
try {
|
|
195
|
-
|
|
273
|
+
HCEManager.getInstance().stopEmulation(this, sessionId);
|
|
196
274
|
} catch (IllegalStateException e) {
|
|
197
|
-
|
|
275
|
+
promise.reject("err_no_session", "The session is not active.");
|
|
276
|
+
return;
|
|
198
277
|
}
|
|
199
278
|
|
|
200
|
-
|
|
279
|
+
refreshPreferredService();
|
|
280
|
+
promise.resolve(null);
|
|
201
281
|
}
|
|
202
282
|
|
|
203
283
|
@Override
|
|
204
|
-
public void
|
|
205
|
-
|
|
206
|
-
if (!this.sessionRunning) {
|
|
207
|
-
promise.reject("err_no_session", "No session is active.");
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (this.hceRunning) {
|
|
212
|
-
promise.reject("err_start_emulation", "Error trying to start emulation. Emulation already started.");
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
this.hceRunning = true;
|
|
217
|
-
this.hceBrokenConnection = false;
|
|
218
|
-
promise.resolve(null);
|
|
284
|
+
public void isEmulationRunning(String sessionId, Promise promise) {
|
|
285
|
+
promise.resolve(HCEManager.getInstance().isEmulationRunning(this, sessionId));
|
|
219
286
|
}
|
|
220
287
|
|
|
221
288
|
@Override
|
|
222
|
-
public void
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
289
|
+
public void respondApdu(String sessionId, double apduId, String rapdu, Promise promise) {
|
|
290
|
+
byte[] response;
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
response = BinaryUtils.HexStringToByteArray(rapdu);
|
|
294
|
+
} catch (IllegalArgumentException e) {
|
|
295
|
+
promise.reject("err_invalid_rapdu", "The R-APDU is not a valid hex-encoded string.");
|
|
226
296
|
return;
|
|
227
297
|
}
|
|
228
298
|
|
|
229
|
-
if (
|
|
230
|
-
|
|
299
|
+
if (response.length < 2) {
|
|
300
|
+
promise.reject("err_invalid_rapdu", "The R-APDU must contain at least the status word.");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
231
303
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
304
|
+
try {
|
|
305
|
+
HCEManager.getInstance().respondApdu(this, sessionId, (long) apduId, response);
|
|
306
|
+
} catch (IllegalStateException e) {
|
|
307
|
+
promise.reject("err_stale_apdu", "The C-APDU is not awaiting a response (reader lost or session closed).");
|
|
308
|
+
return;
|
|
237
309
|
}
|
|
238
310
|
|
|
239
311
|
promise.resolve(null);
|
|
240
312
|
}
|
|
241
313
|
|
|
242
|
-
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
243
314
|
@Override
|
|
244
|
-
public void
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (!this.hceBackgroundReady) {
|
|
248
|
-
if (!this.sessionRunning) {
|
|
249
|
-
promise.reject("err_no_session", "No session is active.");
|
|
250
|
-
return;
|
|
251
|
-
} else if (!this.hceRunning) {
|
|
252
|
-
promise.reject("err_no_hce", "HCE is not running.");
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
315
|
+
public void acquireExclusiveNFC(Promise promise) {
|
|
316
|
+
promise.reject("err_platform_unsupported", "acquireExclusiveNFC() is not supported on Android.");
|
|
317
|
+
}
|
|
256
318
|
|
|
257
|
-
|
|
319
|
+
@Override
|
|
320
|
+
public void releaseExclusiveNFC() {
|
|
321
|
+
// unsupported on Android, no-op
|
|
322
|
+
}
|
|
258
323
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
324
|
+
@Override
|
|
325
|
+
public boolean isExclusiveNFC() {
|
|
326
|
+
// unsupported on Android, always false
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
265
329
|
|
|
266
|
-
|
|
330
|
+
@Override
|
|
331
|
+
public void setBackgroundHandlerRegistered(boolean registered, boolean allowWhenLocked) {
|
|
332
|
+
this.backgroundHandlerDeclaration = new BackgroundHandlerDeclaration(registered, allowWhenLocked);
|
|
333
|
+
UiThreadUtil.runOnUiThread(() -> HCEManager.getInstance().onBackgroundHandlerDeclared(this));
|
|
267
334
|
}
|
|
268
335
|
|
|
269
336
|
@Override
|
|
270
|
-
public
|
|
271
|
-
|
|
272
|
-
promise.resolve(this.hceRunning);
|
|
337
|
+
public boolean backgroundSessionReady(String sessionId) {
|
|
338
|
+
return HCEManager.getInstance().backgroundSessionReady(this, sessionId);
|
|
273
339
|
}
|
|
274
340
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
package com.itsecrnd.rtnhceandroid;
|
|
2
2
|
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
3
4
|
import androidx.annotation.Nullable;
|
|
4
5
|
|
|
5
|
-
import com.facebook.react.
|
|
6
|
+
import com.facebook.react.BaseReactPackage;
|
|
6
7
|
import com.facebook.react.bridge.NativeModule;
|
|
7
8
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
9
|
import com.facebook.react.module.model.ReactModuleInfo;
|
|
@@ -11,10 +12,10 @@ import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
|
11
12
|
import java.util.HashMap;
|
|
12
13
|
import java.util.Map;
|
|
13
14
|
|
|
14
|
-
public class RTNHCEAndroidPackage extends
|
|
15
|
+
public class RTNHCEAndroidPackage extends BaseReactPackage {
|
|
15
16
|
@Nullable
|
|
16
17
|
@Override
|
|
17
|
-
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
|
|
18
|
+
public NativeModule getModule(@NonNull String name, @NonNull ReactApplicationContext reactContext) {
|
|
18
19
|
if (name.equals(RTNHCEAndroidModule.NAME)) {
|
|
19
20
|
return new RTNHCEAndroidModule(reactContext);
|
|
20
21
|
} else {
|
|
@@ -22,6 +23,7 @@ public class RTNHCEAndroidPackage extends TurboReactPackage {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
@NonNull
|
|
25
27
|
@Override
|
|
26
28
|
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
27
29
|
return () -> {
|
|
@@ -33,7 +35,6 @@ public class RTNHCEAndroidPackage extends TurboReactPackage {
|
|
|
33
35
|
RTNHCEAndroidModule.NAME,
|
|
34
36
|
false, // canOverrideExistingModule
|
|
35
37
|
false, // needsEagerInit
|
|
36
|
-
true, // hasConstants
|
|
37
38
|
false, // isCxxModule
|
|
38
39
|
true // isTurboModule
|
|
39
40
|
));
|