@exxili/capacitor-nfc 0.0.3 → 0.0.9

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
@@ -1,31 +1,33 @@
1
1
  # Capacitor NFC Plugin (@exxili/capacitor-nfc)
2
2
 
3
- A Capacitor plugin for reading and writing NFC tags on iOS devices. This plugin allows you to:
3
+ A Capacitor plugin for reading and writing NFC tags on iOS and Android devices. This plugin allows you to:
4
4
 
5
5
  - Read NDEF messages from NFC tags.
6
6
  - Write NDEF messages to NFC tags.
7
7
 
8
- **Note**: NFC functionality is only available on compatible iOS devices running iOS 13.0 or later. Android NFC functionality is still
9
- in development
8
+ **Note**: NFC functionality is only available on compatible iOS devices running iOS 13.0 or later.
10
9
 
11
10
  ## Table of Contents
12
11
 
13
12
  - [Installation](#installation)
14
13
  - [iOS Setup](#ios-setup)
15
- - Android Setup (in Development)
14
+ - [Android Setup](#android-setup)
16
15
  - [Usage](#usage)
17
16
  - [Reading NFC Tags](#reading-nfc-tags)
18
17
  - [Writing NFC Tags](#writing-nfc-tags)
19
18
  - [API](#api)
20
19
  - [Methods](#methods)
20
+ - [`isSupported()`](#issupported)
21
21
  - [`startScan()`](#startscan)
22
- - [`writeNDEF(options)`](#writendefoptions)
22
+ - [`writeNDEF(options)`](#writendefoptions-ndefwriteoptionst-extends-string--number--uint8array--string)
23
+ - [`cancelWriteAndroid`](#cancelwriteandroid)
23
24
  - [Listeners](#listeners)
24
- - [`addListener('nfcTag', listener)`](#addlistenernfctag-listener)
25
- - [`addListener('nfcError', listener)`](#addlistenernfcerror-listener)
26
- - [`addListener('nfcWriteSuccess', listener)`](#addlistenernfcwritesuccess-listener)
25
+ - [`onRead(listener)`](#onreadlistener-data-ndefmessagestransformable--void)
26
+ - [`onError('listener)`](#onerrorlistener-error-nfcerror--void)
27
+ - [`onWrite(listener)`](#onwritelistener---void)
27
28
  - [Interfaces](#interfaces)
28
29
  - [`NDEFWriteOptions`](#ndefwriteoptions)
30
+ - [`NDEFWriteOptions`](#ndefmessagestransformable)
29
31
  - [`NDEFMessages`](#ndefmessages)
30
32
  - [`NDEFMessage`](#ndefmessage)
31
33
  - [`NDEFRecord`](#ndefrecord)
@@ -71,6 +73,15 @@ In your `Info.plist` file (usually located at `ios/App/App/Info.plist`), add:
71
73
 
72
74
  Replace the description with a message that explains why your app needs NFC access.
73
75
 
76
+ ## Android Setup
77
+
78
+ Add the following to your `AndroidManifest.xml` file:
79
+
80
+ ```xml
81
+ <uses-permission android:name="android.permission.NFC" />
82
+ <uses-feature android:name="android.hardware.nfc" android:required="true" />
83
+ ```
84
+
74
85
  ## Usage
75
86
 
76
87
  Import the plugin into your code:
@@ -81,10 +92,10 @@ import { NFC } from '@exxili/capacitor-nfc';
81
92
 
82
93
  ### Reading NFC Tags
83
94
 
84
- To read NFC tags, you need to start a scanning session and listen for `nfcTag` events.
95
+ To read NFC tags, you need to listen for `nfcTag` events. On iOS, you must also start the NFC scanning session using `startScan()`.
85
96
 
86
97
  ```typescript
87
- import { NFC, NDEFMessages, NFCError } from '@exxili/capacitor-nfc';
98
+ import {NFC, NDEFMessagesTransformable, NFCError} from '@exxili/capacitor-nfc';
88
99
 
89
100
  // Start NFC scanning
90
101
  NFC.startScan().catch((error) => {
@@ -92,19 +103,19 @@ NFC.startScan().catch((error) => {
92
103
  });
93
104
 
94
105
  // Listen for NFC tag detection
95
- const nfcTagListener = NFC.addListener('nfcTag', (data: NDEFMessages) => {
96
- console.log('Received NFC tag:', data);
106
+ NFC.onRead((data: NDEFMessagesTransformable) => {
107
+ console.log('Received NFC tag:', data.string());
97
108
  });
98
109
 
99
110
  // Handle NFC errors
100
- const nfcErrorListener = NFC.addListener('nfcError', (error: NFCError) => {
111
+ NFC.onError('nfcError', (error: NFCError) => {
101
112
  console.error('NFC Error:', error);
102
113
  });
103
114
  ```
104
115
 
105
116
  ### Writing NFC Tags
106
117
 
107
- To write NDEF messages to NFC tags, use the `writeNDEF` method and listen for `nfcWriteSuccess` events.
118
+ To write NDEF messages to NFC tags, use the `writeNDEF` method and listen for `onWrite` events.
108
119
 
109
120
  ```typescript
110
121
  import { NFC, NDEFWriteOptions, NFCError } from '@exxili/capacitor-nfc';
@@ -128,12 +139,12 @@ NFC.writeNDEF(message)
128
139
  });
129
140
 
130
141
  // Listen for write success
131
- const nfcWriteSuccessListener = NFC.addListener('nfcWriteSuccess', () => {
142
+ NFC.onWrite(() => {
132
143
  console.log('NDEF message written successfully.');
133
144
  });
134
145
 
135
146
  // Handle NFC errors
136
- const nfcErrorListener = NFC.addListener('nfcError', (error: NFCError) => {
147
+ NFC.onError((error: NFCError) => {
137
148
  console.error('NFC Error:', error);
138
149
  });
139
150
  ```
@@ -142,9 +153,15 @@ const nfcErrorListener = NFC.addListener('nfcError', (error: NFCError) => {
142
153
 
143
154
  ### Methods
144
155
 
156
+ #### `isSupported()`
157
+
158
+ Returns if NFC is supported on the scanning device.
159
+
160
+ **Returns**: `Promise<{ supported: boolean }>`
161
+
145
162
  #### `startScan()`
146
163
 
147
- Starts the NFC scanning session.
164
+ 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.
148
165
 
149
166
  **Returns**: `Promise<void>`
150
167
 
@@ -158,13 +175,17 @@ NFC.startScan()
158
175
  });
159
176
  ```
160
177
 
161
- #### `writeNDEF(options: NDEFWriteOptions)`
178
+ #### `writeNDEF(options: NDEFWriteOptions<T extends string | number[] | Uint8Array = string)`
162
179
 
163
180
  Writes an NDEF message to an NFC tag.
164
181
 
182
+ Payload may be provided as a string, `Uint8Array`, or an array of numbers. The plugin will automatically convert the payload to a byte array for storage on the NFC tag.
183
+
184
+ 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
+
165
186
  **Parameters**:
166
187
 
167
- - `options: NDEFWriteOptions` - The NDEF message to write.
188
+ - `options: NDEFWriteOptions<T extends string | number[] | Uint8Array = string>` - The NDEF message to write.
168
189
 
169
190
  **Returns**: `Promise<void>`
170
191
 
@@ -178,55 +199,61 @@ NFC.writeNDEF(options)
178
199
  });
179
200
  ```
180
201
 
202
+ #### `cancelWriteAndroid()`
203
+
204
+ Cancels an Android NFC write operation. Android does not have a native UI for NFC tag writing, so this method allows developers to hook up a custom UI to cancel an in-progress scan.
205
+
181
206
  ### Listeners
182
207
 
183
- #### `addListener('nfcTag', listener: (data: NDEFMessages) => void)`
208
+ #### `onRead(listener: (data: NDEFMessagesTransformable) => void)`
209
+
210
+ Adds a listener for NFC tag detection events. Returns type `NDEFMessagesTransformable`, which returns the following methods to provide the payload:
184
211
 
185
- Adds a listener for NFC tag detection events.
212
+ * `string()`: Returns `NDEFMessages<string>`, where all payloads are strings.
213
+ * `base64()`: Returns `NDEFMessages<string>`, where all payloads are the base64-encoded payloads read from the NFC tag.
214
+ * `uint8Array()`: Returns `NDEFMessages<Uint8Array>`, where all payloads are the `Uint8Array` bytes from the NFC tag.
215
+ * `numberArray()`: Returns `NDEFMessages<number[]>`, where all payloads bytes from the NFC tag represented as a `number[]`.
186
216
 
187
217
  **Parameters**:
188
218
 
189
- - `eventName: 'nfcTag'`
190
- - `listener: (data: NDEFMessages) => void` - The function to call when an NFC tag is detected.
219
+ - `listener: (data: NDEFMessagesTransformable) => void` - The function to call when an NFC tag is detected.
191
220
 
192
- **Returns**: `PluginListenerHandle`
221
+ **Returns**: `void`
193
222
 
194
223
  ```typescript
195
- const nfcTagListener = NFC.addListener('nfcTag', (data: NDEFMessages) => {
224
+ NFC.onRead((data: NDEFMessages) => {
196
225
  console.log('Received NFC tag:', data);
197
226
  });
198
227
  ```
199
228
 
200
- #### `addListener('nfcError', listener: (error: NFCError) => void)`
229
+ #### `onError(listener: (error: NFCError) => void)`
201
230
 
202
231
  Adds a listener for NFC error events.
203
232
 
204
233
  **Parameters**:
205
234
 
206
- - `eventName: 'nfcError'`
207
235
  - `listener: (error: NFCError) => void` - The function to call when an NFC error occurs.
208
236
 
209
237
  **Returns**: `PluginListenerHandle`
210
238
 
211
239
  ```typescript
212
- const nfcErrorListener = NFC.addListener('nfcError', (error: NFCError) => {
240
+ NFC.onError((error: NFCError) => {
213
241
  console.error('NFC Error:', error);
214
242
  });
215
243
  ```
216
244
 
217
- #### `addListener('nfcWriteSuccess', listener: () => void)`
245
+ #### `onWrite(listener: () => void)`
218
246
 
219
247
  Adds a listener for NFC write success events.
220
248
 
221
249
  **Parameters**:
222
250
 
223
- - `eventName: 'nfcWriteSuccess'`
224
251
  - `listener: () => void` - The function to call when an NDEF message has been written successfully.
225
252
 
226
253
  **Returns**: `PluginListenerHandle`
227
254
 
228
255
  ```typescript
229
- const nfcWriteSuccessListener = NFC.addListener('nfcWriteSuccess', () => {
256
+ NFC.onWrite('nfcWriteSuccess', () => {
230
257
  console.log('NDEF message written successfully.');
231
258
  });
232
259
  ```
@@ -238,8 +265,26 @@ const nfcWriteSuccessListener = NFC.addListener('nfcWriteSuccess', () => {
238
265
  Options for writing an NDEF message.
239
266
 
240
267
  ```typescript
241
- interface NDEFWriteOptions {
242
- records: NDEFRecord[];
268
+ interface NDEFWriteOptions<T extends string | number[] | Uint8Array = string> {
269
+ records: NDEFRecord<T>[];
270
+ }
271
+ ```
272
+
273
+ #### `NDEFMessagesTransformable`
274
+
275
+ Returned by `onRead` and includes the following methods to provide the payload:
276
+
277
+ * `string()`: Returns `NDEFMessages<string>`, where all payloads are strings.
278
+ * `base64()`: Returns `NDEFMessages<string>`, where all payloads are the base64-encoded payloads read from the NFC tag.
279
+ * `uint8Array()`: Returns `NDEFMessages<Uint8Array>`, where all payloads are the `Uint8Array` bytes from the NFC tag.
280
+ * `numberArray()`: Returns `NDEFMessages<number[]>`, where all payloads bytes from the NFC tag represented as a `number[]`.
281
+
282
+ ```typescript
283
+ interface NDEFMessagesTransformable {
284
+ base64: ()=> NDEFMessages;
285
+ uint8Array: ()=> NDEFMessages<Uint8Array>;
286
+ string: ()=> NDEFMessages;
287
+ numberArray: ()=> NDEFMessages<number[]>;
243
288
  }
244
289
  ```
245
290
 
@@ -265,10 +310,10 @@ interface NDEFMessage {
265
310
 
266
311
  #### `NDEFRecord`
267
312
 
268
- An NDEF record.
313
+ An NDEF record. `payload` is, by default, an array of bytes representing the data; this is how an `NDEFRecord` is read from an NFC tag. You can choose to provide an `NDEFRecord` as a string a `Uint8Array` also.
269
314
 
270
315
  ```typescript
271
- interface NDEFRecord {
316
+ interface NDEFRecord<T = number[]> {
272
317
  /**
273
318
  * The type of the record.
274
319
  */
@@ -277,9 +322,9 @@ interface NDEFRecord {
277
322
  /**
278
323
  * The payload of the record.
279
324
  */
280
- payload: string;
325
+ payload: T;
281
326
  }
282
- ```
327
+ ````
283
328
 
284
329
  #### `NFCError`
285
330
 
@@ -320,18 +365,26 @@ Here's a complete example of how to read and write NFC tags in your app:
320
365
  ```typescript
321
366
  import { NFC, NDEFMessages, NDEFWriteOptions, NFCError } from '@exxili/capacitor-nfc';
322
367
 
323
- // Start NFC scanning
368
+ // Check if NFC is supported
369
+ const { supported } = await NFC.isSupported();
370
+
371
+ // Start NFC scanning -- iOS only
324
372
  NFC.startScan().catch((error) => {
325
373
  console.error('Error starting NFC scan:', error);
326
374
  });
327
375
 
328
376
  // Listen for NFC tag detection
329
- const nfcTagListener = NFC.addListener('nfcTag', (data: NDEFMessages) => {
330
- console.log('Received NFC tag:', data);
377
+ NFC.onRead((data: NDEFMessages) => {
378
+ const stringMessages: NDEFMessage<string> = data.string();
379
+ const uint8ArrayMessages: NDEFMessage<Uint8Array> = data.uint8Array();
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[]
331
384
  });
332
385
 
333
386
  // Handle NFC errors
334
- const nfcErrorListener = NFC.addListener('nfcError', (error: NFCError) => {
387
+ NFC.onError((error: NFCError) => {
335
388
  console.error('NFC Error:', error);
336
389
  });
337
390
 
@@ -355,14 +408,14 @@ NFC.writeNDEF(message)
355
408
  });
356
409
 
357
410
  // Listen for write success
358
- const nfcWriteSuccessListener = NFC.addListener('nfcWriteSuccess', () => {
411
+ NFC.onWrite('nfcWriteSuccess', () => {
359
412
  console.log('NDEF message written successfully.');
360
413
  });
361
414
  ```
362
415
 
363
416
  ## License
364
417
 
365
- [MIT License](LICENSE)
418
+ [MIT License](https://opensource.org/license/mit)
366
419
 
367
420
  ---
368
421
 
@@ -6,22 +6,27 @@ ext {
6
6
  }
7
7
 
8
8
  buildscript {
9
+ ext {
10
+ kotlin_version = '2.1.21'
11
+ }
9
12
  repositories {
10
13
  google()
11
14
  mavenCentral()
12
15
  }
13
16
  dependencies {
14
- classpath 'com.android.tools.build:gradle:8.2.1'
17
+ classpath 'com.android.tools.build:gradle:8.2.2'
18
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
15
19
  }
16
20
  }
17
21
 
18
22
  apply plugin: 'com.android.library'
23
+ apply plugin: 'org.jetbrains.kotlin.android'
19
24
 
20
25
  android {
21
26
  namespace "com.exxili.capacitornfc"
22
27
  compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
23
28
  defaultConfig {
24
- minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
29
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 33
25
30
  targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
26
31
  versionCode 1
27
32
  versionName "1.0"
@@ -40,6 +45,9 @@ android {
40
45
  sourceCompatibility JavaVersion.VERSION_17
41
46
  targetCompatibility JavaVersion.VERSION_17
42
47
  }
48
+ kotlinOptions {
49
+ jvmTarget = '17'
50
+ }
43
51
  }
44
52
 
45
53
  repositories {
@@ -52,6 +60,7 @@ dependencies {
52
60
  implementation fileTree(dir: 'libs', include: ['*.jar'])
53
61
  implementation project(':capacitor-android')
54
62
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
63
+ implementation 'androidx.core:core-ktx:1.16.0'
55
64
  testImplementation "junit:junit:$junitVersion"
56
65
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
57
66
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -0,0 +1,11 @@
1
+ package com.exxili.capacitornfc
2
+
3
+ import android.os.Bundle
4
+ import com.getcapacitor.BridgeActivity
5
+
6
+ class MainActivity : BridgeActivity() {
7
+ public override fun onCreate(savedInstanceState: Bundle?) {
8
+ super.onCreate(savedInstanceState)
9
+ registerPlugin(NFCPlugin::class.java)
10
+ }
11
+ }
@@ -0,0 +1,315 @@
1
+ package com.exxili.capacitornfc
2
+
3
+ import android.app.PendingIntent
4
+ import android.content.Intent
5
+ import android.content.IntentFilter
6
+ import android.nfc.NdefMessage
7
+ import android.nfc.NdefRecord
8
+ import android.nfc.NfcAdapter
9
+ import android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED
10
+ import android.nfc.NfcAdapter.ACTION_TAG_DISCOVERED
11
+ import android.nfc.NfcAdapter.ACTION_TECH_DISCOVERED
12
+ import android.nfc.NfcAdapter.EXTRA_NDEF_MESSAGES
13
+ import android.nfc.NfcAdapter.getDefaultAdapter
14
+ import android.nfc.Tag
15
+ import android.nfc.tech.IsoDep
16
+ import android.nfc.tech.MifareClassic
17
+ import android.nfc.tech.MifareUltralight
18
+ import android.nfc.tech.Ndef
19
+ import android.nfc.tech.NdefFormatable
20
+ import android.nfc.tech.NfcA
21
+ import android.nfc.tech.NfcB
22
+ import android.nfc.tech.NfcBarcode
23
+ import android.nfc.tech.NfcF
24
+ import android.nfc.tech.NfcV
25
+ import android.util.Log
26
+ import com.getcapacitor.JSArray
27
+ import com.getcapacitor.JSObject
28
+ import com.getcapacitor.Plugin
29
+ import com.getcapacitor.PluginCall
30
+ import com.getcapacitor.PluginMethod
31
+ import com.getcapacitor.annotation.CapacitorPlugin
32
+ import org.json.JSONObject
33
+ import java.io.IOException
34
+ import java.io.UnsupportedEncodingException
35
+ import java.nio.charset.Charset
36
+ import java.util.Base64
37
+
38
+ @CapacitorPlugin(name = "NFC")
39
+ class NFCPlugin : Plugin() {
40
+ private var writeMode = false
41
+ private var recordsBuffer: JSArray? = null
42
+
43
+ private val techListsArray = arrayOf(arrayOf<String>(
44
+ IsoDep::class.java.name,
45
+ MifareClassic::class.java.name,
46
+ MifareUltralight::class.java.name,
47
+ Ndef::class.java.name,
48
+ NdefFormatable::class.java.name,
49
+ NfcBarcode::class.java.name,
50
+ NfcA::class.java.name,
51
+ NfcB::class.java.name,
52
+ NfcF::class.java.name,
53
+ NfcV::class.java.name
54
+ ))
55
+
56
+ public override fun handleOnNewIntent(intent: Intent?) {
57
+ super.handleOnNewIntent(intent)
58
+
59
+ if (intent == null || intent.action.isNullOrBlank()) {
60
+ return
61
+ }
62
+
63
+ if (writeMode) {
64
+ Log.d("NFC", "WRITE MODE START")
65
+ handleWriteTag(intent)
66
+ writeMode = false
67
+ recordsBuffer = null
68
+ }
69
+ else if (ACTION_NDEF_DISCOVERED == intent.action) {
70
+ Log.d("NFC", "READ MODE START")
71
+ handleReadTag(intent)
72
+ }
73
+ }
74
+
75
+ @PluginMethod
76
+ fun isSupported(call: PluginCall) {
77
+ val adapter = NfcAdapter.getDefaultAdapter(this.activity)
78
+ val ret = JSObject()
79
+ ret.put("supported", adapter != null)
80
+ call.resolve(ret)
81
+ }
82
+
83
+ @PluginMethod
84
+ fun cancelWriteAndroid(call: PluginCall) {
85
+ this.writeMode = false
86
+ call.resolve()
87
+ }
88
+
89
+ @PluginMethod
90
+ fun startScan(call: PluginCall) {
91
+ print("startScan called")
92
+ call.reject("Android NFC scanning does not require 'startScan' method.")
93
+ }
94
+
95
+ @PluginMethod
96
+ fun writeNDEF(call: PluginCall) {
97
+ print("writeNDEF called")
98
+
99
+ writeMode = true
100
+ recordsBuffer = call.getArray("records")
101
+
102
+ call.resolve()
103
+ }
104
+
105
+ override fun handleOnPause() {
106
+ super.handleOnPause()
107
+ getDefaultAdapter(this.activity)?.disableForegroundDispatch(this.activity)
108
+ }
109
+
110
+ override fun handleOnResume() {
111
+ super.handleOnResume()
112
+ if(getDefaultAdapter(this.activity) == null) return;
113
+
114
+ val intent = Intent(context, this.activity.javaClass).apply {
115
+ addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
116
+ }
117
+
118
+ val pendingIntent =
119
+ PendingIntent.getActivity(this.activity, 0, intent, PendingIntent.FLAG_MUTABLE)
120
+
121
+ val intentFilter: Array<IntentFilter> =
122
+ arrayOf(
123
+ IntentFilter(ACTION_NDEF_DISCOVERED).apply {
124
+ try {
125
+ addDataType("text/plain")
126
+ } catch (e: IntentFilter.MalformedMimeTypeException) {
127
+ throw RuntimeException("failed", e)
128
+ }
129
+ },
130
+ IntentFilter(ACTION_TECH_DISCOVERED),
131
+ IntentFilter(ACTION_TAG_DISCOVERED)
132
+ )
133
+
134
+ getDefaultAdapter(this.activity).enableForegroundDispatch(
135
+ this.activity,
136
+ pendingIntent,
137
+ intentFilter,
138
+ techListsArray
139
+ )
140
+ }
141
+
142
+ private fun handleWriteTag(intent: Intent) {
143
+ val records = recordsBuffer?.toList<JSONObject>()
144
+ if(records != null) {
145
+ val ndefRecords = mutableListOf<NdefRecord>()
146
+
147
+ try {
148
+ for (record in records) {
149
+ val payload = record.getJSONArray("payload")
150
+ val type: String? = record.getString("type")
151
+
152
+ if (payload.length() == 0 || type == null) {
153
+ notifyListeners(
154
+ "nfcError",
155
+ JSObject().put(
156
+ "error",
157
+ "Invalid record: payload or type is missing."
158
+ )
159
+ )
160
+ return
161
+ }
162
+
163
+ val typeBytes = type.toByteArray(Charsets.UTF_8)
164
+ val payloadBytes = ByteArray(payload.length())
165
+ for(i in 0 until payload.length()) {
166
+ payloadBytes[i] = payload.getInt(i).toByte()
167
+ }
168
+
169
+ ndefRecords.add(
170
+ NdefRecord(
171
+ NdefRecord.TNF_WELL_KNOWN,
172
+ typeBytes,
173
+ ByteArray(0),
174
+ payloadBytes
175
+ )
176
+ )
177
+ }
178
+
179
+ val ndefMessage = NdefMessage(ndefRecords.toTypedArray())
180
+ val tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java)
181
+ var ndef = Ndef.get(tag)
182
+
183
+ if (ndef == null) {
184
+ val formatable = NdefFormatable.get(tag)
185
+ if (formatable != null) {
186
+ try {
187
+ formatable.connect()
188
+ val mimeRecord = NdefRecord.createMime("text/plain", "INIT".toByteArray(
189
+ Charset.forName("US-ASCII")))
190
+ val msg = NdefMessage(mimeRecord)
191
+ formatable.format(msg)
192
+ // Success!
193
+ // Emit event to Capacitor plugin for success
194
+ println("Successfully formatted and wrote NDEF message to tag!")
195
+ } catch (e: IOException) {
196
+ // Error connecting or formatting
197
+ // Emit event to Capacitor plugin for error
198
+ println("Error formatting or writing to NDEF-formatable tag: ${e.message}")
199
+ } catch (e: Exception) { // Catch other potential exceptions during format, like TagLostException
200
+ println("Error during NDEF formatting: ${e.message}")
201
+ } finally {
202
+ try {
203
+ formatable.close()
204
+ } catch (e: IOException) {
205
+ println("Error closing NdefFormatable connection: ${e.message}")
206
+ }
207
+ }
208
+
209
+ ndef = Ndef.get(formatable.tag)
210
+ } else {
211
+ notifyListeners(
212
+ "nfcError",
213
+ JSObject().put(
214
+ "error",
215
+ "Tag does not support NDEF writing."
216
+ )
217
+ )
218
+ return
219
+ }
220
+ }
221
+
222
+ ndef.use { // Use block ensures ndef.close() is called
223
+ ndef.connect()
224
+ if (!ndef.isWritable) {
225
+ notifyListeners(
226
+ "nfcError",
227
+ JSObject().put(
228
+ "error",
229
+ "NFC tag is not writable"
230
+ )
231
+ )
232
+ return
233
+ }
234
+ if (ndef.maxSize < ndefMessage.toByteArray().size) {
235
+ notifyListeners(
236
+ "nfcError",
237
+ JSObject().put(
238
+ "error",
239
+ "Message too large for this NFC Tag (max ${ndef.maxSize} bytes)."
240
+ )
241
+ )
242
+ return
243
+ }
244
+
245
+ ndef.writeNdefMessage(ndefMessage)
246
+ Log.d("NFC", "NDEF message successfully written to tag.")
247
+ }
248
+
249
+ notifyListeners("nfcWriteSuccess", JSObject().put("success", true))
250
+ }
251
+ catch (e: UnsupportedEncodingException) {
252
+ Log.e("NFC", "Encoding error during NDEF record creation: ${e.message}")
253
+ notifyListeners(
254
+ "nfcError",
255
+ JSObject().put(
256
+ "error",
257
+ "Encoding error: ${e.message}"
258
+ )
259
+ )
260
+ }
261
+ catch (e: IOException) {
262
+ Log.e("NFC", "I/O error during NFC write: ${e.message}")
263
+ notifyListeners(
264
+ "nfcError",
265
+ JSObject().put(
266
+ "error",
267
+ "NFC I/O error: ${e.message}"
268
+ )
269
+ )
270
+ }
271
+ catch (e: Exception) {
272
+ Log.e("NFC", "Error writing NDEF message: ${e.message}", e)
273
+ notifyListeners(
274
+ "nfcError",
275
+ JSObject().put(
276
+ "error",
277
+ "Failed to write NDEF message: ${e.message}"
278
+ )
279
+ )
280
+ }
281
+ }
282
+ else {
283
+ notifyListeners("nfcError", JSObject().put("error", "Failed to write NFC tag"))
284
+ }
285
+ }
286
+
287
+ private fun handleReadTag(intent: Intent) {
288
+ val jsResponse = JSObject()
289
+
290
+ val ndefMessages = JSArray()
291
+ val receivedMessages = intent.getParcelableArrayExtra(
292
+ EXTRA_NDEF_MESSAGES,
293
+ NdefMessage::class.java
294
+ )
295
+
296
+ receivedMessages?.also { rawMessages ->
297
+ for (message in rawMessages) {
298
+ val ndefRecords = JSArray()
299
+ for (record in message.records) {
300
+ val rec = JSObject()
301
+ rec.put("type", String(record.type, Charsets.UTF_8))
302
+ rec.put("payload", Base64.getEncoder().encodeToString(record.payload))
303
+ ndefRecords.put(rec)
304
+ }
305
+
306
+ val msg = JSObject()
307
+ msg.put("records", ndefRecords)
308
+ ndefMessages.put(msg)
309
+ }
310
+ }
311
+
312
+ jsResponse.put("messages", ndefMessages)
313
+ this.notifyListeners("nfcTag", jsResponse)
314
+ }
315
+ }