@exxili/capacitor-nfc 0.0.10 → 0.0.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.
- package/README.md +190 -102
- package/android/src/main/kotlin/com/exxili/capacitornfc/NFCPlugin.kt +136 -15
- package/dist/esm/definitions.d.ts +43 -7
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +205 -42
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +2 -6
- package/dist/esm/web.js +10 -22
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +215 -64
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +215 -64
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NFCPlugin/NFCPlugin.swift +40 -13
- package/ios/Sources/NFCPlugin/NFCReader.swift +183 -42
- package/ios/Sources/NFCPlugin/NFCWriter.swift +7 -14
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -9,32 +9,35 @@ A Capacitor plugin for reading and writing NFC tags on iOS and Android devices.
|
|
|
9
9
|
|
|
10
10
|
## Table of Contents
|
|
11
11
|
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
|
|
23
|
-
- [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- [
|
|
30
|
-
|
|
31
|
-
- [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- [
|
|
36
|
-
- [
|
|
37
|
-
- [
|
|
12
|
+
- [Capacitor NFC Plugin (@exxili/capacitor-nfc)](#capacitor-nfc-plugin-exxilicapacitor-nfc)
|
|
13
|
+
- [Table of Contents](#table-of-contents)
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [iOS Setup](#ios-setup)
|
|
16
|
+
- [1. Enable NFC Capability](#1-enable-nfc-capability)
|
|
17
|
+
- [2. Add Usage Description](#2-add-usage-description)
|
|
18
|
+
- [Android Setup](#android-setup)
|
|
19
|
+
- [Usage](#usage)
|
|
20
|
+
- [Reading NFC Tags](#reading-nfc-tags)
|
|
21
|
+
- [Writing NFC Tags](#writing-nfc-tags)
|
|
22
|
+
- [API](#api)
|
|
23
|
+
- [Methods](#methods)
|
|
24
|
+
- [`isSupported()`](#issupported)
|
|
25
|
+
- [`startScan()`](#startscan)
|
|
26
|
+
- [`cancelScan()`](#cancelscan)
|
|
27
|
+
- [`writeNDEF(options: NDEFWriteOptions<T extends string | number[] | Uint8Array = string>)`](#writendefoptions-ndefwriteoptionst-extends-string--number--uint8array--string)
|
|
28
|
+
- [`cancelWriteAndroid()`](#cancelwriteandroid)
|
|
29
|
+
- [Listeners](#listeners)
|
|
30
|
+
- [`onRead(listener: (data: NDEFMessagesTransformable) => void)`](#onreadlistener-data-ndefmessagestransformable--void)
|
|
31
|
+
- [Interfaces](#interfaces)
|
|
32
|
+
- [`NDEFWriteOptions`](#ndefwriteoptions)
|
|
33
|
+
- [`NDEFMessagesTransformable`](#ndefmessagestransformable)
|
|
34
|
+
- [`NDEFMessages`](#ndefmessages)
|
|
35
|
+
- [`NDEFMessage`](#ndefmessage)
|
|
36
|
+
- [`NDEFRecord`](#ndefrecord)
|
|
37
|
+
- [`NFCError`](#nfcerror)
|
|
38
|
+
- [Integration into a Capacitor App](#integration-into-a-capacitor-app)
|
|
39
|
+
- [Example](#example)
|
|
40
|
+
- [License](#license)
|
|
38
41
|
|
|
39
42
|
## Installation
|
|
40
43
|
|
|
@@ -95,20 +98,37 @@ import { NFC } from '@exxili/capacitor-nfc';
|
|
|
95
98
|
To read NFC tags, you need to listen for `nfcTag` events. On iOS, you must also start the NFC scanning session using `startScan()`.
|
|
96
99
|
|
|
97
100
|
```typescript
|
|
98
|
-
import {NFC, NDEFMessagesTransformable, NFCError} from '@exxili/capacitor-nfc';
|
|
101
|
+
import { NFC, NDEFMessagesTransformable, NFCError } from '@exxili/capacitor-nfc';
|
|
99
102
|
|
|
100
|
-
// Start NFC scanning
|
|
103
|
+
// Start NFC scanning (iOS only)
|
|
101
104
|
NFC.startScan().catch((error) => {
|
|
102
105
|
console.error('Error starting NFC scan:', error);
|
|
103
106
|
});
|
|
104
107
|
|
|
105
108
|
// Listen for NFC tag detection
|
|
106
109
|
NFC.onRead((data: NDEFMessagesTransformable) => {
|
|
107
|
-
|
|
110
|
+
// Text (T) and URI (U) records decoded; others best-effort UTF-8
|
|
111
|
+
const asString = data.string();
|
|
112
|
+
console.log('First record text payload:', asString.messages[0]?.records[0]?.payload);
|
|
113
|
+
|
|
114
|
+
// Raw bytes
|
|
115
|
+
const asUint8 = data.uint8Array();
|
|
116
|
+
console.log('First record raw bytes length:', asUint8.messages[0]?.records[0]?.payload.length);
|
|
117
|
+
|
|
118
|
+
// Access tag information (UID, tech types, etc.)
|
|
119
|
+
if (asString.tagInfo) {
|
|
120
|
+
console.log('Tag UID:', asString.tagInfo.uid);
|
|
121
|
+
console.log('Tag technologies:', asString.tagInfo.techTypes);
|
|
122
|
+
console.log('Tag type:', asString.tagInfo.type);
|
|
123
|
+
if (asString.tagInfo.maxSize) {
|
|
124
|
+
console.log('Max NDEF size:', asString.tagInfo.maxSize);
|
|
125
|
+
}
|
|
126
|
+
console.log('Is writable:', asString.tagInfo.isWritable);
|
|
127
|
+
}
|
|
108
128
|
});
|
|
109
129
|
|
|
110
130
|
// Handle NFC errors
|
|
111
|
-
NFC.onError(
|
|
131
|
+
NFC.onError((error: NFCError) => {
|
|
112
132
|
console.error('NFC Error:', error);
|
|
113
133
|
});
|
|
114
134
|
```
|
|
@@ -123,9 +143,17 @@ import { NFC, NDEFWriteOptions, NFCError } from '@exxili/capacitor-nfc';
|
|
|
123
143
|
const message: NDEFWriteOptions = {
|
|
124
144
|
records: [
|
|
125
145
|
{
|
|
126
|
-
type: 'T', // Text record
|
|
146
|
+
type: 'T', // Well Known Text record. String payload will be encoded as: [status][lang='en'][UTF-8 text]
|
|
127
147
|
payload: 'Hello, NFC!',
|
|
128
148
|
},
|
|
149
|
+
{
|
|
150
|
+
type: 'U', // Well Known URI record. String payload encoded as: [0x00][URI bytes]
|
|
151
|
+
payload: 'https://example.com',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: 'T',
|
|
155
|
+
payload: new Uint8Array([0x01, 0x65, 0x48, 0x69]), // Raw bytes preserved (DO NOT re-format)
|
|
156
|
+
},
|
|
129
157
|
],
|
|
130
158
|
};
|
|
131
159
|
|
|
@@ -161,7 +189,7 @@ Returns if NFC is supported on the scanning device.
|
|
|
161
189
|
|
|
162
190
|
#### `startScan()`
|
|
163
191
|
|
|
164
|
-
Starts the NFC scanning session on
|
|
192
|
+
Starts the NFC scanning session on **_iOS only_**. Android devices are always in reading mode, so setting up the `nfcTag` listener is sufficient to handle tag reads on Android.
|
|
165
193
|
|
|
166
194
|
**Returns**: `Promise<void>`
|
|
167
195
|
|
|
@@ -175,17 +203,45 @@ NFC.startScan()
|
|
|
175
203
|
});
|
|
176
204
|
```
|
|
177
205
|
|
|
178
|
-
#### `
|
|
206
|
+
#### `cancelScan()`
|
|
207
|
+
|
|
208
|
+
Immediately invalidates the active iOS CoreNFC reader session (if any). Useful when you want to abort a scan early instead of waiting for the user to cancel or for a tag detection timeout.
|
|
209
|
+
|
|
210
|
+
Platform notes:
|
|
211
|
+
|
|
212
|
+
- iOS: Actively ends the session. Calling `startScan()` again after the returned promise resolves is safe.
|
|
213
|
+
- Android: No-op (Android is always passively listening via foreground dispatch; you generally just ignore future events or control UI state).
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
await NFC.cancelScan(); // iOS: ends session; Android: no-op
|
|
217
|
+
// Optionally restart
|
|
218
|
+
await NFC.startScan();
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Returns**: `Promise<void>`
|
|
222
|
+
|
|
223
|
+
#### `writeNDEF(options: NDEFWriteOptions<T extends string | number[] | Uint8Array = string>)`
|
|
179
224
|
|
|
180
225
|
Writes an NDEF message to an NFC tag.
|
|
181
226
|
|
|
182
|
-
Payload may be provided as a string, `Uint8Array`, or an array of numbers.
|
|
227
|
+
Payload may be provided as a string, `Uint8Array`, or an array of numbers.
|
|
228
|
+
|
|
229
|
+
Automatic formatting rules (to aid interoperability):
|
|
230
|
+
|
|
231
|
+
- Text (`type: 'T'` + string payload): encoded per NFC Forum RTD Text spec `[status][lang=en][UTF-8 text]`.
|
|
232
|
+
- URI (`type: 'U'` + string payload): encoded as `[0x00][UTF-8 URI bytes]` (prefix compression not yet applied).
|
|
233
|
+
- Any other `type` + string payload: UTF-8 bytes only (no extra framing).
|
|
234
|
+
- `Uint8Array` or `number[]` payloads are treated as raw bytes and written verbatim (never altered).
|
|
235
|
+
|
|
236
|
+
If you need full manual control of a Text or URI record, supply raw bytes (number[] / Uint8Array) and the plugin will not modify them.
|
|
237
|
+
|
|
238
|
+
If you attempt to write zero records the promise rejects with `Error("At least one NDEF record is required")`.
|
|
183
239
|
|
|
184
240
|
Android use: since Android has no default UI for reading and writing NFC tags, it is recommended that you add a UI indicator to your application when calling `writeNDEF` and remove it in the `nfcWriteSuccess` listener callback and the `nfcError` listener callback. This will prevent accidental writes to tags that your users intended to read from.
|
|
185
241
|
|
|
186
242
|
**Parameters**:
|
|
187
243
|
|
|
188
|
-
- `options: NDEFWriteOptions<T extends string | number[] | Uint8Array = string>` - The NDEF message to write.
|
|
244
|
+
- `options: NDEFWriteOptions<T extends string | number[] | Uint8Array = string>` - The NDEF message to write. Must include at least one record.
|
|
189
245
|
|
|
190
246
|
**Returns**: `Promise<void>`
|
|
191
247
|
|
|
@@ -209,24 +265,35 @@ Cancels an Android NFC write operation. Android does not have a native UI for NF
|
|
|
209
265
|
|
|
210
266
|
Adds a listener for NFC tag detection events. Returns type `NDEFMessagesTransformable`, which returns the following methods to provide the payload:
|
|
211
267
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
268
|
+
- `string()`: Returns `NDEFMessages<string>`, where all payloads are strings.
|
|
269
|
+
- `base64()`: Returns `NDEFMessages<string>`, where all payloads are the base64-encoded payloads read from the NFC tag.
|
|
270
|
+
- `uint8Array()`: Returns `NDEFMessages<Uint8Array>`, where all payloads are the `Uint8Array` bytes from the NFC tag.
|
|
271
|
+
- `numberArray()`: Returns `NDEFMessages<number[]>`, where all payloads' bytes from the NFC tag are represented as a `number[]`.
|
|
216
272
|
|
|
217
273
|
**Parameters**:
|
|
218
274
|
|
|
219
275
|
- `listener: (data: NDEFMessagesTransformable) => void` - The function to call when an NFC tag is detected.
|
|
220
276
|
|
|
221
|
-
**Returns**: `void`
|
|
277
|
+
**Returns**: `() => void` Unsubscribe function to remove just this listener.
|
|
222
278
|
|
|
223
279
|
```typescript
|
|
224
|
-
NFC.onRead((data
|
|
225
|
-
|
|
280
|
+
const offRead = NFC.onRead((data) => {
|
|
281
|
+
const textRecords = data.string(); // Decoded string representation
|
|
282
|
+
const base64Records = data.base64(); // Original base64 payloads
|
|
283
|
+
const bytesRecords = data.uint8Array(); // Uint8Array payloads
|
|
284
|
+
const numArrayRecords = data.numberArray(); // number[] representation
|
|
285
|
+
console.log(textRecords);
|
|
226
286
|
});
|
|
287
|
+
|
|
288
|
+
// Later (component unmount / cleanup)
|
|
289
|
+
offRead();
|
|
227
290
|
```
|
|
228
291
|
|
|
229
|
-
|
|
292
|
+
> Tip: Register listeners once per screen/component and always dispose them when unmounting to avoid duplicate callbacks.
|
|
293
|
+
|
|
294
|
+
````
|
|
295
|
+
|
|
296
|
+
#### On Error
|
|
230
297
|
|
|
231
298
|
Adds a listener for NFC error events.
|
|
232
299
|
|
|
@@ -234,29 +301,23 @@ Adds a listener for NFC error events.
|
|
|
234
301
|
|
|
235
302
|
- `listener: (error: NFCError) => void` - The function to call when an NFC error occurs.
|
|
236
303
|
|
|
237
|
-
**Returns**: `
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
NFC.onError((error: NFCError) => {
|
|
241
|
-
console.error('NFC Error:', error);
|
|
242
|
-
});
|
|
243
|
-
```
|
|
304
|
+
**Returns**: `() => void` Unsubscribe function.
|
|
244
305
|
|
|
245
|
-
####
|
|
306
|
+
#### On Error
|
|
246
307
|
|
|
247
|
-
Adds a listener for NFC
|
|
308
|
+
Adds a listener for NFC error events.
|
|
248
309
|
|
|
249
310
|
**Parameters**:
|
|
250
311
|
|
|
251
|
-
- `listener: () => void` - The function to call when an
|
|
312
|
+
- `listener: (error: NFCError) => void` - The function to call when an NFC error occurs.
|
|
252
313
|
|
|
253
|
-
**Returns**: `
|
|
314
|
+
**Returns**: `() => void` Unsubscribe function.
|
|
254
315
|
|
|
255
316
|
```typescript
|
|
256
|
-
NFC.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
317
|
+
const offError = NFC.onError(err => console.error('NFC Error:', err));
|
|
318
|
+
// later
|
|
319
|
+
offError();
|
|
320
|
+
````
|
|
260
321
|
|
|
261
322
|
### Interfaces
|
|
262
323
|
|
|
@@ -274,17 +335,17 @@ interface NDEFWriteOptions<T extends string | number[] | Uint8Array = string> {
|
|
|
274
335
|
|
|
275
336
|
Returned by `onRead` and includes the following methods to provide the payload:
|
|
276
337
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
338
|
+
- `string()`: Returns `NDEFMessages<string>`, where all payloads are strings.
|
|
339
|
+
- `base64()`: Returns `NDEFMessages<string>`, where all payloads are the base64-encoded payloads read from the NFC tag.
|
|
340
|
+
- `uint8Array()`: Returns `NDEFMessages<Uint8Array>`, where all payloads are the `Uint8Array` bytes from the NFC tag.
|
|
341
|
+
- `numberArray()`: Returns `NDEFMessages<number[]>`, where all payloads bytes from the NFC tag represented as a `number[]`.
|
|
281
342
|
|
|
282
343
|
```typescript
|
|
283
344
|
interface NDEFMessagesTransformable {
|
|
284
|
-
base64: ()=> NDEFMessages
|
|
285
|
-
uint8Array: ()=> NDEFMessages<Uint8Array>;
|
|
286
|
-
string: ()=> NDEFMessages
|
|
287
|
-
numberArray: ()=> NDEFMessages<number[]>;
|
|
345
|
+
base64: () => NDEFMessages<string>; // Original base64 strings
|
|
346
|
+
uint8Array: () => NDEFMessages<Uint8Array>; // Raw bytes
|
|
347
|
+
string: () => NDEFMessages<string>; // Decoded (T & U handled, others UTF-8 best-effort)
|
|
348
|
+
numberArray: () => NDEFMessages<number[]>; // Raw bytes as number[]
|
|
288
349
|
}
|
|
289
350
|
```
|
|
290
351
|
|
|
@@ -295,6 +356,40 @@ Data received from an NFC tag.
|
|
|
295
356
|
```typescript
|
|
296
357
|
interface NDEFMessages {
|
|
297
358
|
messages: NDEFMessage[];
|
|
359
|
+
tagInfo?: TagInfo;
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### `TagInfo`
|
|
364
|
+
|
|
365
|
+
Information about the NFC tag that was read.
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
interface TagInfo {
|
|
369
|
+
/**
|
|
370
|
+
* The unique identifier of the tag (UID) as a hex string
|
|
371
|
+
*/
|
|
372
|
+
uid: string;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The NFC tag technology types supported
|
|
376
|
+
*/
|
|
377
|
+
techTypes: string[];
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* The maximum size of NDEF message that can be written to this tag (if applicable)
|
|
381
|
+
*/
|
|
382
|
+
maxSize?: number;
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Whether the tag is writable
|
|
386
|
+
*/
|
|
387
|
+
isWritable?: boolean;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The tag type (e.g., "ISO14443-4", "MifareClassic", etc.)
|
|
391
|
+
*/
|
|
392
|
+
type?: string;
|
|
298
393
|
}
|
|
299
394
|
```
|
|
300
395
|
|
|
@@ -324,7 +419,7 @@ interface NDEFRecord<T = number[]> {
|
|
|
324
419
|
*/
|
|
325
420
|
payload: T;
|
|
326
421
|
}
|
|
327
|
-
|
|
422
|
+
```
|
|
328
423
|
|
|
329
424
|
#### `NFCError`
|
|
330
425
|
|
|
@@ -363,54 +458,47 @@ To integrate this plugin into your Capacitor app:
|
|
|
363
458
|
Here's a complete example of how to read and write NFC tags in your app:
|
|
364
459
|
|
|
365
460
|
```typescript
|
|
366
|
-
import { NFC,
|
|
461
|
+
import { NFC, NDEFWriteOptions, NFCError, NDEFMessagesTransformable } from '@exxili/capacitor-nfc';
|
|
367
462
|
|
|
368
|
-
// Check if NFC is supported
|
|
463
|
+
// Check if NFC is supported (optional gating logic)
|
|
369
464
|
const { supported } = await NFC.isSupported();
|
|
465
|
+
if (!supported) {
|
|
466
|
+
console.warn('NFC not supported on this device');
|
|
467
|
+
}
|
|
370
468
|
|
|
371
|
-
// Start NFC scanning
|
|
372
|
-
NFC.startScan().catch((
|
|
373
|
-
console.error('Error starting NFC scan:', error);
|
|
374
|
-
});
|
|
469
|
+
// Start NFC scanning (needed on iOS only)
|
|
470
|
+
NFC.startScan().catch((err) => console.error('Failed to start scan', err));
|
|
375
471
|
|
|
376
|
-
//
|
|
377
|
-
NFC.onRead((data:
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
// Print all Uint8Array payloads
|
|
382
|
-
console.log('Received NFC tag:', stringMessages.messages?.at(0)?.records?.at(0).payload); // prints string[]
|
|
383
|
-
console.log('Received NFC tag:', uint8ArrayPayloads.messages?.at(0)?.records?.at(0).payload); // prints Uint8Array[]
|
|
384
|
-
});
|
|
472
|
+
// Read listener returns a transformable wrapper
|
|
473
|
+
const offRead = NFC.onRead((data: NDEFMessagesTransformable) => {
|
|
474
|
+
const textView = data.string(); // NDEFMessages<string>
|
|
475
|
+
const rawBytesView = data.uint8Array(); // NDEFMessages<Uint8Array>
|
|
385
476
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
console.
|
|
477
|
+
const firstText = textView.messages[0]?.records[0]?.payload;
|
|
478
|
+
const firstLength = rawBytesView.messages[0]?.records[0]?.payload.length;
|
|
479
|
+
console.log('First text record:', firstText);
|
|
480
|
+
console.log('First record byte length:', firstLength);
|
|
389
481
|
});
|
|
390
482
|
|
|
391
|
-
//
|
|
483
|
+
// Error listener (covers read & write errors)
|
|
484
|
+
const offError = NFC.onError((error: NFCError) => console.error('NFC Error:', error));
|
|
485
|
+
|
|
486
|
+
// Prepare an NDEF message to write (auto-formats Text/URI if payload is string)
|
|
392
487
|
const message: NDEFWriteOptions = {
|
|
393
488
|
records: [
|
|
394
|
-
{
|
|
395
|
-
|
|
396
|
-
payload: 'Hello, NFC!',
|
|
397
|
-
},
|
|
489
|
+
{ type: 'T', payload: 'Hello, NFC!' },
|
|
490
|
+
{ type: 'U', payload: 'https://example.com' },
|
|
398
491
|
],
|
|
399
492
|
};
|
|
400
493
|
|
|
401
|
-
|
|
402
|
-
NFC.writeNDEF(message)
|
|
403
|
-
.then(() => {
|
|
404
|
-
console.log('Write initiated');
|
|
405
|
-
})
|
|
406
|
-
.catch((error) => {
|
|
407
|
-
console.error('Error writing to NFC tag:', error);
|
|
408
|
-
});
|
|
494
|
+
await NFC.writeNDEF(message).catch((err) => console.error('Write failed', err));
|
|
409
495
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
496
|
+
const offWrite = NFC.onWrite(() => console.log('Write success'));
|
|
497
|
+
|
|
498
|
+
// Later (cleanup)
|
|
499
|
+
offRead();
|
|
500
|
+
offError();
|
|
501
|
+
offWrite();
|
|
414
502
|
```
|
|
415
503
|
|
|
416
504
|
## License
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.exxili.capacitornfc
|
|
2
2
|
|
|
3
|
+
import android.app.ActivityOptions
|
|
3
4
|
import android.app.PendingIntent
|
|
4
5
|
import android.content.Intent
|
|
5
6
|
import android.content.IntentFilter
|
|
@@ -22,7 +23,10 @@ import android.nfc.tech.NfcB
|
|
|
22
23
|
import android.nfc.tech.NfcBarcode
|
|
23
24
|
import android.nfc.tech.NfcF
|
|
24
25
|
import android.nfc.tech.NfcV
|
|
26
|
+
import android.os.Build
|
|
27
|
+
import android.os.Bundle
|
|
25
28
|
import android.util.Log
|
|
29
|
+
import androidx.annotation.RequiresApi
|
|
26
30
|
import com.getcapacitor.JSArray
|
|
27
31
|
import com.getcapacitor.JSObject
|
|
28
32
|
import com.getcapacitor.Plugin
|
|
@@ -53,6 +57,7 @@ class NFCPlugin : Plugin() {
|
|
|
53
57
|
NfcV::class.java.name
|
|
54
58
|
))
|
|
55
59
|
|
|
60
|
+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
|
56
61
|
public override fun handleOnNewIntent(intent: Intent?) {
|
|
57
62
|
super.handleOnNewIntent(intent)
|
|
58
63
|
|
|
@@ -66,9 +71,9 @@ class NFCPlugin : Plugin() {
|
|
|
66
71
|
writeMode = false
|
|
67
72
|
recordsBuffer = null
|
|
68
73
|
}
|
|
69
|
-
|
|
74
|
+
else if (ACTION_NDEF_DISCOVERED == intent.action || ACTION_TAG_DISCOVERED == intent.action || ACTION_TECH_DISCOVERED == intent.action) {
|
|
70
75
|
Log.d("NFC", "READ MODE START")
|
|
71
|
-
|
|
76
|
+
handleReadTag(intent)
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
|
|
@@ -115,8 +120,28 @@ class NFCPlugin : Plugin() {
|
|
|
115
120
|
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
116
121
|
}
|
|
117
122
|
|
|
123
|
+
val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
124
|
+
PendingIntent.FLAG_MUTABLE
|
|
125
|
+
} else {
|
|
126
|
+
PendingIntent.FLAG_UPDATE_CURRENT
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
var activityOptionsBundle: Bundle? = null
|
|
130
|
+
|
|
131
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // API 35 (Android 15)
|
|
132
|
+
activityOptionsBundle = ActivityOptions.makeBasic().apply {
|
|
133
|
+
setPendingIntentCreatorBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
|
|
134
|
+
}.toBundle()
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
val pendingIntent =
|
|
119
|
-
PendingIntent.getActivity(
|
|
138
|
+
PendingIntent.getActivity(
|
|
139
|
+
this.activity,
|
|
140
|
+
0,
|
|
141
|
+
intent,
|
|
142
|
+
pendingIntentFlags,
|
|
143
|
+
activityOptionsBundle
|
|
144
|
+
)
|
|
120
145
|
|
|
121
146
|
val intentFilter: Array<IntentFilter> =
|
|
122
147
|
arrayOf(
|
|
@@ -139,6 +164,7 @@ class NFCPlugin : Plugin() {
|
|
|
139
164
|
)
|
|
140
165
|
}
|
|
141
166
|
|
|
167
|
+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
|
142
168
|
private fun handleWriteTag(intent: Intent) {
|
|
143
169
|
val records = recordsBuffer?.toList<JSONObject>()
|
|
144
170
|
if(records != null) {
|
|
@@ -284,32 +310,127 @@ class NFCPlugin : Plugin() {
|
|
|
284
310
|
}
|
|
285
311
|
}
|
|
286
312
|
|
|
313
|
+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
|
287
314
|
private fun handleReadTag(intent: Intent) {
|
|
288
315
|
val jsResponse = JSObject()
|
|
289
|
-
|
|
290
316
|
val ndefMessages = JSArray()
|
|
317
|
+
|
|
318
|
+
// Get tag information regardless of NDEF content
|
|
319
|
+
val tag: Tag? = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java)
|
|
320
|
+
val tagInfo = tag?.let { extractTagInfo(it) }
|
|
321
|
+
|
|
322
|
+
// Try to obtain raw NDEF messages first (ACTION_NDEF_DISCOVERED path)
|
|
291
323
|
val receivedMessages = intent.getParcelableArrayExtra(
|
|
292
324
|
EXTRA_NDEF_MESSAGES,
|
|
293
325
|
NdefMessage::class.java
|
|
294
326
|
)
|
|
295
327
|
|
|
296
|
-
receivedMessages
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
328
|
+
if (receivedMessages != null && receivedMessages.isNotEmpty()) {
|
|
329
|
+
// Standard NDEF-discovered path
|
|
330
|
+
for (message in receivedMessages) {
|
|
331
|
+
ndefMessages.put(ndefMessageToJS(message))
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
// For ACTION_TAG_DISCOVERED or ACTION_TECH_DISCOVERED we may still have an NDEF tag.
|
|
335
|
+
var added = false
|
|
336
|
+
if (tag != null) {
|
|
337
|
+
val ndef = Ndef.get(tag)
|
|
338
|
+
if (ndef != null) {
|
|
339
|
+
try {
|
|
340
|
+
ndef.connect()
|
|
341
|
+
// Prefer cached message to avoid additional IO if available
|
|
342
|
+
val message: NdefMessage? = ndef.cachedNdefMessage ?: try {
|
|
343
|
+
ndef.ndefMessage
|
|
344
|
+
} catch (e: Exception) { null }
|
|
345
|
+
if (message != null) {
|
|
346
|
+
ndefMessages.put(ndefMessageToJS(message))
|
|
347
|
+
added = true
|
|
348
|
+
}
|
|
349
|
+
} catch (e: Exception) {
|
|
350
|
+
Log.w("NFC", "Failed to read NDEF message from TECH/TAG intent: ${e.message}")
|
|
351
|
+
} finally {
|
|
352
|
+
try { ndef.close() } catch (_: Exception) {}
|
|
353
|
+
}
|
|
304
354
|
}
|
|
305
355
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
356
|
+
// If no NDEF message found, fallback to tag ID (legacy behavior)
|
|
357
|
+
if (!added) {
|
|
358
|
+
val tagId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID) ?: tag.id
|
|
359
|
+
val result = if (tagId != null) byteArrayToHexString(tagId) else ""
|
|
360
|
+
val rec = JSObject()
|
|
361
|
+
rec.put("type", "ID")
|
|
362
|
+
rec.put("payload", Base64.getEncoder().encodeToString(result.toByteArray()))
|
|
363
|
+
val ndefRecords = JSArray().apply { put(rec) }
|
|
364
|
+
val msg = JSObject().apply { put("records", ndefRecords) }
|
|
365
|
+
ndefMessages.put(msg)
|
|
366
|
+
}
|
|
309
367
|
}
|
|
310
368
|
}
|
|
311
369
|
|
|
312
370
|
jsResponse.put("messages", ndefMessages)
|
|
371
|
+
// Always include tag information if available
|
|
372
|
+
if (tagInfo != null) {
|
|
373
|
+
jsResponse.put("tagInfo", tagInfo)
|
|
374
|
+
}
|
|
313
375
|
this.notifyListeners("nfcTag", jsResponse)
|
|
314
376
|
}
|
|
377
|
+
|
|
378
|
+
private fun extractTagInfo(tag: Tag): JSObject {
|
|
379
|
+
val tagInfo = JSObject()
|
|
380
|
+
|
|
381
|
+
// Always include UID
|
|
382
|
+
val uid = byteArrayToHexString(tag.id)
|
|
383
|
+
tagInfo.put("uid", uid)
|
|
384
|
+
|
|
385
|
+
// Include technology types
|
|
386
|
+
val techTypes = JSArray()
|
|
387
|
+
for (tech in tag.techList) {
|
|
388
|
+
techTypes.put(tech)
|
|
389
|
+
}
|
|
390
|
+
tagInfo.put("techTypes", techTypes)
|
|
391
|
+
|
|
392
|
+
// Try to get NDEF-specific information
|
|
393
|
+
val ndef = Ndef.get(tag)
|
|
394
|
+
if (ndef != null) {
|
|
395
|
+
try {
|
|
396
|
+
ndef.connect()
|
|
397
|
+
tagInfo.put("maxSize", ndef.maxSize)
|
|
398
|
+
tagInfo.put("isWritable", ndef.isWritable)
|
|
399
|
+
tagInfo.put("type", ndef.type)
|
|
400
|
+
} catch (e: Exception) {
|
|
401
|
+
Log.w("NFC", "Failed to read NDEF tag info: ${e.message}")
|
|
402
|
+
} finally {
|
|
403
|
+
try { ndef.close() } catch (_: Exception) {}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return tagInfo
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private fun ndefMessageToJS(message: NdefMessage): JSObject {
|
|
411
|
+
val ndefRecords = JSArray()
|
|
412
|
+
for (record in message.records) {
|
|
413
|
+
val rec = JSObject()
|
|
414
|
+
rec.put("type", String(record.type, Charsets.UTF_8))
|
|
415
|
+
rec.put("payload", Base64.getEncoder().encodeToString(record.payload))
|
|
416
|
+
ndefRecords.put(rec)
|
|
417
|
+
}
|
|
418
|
+
val msg = JSObject()
|
|
419
|
+
msg.put("records", ndefRecords)
|
|
420
|
+
return msg
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private fun byteArrayToHexString(inarray: ByteArray): String {
|
|
424
|
+
val hex = arrayOf("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F")
|
|
425
|
+
var out = ""
|
|
426
|
+
|
|
427
|
+
for (j in inarray.indices) {
|
|
428
|
+
val `in` = inarray[j].toInt() and 0xff
|
|
429
|
+
val i1 = (`in` shr 4) and 0x0f
|
|
430
|
+
out += hex[i1]
|
|
431
|
+
val i2 = `in` and 0x0f
|
|
432
|
+
out += hex[i2]
|
|
433
|
+
}
|
|
434
|
+
return out
|
|
435
|
+
}
|
|
315
436
|
}
|