Compare commits

..

No commits in common. "master" and "v2-type-define" have entirely different histories.

77 changed files with 4582 additions and 39279 deletions

3
.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["env"]
}

View File

@ -1,6 +0,0 @@
node_modules/
coverage/
dist/
lib/
example/
index.d.ts

View File

@ -1,6 +0,0 @@
module.exports = {
extends: ['@react-native-community'],
rules: {
'no-bitwise': 0,
},
};

View File

@ -1,20 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.'
days-before-issue-stale: 90
days-before-pr-stale: 90
days-before-issue-close: 15
days-before-pr-close: 15

3
.gitignore vendored
View File

@ -38,6 +38,3 @@ yarn-error.log
# Vim
.ctags
tags
# dotenv
.env

View File

@ -1,6 +0,0 @@
module.exports = {
bracketSpacing: false,
jsxBracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
};

View File

@ -1,17 +0,0 @@
{
"git": {
"commitMessage": "chore: release ${version}",
"tagName": "v${version}"
},
"npm": {
"publish": true
},
"github": {
"release": true
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular"
}
}
}

625
APIv1.md Normal file
View File

@ -0,0 +1,625 @@
## API
This library provide a default export `NfcManager` and 3 named exports `Ndef`, `NfcTech` and `ByteParser`, like this:
```javascript
import NfcManager, {Ndef, NfcTech, ByteParser} from 'react-native-nfc-manager'
```
All methods in `NfcManager` return a `Promise` object and are resolved to different types of data according to individual API.
* `Ndef` is an utility module to encode and decode some well-known NDEF format.
* `ByteParser` is an utility module to encode and decode byte[] arrays (used in Mifare Classic technology).
* `NfcTech` contains predefined constants for specific NFC technologies, which include `NfcA`, `NfcB`, `NfcF`, `NfcV`, `IsoDep`, `MifareClassic` and `MifareUltralight`.
* These constants should be used with `requestTechnology` (Android Only) to obtain a NFC technology handle, and use it to perform technology specific operations.
The API documentation is grouped into 6 parts:
* [NfcManager API](##NfcManager-API)
* [Ndef API](##Ndef-API)
* [NfcTech.Ndef API](##NfcTech.Ndef-[Android-only])
* [Generic NfcTech API](##Generic-NfcTech-API-[Android-only])
* [NfcTech.MifareClassic API](##NfcTech.MifareClassic-API-[Android-only])
* [NfcTech.MifareUltralight API](##NfcTech.MifareUltralight-API-[Android-only])
* [ByteParser API](##ByteParser-API)
## NfcManager API
### start({onSessionClosedIOS})
Init the module. If the device doesn't support NFC, the returning promise will be rejected.
__Arguments__
- `onSessionClosedIOS` - `function` - [iOS only] the callback to invoke when an `NFCNDEFReaderSession` becomes invalidated
__Examples__
```js
NfcManager.start({
onSessionClosedIOS: () => {
console.log('ios session closed');
}
})
.then(result => {
console.log('start OK', result);
})
.catch(error => {
console.warn('device does not support nfc!');
this.setState({supported: false});
})
```
### stop()
Terminates the module. This will remove the onSessionClosedIOS listener that is attached in the `start` function.
### isSupported(tech = '')
Check if NFC (and specified NFC technology) is supported by the hardware.
When you specify a technology, some extra checks are performed to see if the technology is supported or not.
Returnes `Promise` resolved to a boolean value to indicate whether NFC (and specified NFC technology) is supported.
__Arguments__
- `tech` - `string` - optional parameter, use a constant of the NfcTech class, defaults to ''
__Examples__
```js
NfcManager.isSupported(NfcTech.MifareClassic)
.then(() => console.log('Mifare classic is supported'))
.catch(err => console.warn(err))
```
### isEnabled() [Android only]
Check if the NFC is enabled.
Returned `Promise` resolved to a boolean value to indicate whether NFC is enabled.
### goToNfcSetting() [Android only]
Direct the user to NFC setting.
### getLaunchTagEvent() [Android only]
Get the NFC tag object which launches the app.
Returned `Promise` resolved to the NFC tag object launching the app and resolved to null if the app isn't launched by NFC tag.
### registerTagEvent(listener, alertMessage, invalidateAfterFirstRead)
Start to listen to *ANY* NFC tags.
__Arguments__
- `listener` - `function` - the callback when discovering NFC tags
- `alertMessage` - `string` - (iOS) the message to display on iOS when the NFCScanning pops up
- `options` - `object` - Object containing (iOS)invalidateAfterFirstRead, (Android)isReaderModeEnabled, (Android)readerModeFlags. Use `NfcAdapter` flags. **Reader mode can only be used in Android 19 or later**.
**Examples**
```js
NfcManager.registerTagEvent(
tag => {
console.log('Tag Discovered', tag);
},
'Hold your device over the tag',
{
invalidateAfterFirstRead: true,
isReaderModeEnabled: true,
readerModeFlags:
NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
},
);
```
### unregisterTagEvent()
Stop listening to NFC tags.
### requestNdefWrite(bytes, options) [Android only]
Request writing **NdefMessage** (constructed by `bytes` array you passed) into next discovered tag.
Notice you must call `registerTagEvent` first before calling this.
__Arguments__
- `bytes` - `array` - the full NdefMessage, which is an array of number
- `options` - `object` - optional argument used to trigger actions such as `format` or `formatReadOnly`
__Examples__
```js
// write ndef
NfcManager.requestNdefWrite(bytes)
.then(() => console.log('write completed'))
.catch(err => console.warn(err))
// request ndef formating (first argument can be null in this case)
NfcManager.requestNdefWrite(null, {format: true})
.then(() => console.log('format completed'))
.catch(err => console.warn(err))
```
### cancelNdefWrite() [Android only]
Cancel the pending ndef writing operation.
### onStateChanged(listener) [Android only]
Listen to NFC state change (on/off/turning_on/turning_off)
__Arguments__
- `listener` - `function` - the callback when NFC state changed
__Examples__
```js
NfcManager.onStateChanged(
event => {
if (event.state === 'on') {
// do whatever you want
} else if (event.state === 'off') {
// do whatever you want
} else if (event.state === 'turning_on') {
// do whatever you want
} else if (event.state === 'turning_off') {
// do whatever you want
}
}
)
.then(sub => {
this._stateChangedSub = sub;
// remember to call this._stateChangedSub.remove()
// when you don't want to listen to this anymore
})
.catch(err => {
console.warn(err);
})
```
### requestTechnology(tech) [Android only]
Request specific NFC Technology to perform advanced actions.
- Please refer to [Android Advanced NFC Guide](https://stuff.mit.edu/afs/sipb/project/android/docs/guide/topics/connectivity/nfc/advanced-nfc.html) to understand what a `NFC Technology` means.
> This method returns a promise:
> * if resolved, it means you already find and connect to the tag supporting the requested technology, so the technology specific API can be called.
> * if rejected, it means either the request is cancelled or the discovered tag doesn't support the requested technology.
Notice you must call `registerTagEvent` first before calling this.
__Arguments__
- `tech` - `string` - the NFC Technology you want to use
- the available ones are defined in `NfcTech` (please do `import {NfcTech} from 'react-native-nfc-manager`)
__Examples__
> Concrete examples using NFC Technology can be found in `example/AndroidTechTestNdef.js` and `example/AndroidMifareClassic.js`
### cancelTechnologyRequest() [Android only]
Cancel previous NFC Technology request.
### closeTechnology() [Android only]
When all your NFC Technology operations are finished, you should call this API to disconnect from the tag and release resources.
### setNdefPushMessage(bytes) [Android only]
This API triggers [**Android Beam**](https://developer.android.com/guide/topics/connectivity/nfc/nfc#p2p), it can send Ndef (constructed by `bytes` array you passed) to remote device.
Notice you must call `registerTagEvent` first before calling this.
> When you want to cancel the Ndef sending, simply call this API again and pass `null` to it.
__Arguments__
- `bytes` - `array` - the full NdefMessage, which is an array of number
__Examples__
> Please see `examples/App.js` for a concrete example
```js
// register Android Beam
NfcManager.setNdefPushMessage(bytes)
.then(() => console.log('ready to beam'))
.catch(err => console.warn(err))
// cancel Android Beam
NfcManager.setNdefPushMessage(null)
.then(() => console.log('beam cancelled'))
.catch(err => console.warn(err))
```
## Ndef API
This module is integrated from [`ndef-js`](https://github.com/don/ndef-js) to perform Ndef encoding & decoding. Great thanks for their brilliant work!
We mainly remove the dependency to NodeJS `Buffer` and maintain most of the original structure.
### Encode example:
```js
let bytes = Ndef.encodeMessage([
Ndef.textRecord("hello, world"),
Ndef.uriRecord("http://nodejs.org"),
]);
// then you can pass `bytes` into API such as NfcManager.requestNdefWrite()
```
### Decode example:
```js
_onTagDiscovered = tag => {
console.log('Tag Discovered', tag);
this.setState({ tag });
let parsed = null;
if (tag.ndefMessage && tag.ndefMessage.length > 0) {
// ndefMessage is actually an array of NdefRecords,
// and we can iterate through each NdefRecord, decode its payload
// according to its TNF & type
const ndefRecords = tag.ndefMessage;
function decodeNdefRecord(record) {
if (Ndef.isType(record, Ndef.TNF_WELL_KNOWN, Ndef.RTD_TEXT)) {
return ['text', Ndef.text.decodePayload(record.payload)];
} else if (Ndef.isType(record, Ndef.TNF_WELL_KNOWN, Ndef.RTD_URI)) {
return ['uri', Ndef.uri.decodePayload(record.payload)];
}
return ['unknown', '---']
}
parsed = ndefRecords.map(decodeNdefRecord);
}
this.setState({parsed});
}
```
## NfcTech.Ndef API [Android only]
To use the NfcTech.Ndef API, you first need to request the `NfcTech.Ndef` technology (see `requestTechnology`). Once you have the tech request, you can use the following methods:
### writeNdefMessage(bytes) [Android only]
Request writing **NdefMessage** (constructed by `bytes` array you passed) into the tag.
> This method returns a promise:
> * if resolved, it means you successfully write NdefMessage to the tag.
> * if rejected, it means either the request is cancelled, the write operation fail or the operation is not supported in current tech handle.
__Arguments__
- `bytes` - `array` - the full NdefMessage, which is an array of bytes
### getNdefMessage() [Android only]
Read current NdefMessage inside the tag.
> This method returns a promise:
> * if resolved, the resolved value will be a tag object, which should contain a `ndefMessage` property.
> * if rejected, it means either the request is cancelled, the read operation fail or the operation is not supported in current tech handle.
### getCachedNdefMessage() [Android only]
Read cached NdefMessage inside the tag, no further IO operation occurs.
> This method returns a promise:
> * if resolved, the resolved value will be a tag object, which should contain a `ndefMessage` property.
> * if rejected, it means either the request is cancelled or the operation is not supported in current tech handle.
### makeReadOnly() [Android only]
Make the tag become read-only.
> This method returns a promise:
> * if resolved, the operation success and tag should become read-only.
> * if rejected, it means either the request is cancelled, the operation fail or the operation is not supported in current tech handle.
## Generic NfcTech API [Android only]
To use the these API, you first need to request specific NFC technology (see `requestTechnology`). Once you have the tech request, you can use the following methods:
### transceive(bytes) [Android only]
Send raw data to a tag and receive the response. This API is compatible with following NfcTech: NfcA, NfcB, NfcF, NfcV, IsoDep and MifareUltralight.
> This method returns a promise:
> * if resolved, it means you successfully send data to the tag, and the resolved value will the response, which is also an `array of bytes`.
> * if rejected, it means either the request is cancelled, the operation fail or the operation is not supported in current tech handle.
__Arguments__
- `bytes` - `array` - the raw data you want to send, which is an array of bytes
### getMaxTransceiveLength() [Android only]
Return the maximum number of bytes that can be sent. This API is compatible with following NfcTech: NfcA, NfcB, NfcF, NfcV, IsoDep and MifareUltralight.
> This method returns a promise:
> * if resolved, the resolved value will be the maximum number of bytes that can be sent to transceive.
> * if rejected, it means either the request is cancelled, the operation fail or the operation is not supported in current tech handle.
### setTimeout(timeout) [Android only]
Set the transceive timeout in milliseconds. This API is compatible with following NfcTech: NfcA, NfcF, IsoDep, MifareClassic and MifareUltralight.
> This method returns a promise:
> * if resolved, it means the setTimeout operation is success.
> * if rejected, it means either the request is cancelled, the operation fail or the operation is not supported in current tech handle.
__Arguments__
- `timeout` - `int` - the transceive timeout in milliseconds
## NfcTech.MifareClassic API [Android only]
This module enables you to read encrypted [Mifare Classic](https://en.wikipedia.org/wiki/MIFARE#MIFARE_Classic_family) cards (as long as you have the authentication keys). A concrete example can be found in `example/AndroidMifareClassic.js`
To find more information about the low level APIs of Mifare Classic on Android checkout this excellent blog post: [MiFare Classic Detection on Android](http://mifareclassicdetectiononandroid.blogspot.com/2011/04/reading-mifare-classic-1k-from-android.html)
At the time of writing, iOS 12 still doesn't support any Mifare cards, or any NFC technology that doesn't use the NDEF standards.
To use the Mifare Classic API, you first need to request the `NfcTech.MifareClassic` technology (see `requestTechnology`). Once you have the tech request, you can use the following methods to interact with the Mifare Classic cards:
### mifareClassicAuthenticateA(sector, key) and mifareClassicAuthenticateB(sector, key) [Android only]
Authenticate to the Mifare Classic card using key A or key B.
> This method returns a promise:
> * if resolved, it means you successfully authenticated to the Mifare Classic card, and a read request can be called.
> * if rejected, it means either the request is cancelled, the discovered card doesn't support the requested technology or the authentication simply failed. The returned error should give you some insights about what went wrong.
Notice you must have successfully requested the Mifare Classic technology with the `requestTechnology` call before using this method.
__Arguments__
- `sector` - `number` - the Mifare Classic sector to authenticate to (e.g. 0 - 15 for Mifare Classic 1K cards)
- `key` - `byte[]` - an array of bytes (numbers) that contains the key
__Examples__
> A concrete example using Mifare Classic can be found in `example/AndroidMifareClassic.js`
```js
NfcManager.mifareClassicAuthenticateA(0, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).then(() => {
/* ...do your stuff here... */
});
```
### mifareClassicGetBlockCountInSector(sector) [Android only]
Returns a promise with the number of blocks in a given sector.
Note: because the block count and sector count can vary from card to card, the card must be successfully detected by the `NfcManager.requestTechnology(NfcTech.MifareClassic)` callback first before calling this method. Does not cause any RF activity and does not block.
__Arguments__
- `sector` - `number` - the Mifare Classic sector to get the number of blocks from (the number of blocks might depend on the detected card type)
__Return value__
- `blocks` - `number` - the number of blocks
### mifareClassicGetSectorCount() [Android only]
Returns a promise with the number of sectors on the card.
Note: because the sector count can vary from card to card, the card must be successfully detected by the `NfcManager.requestTechnology(NfcTech.MifareClassic)` callback first before calling this method. Does not cause any RF activity and does not block.
__Return value__
- `sectors` - `number` - the number of sectors
### mifareClassicSectorToBlock(sector) [Android only]
Returns a promise with the blockIndex for a given sector.
Note: because the block count and sector count can vary from card to card, the card must be successfully detected by the `NfcManager.requestTechnology(NfcTech.MifareClassic)` callback first before calling this method. Does not cause any RF activity and does not block.
__Arguments__
- `sector` - `number` - the Mifare Classic sector to get the blockIndex from (the number of blocks might depend on the detected card type)
__Return value__
- `blockIndex` - `number` - the block index of the sector
### mifareClassicReadBlock(block) and mifareClassicReadSector(sector) [Android only]
Reads a block/sector from a Mifare Classic card. You must be authenticated according to the card's configuration, or this promise will be rejected with `mifareClassicReadBlock fail: java.io.IOException: Transceive failed`.
The difference between readBlock and readSector is that readBlock will only read one block, while readSector will first get the blockIndex of the specified sector, and will read as many blocks as there are in the specified sector. It's generally speaking faster than calling `mifareClassicGetBlockCountInSector` yourself and doing consecutive reads yourself.
> This method returns a promise:
> * if resolved, it returns the data (array of bytes (numbers)) from the specified block/sector.
> * if rejected, it means either the request is cancelled, the discovered card doesn't support the requested technology, the authentication failed or something else went wrong. The returned error should give you some insights about what went wrong.
Notice you must be successfully authenticated with the `mifareClassicAuthenticateA` or `mifareClassicAuthenticateB` call before using this method.
__Arguments__
- `block/sector` - `number` - the Mifare Classic block/sector to read (the number of blocks/sector might depend on the detected card type)
__Return value__
- `data` - `byte[]` - an array of bytes (numbers)
For convenience, a class `ByteParser` is included in the NfcManager exports. This class contains 2 methods `byteToHexString` and `byteToString` who can be used to get the raw data into a hex string or a string, depending on what data is stored on the card.
__Examples__
> A concrete example using Mifare Classic can be found in `example/AndroidMifareClassic.js`
```js
NfcManager.mifareClassicReadBlock(0).then((message) => {
const str = ByteParser.byteToString(message);
/* ...do your stuff here... */
});
```
#### Read authenticated example:
The following is some wrapper code that uses the `async/await` syntax.
```js
const readAuthenticatedA = async (sector, code) => {
return new Promise((resolve, reject) => {
NfcManager.mifareClassicAuthenticateA(sector, code)
.then(() => {
console.log(`mifareClassicAuthenticateA(${sector}) completed`);
NfcManager.mifareClassicReadSector(sector)
.then(data => {
console.log(`mifareClassicReadSector(${sector}) completed`);
resolve(data);
})
.catch(err => {
console.log(`mifareClassicReadSector(${sector}) failed:`, err);
reject(err);
});
})
.catch(err => {
console.log(`mifareClassicAuthenticateA(${sector}) failed:`, err);
reject(err);
});
});
};
const sector0Data = await readAuthenticatedA(0, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
```
### mifareClassicWriteBlock(block, data) [Android only]
Writes a block to a Mifare Classic card. You must be authenticated according to the card's configuration, or this promise will be rejected with `mifareClassicWriteBlock fail: java.io.IOException: Transceive failed`.
To write a full sector, you must first get the blockIndex of the specified sector by calling `mifareClassicSectorToBlock` and write all the blocks in the sector (`mifareClassicGetBlockCountInSector` times).
> This method returns a promise:
> * if resolved, it returns true.
> * if rejected, it means either the request is cancelled, the discovered card doesn't support the requested technology, the authentication failed or something else went wrong. The returned error should give you some insights about what went wrong.
Notice you must be successfully authenticated with the `mifareClassicAuthenticateA` or `mifareClassicAuthenticateB` call before using this method.
__Arguments__
- `block` - `number` - the Mifare Classic block to write (the number of blocks/sector might depend on the detected card type)
- `data` - `byte[]` - an array of bytes (numbers) with length of `NfcManager.MIFARE_BLOCK_SIZE`
__Examples__
> A concrete example using Mifare Classic can be found in `example/AndroidMifareClassic.js`
```js
NfcManager.mifareClassicWriteBlock(0, [ 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 0, 0, 0 ]).then(() => {
console.log('Wrote "Hello, world!" to the card');
});
```
## NfcTech.MifareUltralight API [Android only]
To use the NfcTech.MifareUltralight API, you first need to request the `NfcTech.MifareUltralight` technology (see `requestTechnology`). Once you have the tech request, you can use the following methods:
### mifareUltralightReadPages(pageOffset) [Android only]
Read 4 pages (16 bytes).
> This method returns a promise:
> * if resolved, the resolved value will be the 16 bytes page data.
> * if rejected, it means either the request is cancelled, the write operation fail or the operation is not supported in current tech handle.
__Arguments__
- `pageOffset` - `number` - index of first page to read, starting from 0
### mifareUltralightWritePage(pageOffset, bytes) [Android only]
Write 1 pages (4 bytes).
> This method returns a promise:
> * if resolved, it means the write operation is completed.
> * if rejected, it means either the request is cancelled, the write operation fail or the operation is not supported in current tech handle.
__Arguments__
- `pageOffset` - `number` - index of first page to read, starting from 0
- `bytes` - `array` - 4 bytes to write
## ByteParser API
Simple utility for working with byte[] arrays like in Mifare Classic cards)
### byteToHexString(bytes)
Converts a byte array `byte[]` to a hex string.
__Arguments__
- `bytes` - `byte[]` - the result of a mifareClassicReadBlock call.
__Examples__
```js
let hexString = ByteParser.byteToHexString(result);
console.log('hex string: ' + hexString);
```
### byteToString(bytes)
Converts a byte array `byte[]` to a string (if the data represents an ASCII string).
__Arguments__
- `bytes` - `byte[]` - the result of a mifareClassicReadBlock call.
__Examples__
```js
let str = ByteParser.byteToString(result);
console.log('string: ' + str);
```
## NFC Hardware requirement on Android
By default react-native-nfc-manager is set to not require NFC hardware on Android. This setting will overwrite what ever you put in your main AndroidManifest.xml file during `react-native link` phase.
If you want to change this behavior to only have your app support NFC devices you have to override you app manifest manually.
Current setting is:
```<uses-feature android:name="android.hardware.nfc" android:required="false" />```
If you want to only have your app support NFC devices then you have to change required to true.
## Version history (from v0.1.0)
v1.2.0
- support Android `readerMode` feature (limit the NFC controller to reader mode only)
v1.1.0
- support Mifare Ultralight
v1.0.0
- support Mifare Classic write operation (thanks to @poison)
- refactor Mifare Classic read operation to distinguish from `sector` and `block` (thanks for @poison)
- basic support for NfcF, NfcV, IsoDep with `transceive` method
v0.7.0
- basic support for Mifare Classic (thanks to @poison)
- see `example/AndroidMifareClassic.js` for a full example.
v0.6.0
- integrate [`ndef-js`](https://github.com/don/ndef-js) to perform Ndef encoding & decoding. Great thanks for their brilliant work!
- as a result of previous integration, users can now easily handle the NdefMessage consists of multi NdefRecords.
- see `example/MultiNdefRecord.js` for a full example to write or read such an NdefMessage.
v0.5.4
- (android) support `getTag` for all NFC technologies
- (android) update **compileSdkVersion** and **buildToolsVersion** to 26
- (ios) bug fix: clear event subscription when reader session closed
v0.5.2
- support **Android Beam** via `setNdefPushMessage` API [Android only]
- please see `examples/App.js` for a concrete example
- new methods for `NfcTech.Ndef` [Android only]
- supported methods: `makeReadOnly`
- bug fix: guard against getCurrentActivity() returns null
v0.5.1
- support `NfcTech.NfcA` [Android only]:
- representing `android.nfc.tech.NfcA` [link](https://developer.android.com/reference/android/nfc/tech/NfcA)
- supported methods: `transceive`
v0.5.0
- support `NfcTech.Ndef` [Android only]:
- representing `android.nfc.tech.Ndef` [link](https://developer.android.com/reference/android/nfc/tech/Ndef)
- supported methods: `writeNdefMessage`, `getNdefMessage`, `getCachedNdefMessage`
- please see `examples/AndroidTechTestNdef.js` for a concrete example
v0.4.0
- support `NdefParser.parseText` for RTD_TEXT parsing
v0.3.2
- change `isSupported` API to utilize `NFCNDEFReaderSession.readingAvailable` [iOS]
- change minSdkVersion to 16 [Android]
v0.3.0
- add `onStateChanged` [Android]
- add options for `requestNdefWrite` to allow NDEF formating [Android]
v0.2.0
- add `requestNdefWrite` and `cancelNdefWrite` [Android]
v0.1.0
- add `isNfcSupported`
## Deprecated API
<details>
<summary>
NdefParser
</summary>
## NdefParser API (deprecated, please use `Ndef` instead)
### parseUri(ndef)
Try to parse RTD_URI from a NdefMessage, return an object with an `uri` property.
__Arguments__
- `ndef` - `object` - this object should be obtained from nfc tag object with this form: `tag.ndefMessage[0]`. (NFC tag object can be obtained by `getLaunchTagEvent` or `registerTagEvent`)
__Examples__
```js
let {uri} = NdefParser.parseUri(sampleTag.ndefMessage[0]);
console.log('parseUri: ' + uri);
```
### parseText(ndef)
Try to parse RTD_TEXT from a NdefMessage, return parsed string or null if the operation fail. Currently only support utf8.
__Arguments__
- `ndef` - `object` - this object should be obtained from nfc tag object with this form: `tag.ndefMessage[0]`. (NFC tag object can be obtained by `getLaunchTagEvent` or `registerTagEvent`)
__Examples__
```js
let text = NdefParser.parseText(sampleTag.ndefMessage[0]);
console.log('parsedText: ' + text);
```
</details>

159
APIv2.md Normal file
View File

@ -0,0 +1,159 @@
## API v2
In v2, we seperate the API to 2 different usage:
* **basic usage**: only reading NDEF tags
* **advanced usage**: use different NFC technologies to perform specific tasks
### Basic usage
For **basic** case, the main API you need will be:
* `registerTagEvent`, to enable NFC tag reading functionality
* `setEventListener`, to listen to the discovered tags and the NdefMessage within them
* `unregisterTagEvent`
> For V1 users, please notice we no longer accept passing callback into `registerTagEvent`, so you will need to use `setEventListener` for `NfcEvents.DiscoverTag` explicitly
Here is an example, work for both iOS & Android:
```javascript
import React from 'react'
import {
View, Text, TouchableOpacity
} from 'react-native'
import NfcManager, {NfcEvents} from 'react-native-nfc-manager';
class AppV2 extends React.Component {
componentDidMount() {
NfcManager.start();
NfcManager.setEventListener(NfcEvents.DiscoverTag, tag => {
console.warn('tag', tag);
NfcManager.setAlertMessageIOS('I got your tag!');
NfcManager.unregisterTagEvent().catch(() => 0);
});
}
componentWillUnmount() {
NfcManager.setEventListener(NfcEvents.DiscoverTag, null);
NfcManager.unregisterTagEvent().catch(() => 0);
}
render() {
return (
<View style={{padding: 20}}>
<Text>NFC Demo</Text>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._test}
>
<Text>Test</Text>
</TouchableOpacity>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._cancel}
>
<Text>Cancel Test</Text>
</TouchableOpacity>
</View>
)
}
_cancel = () => {
NfcManager.unregisterTagEvent().catch(() => 0);
}
_test = async () => {
try {
await NfcManager.registerTagEvent();
} catch (ex) {
console.warn('ex', ex);
NfcManager.unregisterTagEvent().catch(() => 0);
}
}
}
```
## Advanced usage
In order to use specific NFC technology to perform specific tasks, two steps are required:
* `requestTechnology`, pass your desired NFC tech to this API, and it will return a promise which resolves when the desired tag is found
* After the tag with desired tech is found, use tech speicific API to perform your tasks
* once you're done, call `cancelTechnologyRequest` to ask native platform to stop NFC tag scanning.
> For V1 users, please notice that you don't need to manually call `registerTagEvent` like before, we will do this for you as well as the clean up with `unregisterTagEvent`
A most common example is to write NdefMessage into a tag:
```javascript
import React from 'react';
import {
View,
Text,
TouchableOpacity,
} from 'react-native';
import NfcManager, {Ndef, NfcEvents} from 'react-native-nfc-manager';
function buildUrlPayload(valueToWrite) {
return Ndef.encodeMessage([
Ndef.uriRecord(valueToWrite),
]);
}
class AppV2Ndef extends React.Component {
componentDidMount() {
NfcManager.start();
}
componentWillUnmount() {
this._cleanUp();
}
render() {
return (
<View style={{padding: 20}}>
<Text>NFC Demo</Text>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._testNdef}
>
<Text>Test Ndef</Text>
</TouchableOpacity>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._cleanUp}
>
<Text>Cancel Test</Text>
</TouchableOpacity>
</View>
)
}
_cleanUp = () => {
NfcManager.cancelTechnologyRequest().catch(() => 0);
}
_testNdef = async () => {
try {
let resp = await NfcManager.requestTechnology(NfcTech.Ndef, {
alertMessage: 'Ready to write some NFC tags!'
});
console.warn(resp);
let ndef = await NfcManager.getNdefMessage();
console.warn(ndef);
let bytes = buildUrlPayload('https://www.revteltech.com');
await NfcManager.writeNdefMessage(bytes);
console.warn('successfully write ndef');
await NfcManager.setAlertMessageIOS('I got your tag!');
this._cleanUp();
} catch (ex) {
console.warn('ex', ex);
this._cleanUp();
}
}
}
export default AppV2Ndef;
```
More examples can be found in `example` directory.

24
ByteParser.js Normal file
View File

@ -0,0 +1,24 @@
function byteToHexString(bytes) {
if (!Array.isArray(bytes) || !bytes.length) return '';
let result = '';
for (let i = 0; i < bytes.length; i++) {
result += `0${bytes[i].toString(16)}`.slice(-2);
}
return result;
}
function byteToString(bytes) {
if (!Array.isArray(bytes) || !bytes.length) return '';
let result = '';
for (let i = 0; i < bytes.length; i++) {
result += String.fromCharCode(bytes[i]);
}
return result;
}
export default {
byteToHexString,
byteToString,
}

19
ByteParser.test.js Normal file
View File

@ -0,0 +1,19 @@
import ByteParser from './ByteParser';
test('parse byteToHexString', () => {
let payload = [104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 119, 104, 105, 116, 101, 100, 111, 103, 103, 49, 51, 47, 114, 101, 97, 99, 116, 45, 110, 97, 116, 105, 118, 101, 45, 110, 102, 99, 45, 109, 97, 110, 97, 103, 101, 114, 35, 114, 101, 97, 100, 109, 101];
expect(ByteParser.byteToHexString(payload)).toBe("68747470733a2f2f6769746875622e636f6d2f7768697465646f676731332f72656163742d6e61746976652d6e66632d6d616e6167657223726561646d65");
});
test('parse byteToHexString should return empty', () => {
expect(ByteParser.byteToHexString({something: "wrong"})).toBe("");
});
test('parse byteToString', () => {
let payload = [104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 119, 104, 105, 116, 101, 100, 111, 103, 103, 49, 51, 47, 114, 101, 97, 99, 116, 45, 110, 97, 116, 105, 118, 101, 45, 110, 102, 99, 45, 109, 97, 110, 97, 103, 101, 114, 35, 114, 101, 97, 100, 109, 101];
expect(ByteParser.byteToString(payload)).toBe("https://github.com/whitedogg13/react-native-nfc-manager#readme");
});
test('parse byteToString should return empty', () => {
expect(ByteParser.byteToString({something: "wrong"})).toBe("");
});

View File

@ -1,98 +0,0 @@
## [3.0.2](https://github.com/whitedogg13/react-native-nfc-manager/compare/v3.0.1...v3.0.2) (2021-02-22)
### Features
* **android:** allow requestTechnology resolves to tags even when tech ([8b7f862](https://github.com/whitedogg13/react-native-nfc-manager/commit/8b7f8629a8e46304f2a68d62f01e745047fe01dc))
## [3.0.1](https://github.com/whitedogg13/react-native-nfc-manager/compare/v3.0.0...v3.0.1) (2021-02-20)
### Bug Fixes
* [#371](https://github.com/whitedogg13/react-native-nfc-manager/issues/371) missing MIFARE_BLOCK_SIZE for mifareClassicHandlerAndroid ([b136419](https://github.com/whitedogg13/react-native-nfc-manager/commit/b1364197c80ab16542bbb823483a2079a896e169))
# [3.0.0](https://github.com/whitedogg13/react-native-nfc-manager/compare/v3.0.0-0...v3.0.0) (2021-02-18)
### Features
* expose RTD_URI_PROTOCOLS ([26516a7](https://github.com/whitedogg13/react-native-nfc-manager/commit/26516a7ce8309ade21464df13a8e5c799c0e417c))
# [3.0.0-0](https://github.com/whitedogg13/react-native-nfc-manager/compare/v2.0.0-beta.1...v3.0.0-0) (2021-02-10)
### Bug Fixes
* disable 18092 polling (cause iOS freeze) ([64115f8](https://github.com/whitedogg13/react-native-nfc-manager/commit/64115f80f8cd93ed5ef3b01987d6c628a5a9127b))
* **ios:** add polling for iso18092 ([b7984a5](https://github.com/whitedogg13/react-native-nfc-manager/commit/b7984a59ea27076d7fd34a2614592f468c80f5e1))
* add callback type null possible ([1371533](https://github.com/whitedogg13/react-native-nfc-manager/commit/13715334ea908b89654b42eba4ec902bcc6f4229))
* cancelTechnology might cause exception if there's a connected tag ([af2f1f9](https://github.com/whitedogg13/react-native-nfc-manager/commit/af2f1f92bbd7bfcf8c604db48b790289c3ca4946))
* cancelTechnology might cause exception if there's a connected tag ([dcb26b8](https://github.com/whitedogg13/react-native-nfc-manager/commit/dcb26b85a955ce17ae633e00f6f189a895e8adcb))
* getNdefMessage should use the same structure for platforms ([73859fb](https://github.com/whitedogg13/react-native-nfc-manager/commit/73859fbac8939277f1e7fc2ad13ececee8c9d4c7))
* import NfcManager from new location ([c95cc76](https://github.com/whitedogg13/react-native-nfc-manager/commit/c95cc76a0a3b52c751c1a0f00895cf1b34b66b28))
* ios isSupported ([adcc966](https://github.com/whitedogg13/react-native-nfc-manager/commit/adcc966c8ed4a565c2800fabc5e5a0cad07ea376))
* promise won't be rejected when calling cancelTechnologyRequest in ([64c9cbd](https://github.com/whitedogg13/react-native-nfc-manager/commit/64c9cbd05e34e975fa4744727859efd08ea610bf))
* reject tech request promise when native iOS cancel button is pressed ([7915b98](https://github.com/whitedogg13/react-native-nfc-manager/commit/7915b98132cf5c6f86eb96dfe242b67bef32e8a7))
* remove getNdefMessage's parameter declaration ([6fc257e](https://github.com/whitedogg13/react-native-nfc-manager/commit/6fc257e61e1da5600a7aac44d9a8a97a7ecc988c))
### Features
* extract more NfcTech handlers ([0032408](https://github.com/whitedogg13/react-native-nfc-manager/commit/0032408af21cafa3f5f92c16083f914325450524))
* **android:** getNdefStatus ([4cc68ce](https://github.com/whitedogg13/react-native-nfc-manager/commit/4cc68ce233d81c03760b791fc2f568fe4b8415c4))
* **ios:** enhance didDetectTags and add queryNDEFStatus / makeReadOnly ([32dd3be](https://github.com/whitedogg13/react-native-nfc-manager/commit/32dd3be2968abbe095ae42fd12793a9a8c0725c6))
* invalidateSessionWithErrorIOS ([c631110](https://github.com/whitedogg13/react-native-nfc-manager/commit/c631110b6454c08821607014a28b8dffb20fe8b0))
* ios 13 ndef read and write ([31a3ae6](https://github.com/whitedogg13/react-native-nfc-manager/commit/31a3ae6e141e41f3f36a102d5c2bb5ce04ba78a5))
* requestTechnology support multi-techs ([65e8f02](https://github.com/whitedogg13/react-native-nfc-manager/commit/65e8f02b83820cb72e65c8443a607b6dca2c8fae))
# [2.0.0-beta.1](https://github.com/whitedogg13/react-native-nfc-manager/compare/0.0.9...v2.0.0-beta.1) (2019-07-13)
### Bug Fixes
* call the correct method `mifareUltralightReadPages` (notice the tailing s) ([60feee8](https://github.com/whitedogg13/react-native-nfc-manager/commit/60feee88c2d20bc18af23b3abb6d8cb22fe17e16))
* ios crash on registerTagEvent ([e654d93](https://github.com/whitedogg13/react-native-nfc-manager/commit/e654d9359751327284d20b3bee1139c78657dcb4))
* MifareClassic should also support `transceive` ([dd4a902](https://github.com/whitedogg13/react-native-nfc-manager/commit/dd4a902257a402e5d6d3eae8bd43f9d37c03928b))
* use non-cached ndef for getNdefMessage API ([49f8541](https://github.com/whitedogg13/react-native-nfc-manager/commit/49f85415a5bc6d0218992dc6ba2b9303cee8e3ac))
### Features
* mifareultralight support ([4a53a13](https://github.com/whitedogg13/react-native-nfc-manager/commit/4a53a135c594ef340927ae053dfc142ebfd0b9b6))
* support `getMaxTransceiveLength` and `setTimeout` ([e759c39](https://github.com/whitedogg13/react-native-nfc-manager/commit/e759c399008e9060ae6f5fe23d7b31a61b1f8037))
## [0.0.9](https://github.com/whitedogg13/react-native-nfc-manager/compare/0.0.8...0.0.9) (2018-01-02)
## [0.0.8](https://github.com/whitedogg13/react-native-nfc-manager/compare/0.0.7...0.0.8) (2017-12-09)
## [0.0.7](https://github.com/whitedogg13/react-native-nfc-manager/compare/0.0.6...0.0.7) (2017-12-04)
## [0.0.6](https://github.com/whitedogg13/react-native-nfc-manager/compare/0.0.3...0.0.6) (2017-11-17)
## [0.0.3](https://github.com/whitedogg13/react-native-nfc-manager/compare/0.0.2...0.0.3) (2017-09-27)
## 0.0.2 (2017-08-01)

28
FAQ.md
View File

@ -1,28 +0,0 @@
# FAQ
## [iOS] cannot write NDEF into NFC tags
iOS won't allow writing NDEF into un-formatted tags, so you should first format your tags. For type 2 tags (most NTAG2xx), you can use an app like NFC Tools or NXP Tag Writer to do perform formatting.
> It's possible to implement the NDEF formatting ourself through this library, here's an [example](https://github.com/revtel/react-native-nfc-rewriter/tree/naive-ndef-format). However, it's actually an naive approach, since we don't handle the case if the tag is already formatted and we provide no extra lock / memory information before NDEF TLV, so use this code snippet with cautions.
## [iOS] cannot read NDEF from NFC tags
The same as above, please check if the tag is properly formatted, and contain at least 1 NdefMessage
* This NdefMessage can contain only one NdefRecord as [TNF_EMPTY](https://developer.android.com/reference/android/nfc/NdefRecord#TNF_EMPTY)
## [iOS] cannot read / write Mifare Classic
Indeed, currently MifareClassic isn't supported by Core NFC in our tests. It is also not listed in Core NFC's [NFCMiFareFamily](https://developer.apple.com/documentation/corenfc/nfcmifarefamily?language=objc)
## [Android] My NFC tag cannot launch my app
Note on getLaunchTagEvent: keep in mind that you can only create intent-filters for the very first NDEF record on an NFC tag! If your intent-filter doesn't match the FIRST record your app will launch but it won't get the tag data. Check out for details:
https://stackoverflow.com/questions/25504418/get-nfc-tag-with-ndef-android-application-record-aar/25510642
Also you should add
```xml
android:launchMode="singleTask"
```
to your manifest to prevent launching your app as another task when it is already running.

215
LICENSE
View File

@ -1,21 +1,202 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) Richie Hsieh and Revteltech.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014-2016 Don Coleman
Copyright 2016 Innove
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

13
NativeNfcManager.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
import {
NativeModules,
NativeEventEmitter,
} from 'react-native'
const NativeNfcManager = NativeModules.NfcManager;
const NfcManagerEmitter = new NativeEventEmitter(NativeNfcManager);
export {
NativeNfcManager,
NfcManagerEmitter,
}

38
NdefParser.js Normal file
View File

@ -0,0 +1,38 @@
const textHelper = require("./ndef-lib/ndef-text");
const uriHelper = require("./ndef-lib/ndef-uri");
const arrayEqual = (a, b) => a && b && a.length === b.length && a.every((v, i) => v === b[i]);
function parseText(record) {
const RTD_TEXT_TYPE = [0x54];
if (record && record.tnf === 1) {
if (record.type && arrayEqual(RTD_TEXT_TYPE, record.type)) {
try { // only handle utf8 for now
return textHelper.decodePayload(record.payload);
} catch (ex) {
return null;
}
}
}
return null;
}
function parseUri(record) {
const RTD_URI_TYPE = [0x55];
if (record && record.tnf === 1) {
if (record.type && arrayEqual(RTD_URI_TYPE, record.type)) {
try {
return {
uri: uriHelper.decodePayload(record.payload)
}
} catch (err) {
return null;
}
}
}
return null;
}
export default {
parseUri,
parseText,
}

35
NdefParser.test.js Normal file
View File

@ -0,0 +1,35 @@
import NdefParser from './NdefParser';
test('parse RTD_TEXT', () => {
let RTD_TEXT_TYPE = [0x54];
let record = {
tnf: 1,
type: RTD_TEXT_TYPE,
payload: [
0x02, 0x65, 0x6e, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
]
};
let result = NdefParser.parseText(record);
let answer = "hello, world!";
expect(result).toBe(answer);
});
test('parse RTD_URI', () => {
let RTD_URI_TYPE = [0x55];
let record = {
tnf: 1,
type: RTD_URI_TYPE,
payload: [
0x01, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x63, 0x6f, 0x6d,
]
};
let {uri: result} = NdefParser.parseUri(record);
let answer = "http://www.google.com";
expect(result).toBe(answer);
});

861
NfcManager.js Normal file
View File

@ -0,0 +1,861 @@
'use strict';
import {
Platform
} from 'react-native'
import ByteParser from './ByteParser'
import NdefParser from './NdefParser'
import Ndef from './ndef-lib'
import {NativeNfcManager, NfcManagerEmitter} from './NativeNfcManager'
const DEFAULT_REGISTER_TAG_EVENT_OPTIONS = {
alertMessage: 'Please tap NFC tags',
invalidateAfterFirstRead: false,
isReaderModeEnabled: false,
readerModeFlags: 0,
};
const NfcEvents = {
DiscoverTag: 'NfcManagerDiscoverTag',
SessionClosed: 'NfcManagerSessionClosed',
StateChanged: 'NfcManagerStateChanged',
}
const NfcTech = {
Ndef: 'Ndef',
NfcA: 'NfcA',
NfcB: 'NfcB',
NfcF: 'NfcF',
NfcV: 'NfcV',
IsoDep: 'IsoDep',
MifareClassic: 'MifareClassic',
MifareUltralight: 'MifareUltralight',
MifareIOS: 'mifare',
}
const NfcAdapter = {
FLAG_READER_NFC_A: 0x1,
FLAG_READER_NFC_B: 0x2,
FLAG_READER_NFC_F: 0x4,
FLAG_READER_NFC_V: 0x8,
FLAG_READER_NFC_BARCODE: 0x10,
FLAG_READER_SKIP_NDEF_CHECK: 0x80,
FLAG_READER_NO_PLATFORM_SOUNDS: 0x100,
};
class NfcManager {
constructor() {
this.cleanUpTagRegistration = false;
this._subscribeNativeEvents();
// legacy stuff
this._clientTagDiscoveryListener = null;
this._clientSessionClosedListener = null;
this._subscription = null;
}
// -------------------------------------
// public
// -------------------------------------
setEventListener = (name, callback) => {
const allNfcEvents = Object.keys(NfcEvents).map(k => NfcEvents[k]);
if (allNfcEvents.indexOf(name) === -1) {
throw new Error('no such event');
}
this._clientListeners[name] = callback;
}
get MIFARE_BLOCK_SIZE() { return NativeNfcManager.MIFARE_BLOCK_SIZE };
get MIFARE_ULTRALIGHT_PAGE_SIZE() { return NativeNfcManager.MIFARE_ULTRALIGHT_PAGE_SIZE };
get MIFARE_ULTRALIGHT_TYPE() { return NativeNfcManager.MIFARE_ULTRALIGHT_TYPE };
get MIFARE_ULTRALIGHT_TYPE_C() { return NativeNfcManager.MIFARE_ULTRALIGHT_TYPE_C };
get MIFARE_ULTRALIGHT_TYPE_UNKNOWN() { return NativeNfcManager.MIFARE_ULTRALIGHT_TYPE_UNKNOWN };
start() {
return new Promise((resolve, reject) => {
NativeNfcManager.start((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
})
}
isSupported(tech = ''){
return new Promise((resolve, reject) => {
NativeNfcManager.isSupported(tech, (err,result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
registerTagEvent(options = {}) {
const optionsWithDefault = {
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
...options,
};
return new Promise((resolve, reject) => {
NativeNfcManager.registerTagEvent(optionsWithDefault, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
unregisterTagEvent() {
return new Promise((resolve, reject) => {
NativeNfcManager.unregisterTagEvent((err, result) => {
if (err) {
reject(err);
} else {
resolve(result)
}
})
})
}
// -------------------------------------
// private
// -------------------------------------
_subscribeNativeEvents = () => {
this._subscriptions = {}
this._clientListeners = {};
this._subscriptions[NfcEvents.DiscoverTag] = NfcManagerEmitter.addListener(
NfcEvents.DiscoverTag, this._onDiscoverTag
);
if (Platform.OS === 'ios') {
this._subscriptions[NfcEvents.SessionClosed] = NfcManagerEmitter.addListener(
NfcEvents.SessionClosed, this._onSessionClosedIOS
);
}
if (Platform.OS === 'android') {
this._subscriptions[NfcEvents.StateChanged] = NfcManagerEmitter.addListener(
NfcEvents.StateChanged, this._onStateChangedAndroid
);
}
}
_onDiscoverTag = tag => {
const callback = this._clientListeners[NfcEvents.DiscoverTag];
if (callback) {
callback(tag);
}
}
_onSessionClosedIOS = () => {
const callback = this._clientListeners[NfcEvents.SessionClosed];
if (callback) {
callback();
}
}
_onStateChangedAndroid = state => {
const callback = this._clientListeners[NfcEvents.StateChanged];
if (callback) {
callback(state);
}
}
_requestTechnology(tech) {
return new Promise((resolve, reject) => {
NativeNfcManager.requestTechnology(tech, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
_cancelTechnologyRequest() {
return new Promise((resolve, reject) => {
NativeNfcManager.cancelTechnologyRequest((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
_getTag() {
return new Promise((resolve, reject) => {
NativeNfcManager.getTag((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// public only for Android
// -------------------------------------
isEnabled() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.isEnabled((err, result) => {
if (err) {
reject(err);
} else {
resolve(result)
}
})
})
}
goToNfcSetting() {
return new Promise((resolve, reject) => {
NativeNfcManager.goToNfcSetting((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
getLaunchTagEvent() {
return new Promise((resolve, reject) => {
NativeNfcManager.getLaunchTagEvent((err, tag) => {
if (err) {
reject(err);
} else {
resolve(tag)
}
});
})
}
setNdefPushMessage(bytes) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.setNdefPushMessage(bytes, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// public only for iOS
// -------------------------------------
setAlertMessageIOS(alertMessage) {
if (Platform.OS !== 'ios') {
// it's a no-op for android
return;
}
return new Promise((resolve, reject) => {
NativeNfcManager.setAlertMessage(alertMessage, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
})
})
}
// -------------------------------------
// Android private
// -------------------------------------
_hasTagEventRegistrationAndroid() {
if (Platform.OS !== 'android') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.hasTagEventRegistration((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
// -------------------------------------
// iOS private
// -------------------------------------
_isSessionAvailableIOS() {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.isSessionAvailable((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
_isSessionExAvailableIOS() {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.isSessionExAvailable((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
_registerTagEventExIOS(options = {}) {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
const optionsWithDefault = {
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
...options,
};
return new Promise((resolve, reject) => {
NativeNfcManager.registerTagEventEx(optionsWithDefault, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
_unregisterTagEventExIOS() {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.unregisterTagEventEx((err, result) => {
if (err) {
reject(err);
} else {
resolve(result)
}
})
})
}
// -------------------------------------
// deprecated APIs
// -------------------------------------
requestNdefWrite(bytes, {format=false, formatReadOnly=false}={}) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.requestNdefWrite(bytes, {format, formatReadOnly}, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
cancelNdefWrite() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.cancelNdefWrite((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// Nfc Tech request API
// -------------------------------------
requestTechnology = async (tech, options={}) => {
try {
if (typeof tech === 'string') {
tech = [tech];
}
let hasNdefTech = tech.indexOf(NfcTech.Ndef) !== -1;
let sessionAvailable = false;
// check if required session is available
if (Platform.OS === 'ios') {
if (hasNdefTech) {
sessionAvailable = await this._isSessionAvailableIOS();
} else {
sessionAvailable = await this._isSessionExAvailableIOS();
}
} else {
sessionAvailable = await this._hasTagEventRegistrationAndroid();
}
// make sure we do register for tag event
if (!sessionAvailable) {
if (Platform.OS === 'ios') {
if (hasNdefTech) {
await this.registerTagEvent(options);
} else {
await this._registerTagEventExIOS(options);
}
} else {
await this.registerTagEvent(options);
}
// the tag registration is
this.cleanUpTagRegistration = true;
}
return await this._requestTechnology(tech);
} catch (ex) {
throw ex;
}
}
cancelTechnologyRequest = async () => {
await this._cancelTechnologyRequest();
if (this.cleanUpTagRegistration) {
this.cleanUpTagRegistration = false;
if (Platform.OS === 'ios') {
let sessionAvailable = false;
// because we don't know which tech currently requested
// so we try both, and perform early return when hitting any
sessionAvailable = await this._isSessionExAvailableIOS();
if (sessionAvailable) {
await this._unregisterTagEventExIOS();
return;
}
sessionAvailable = await this._isSessionAvailableIOS();
if (sessionAvailable) {
await this.unregisterTagEvent();
return;
}
} else {
await this.unregisterTagEvent();
return;
}
}
}
getTag = async () => {
return this._getTag();
}
// -------------------------------------
// NfcTech.Ndef API
// -------------------------------------
writeNdefMessage(bytes) {
return new Promise((resolve, reject) => {
NativeNfcManager.writeNdefMessage(bytes, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
getNdefMessage() {
return new Promise((resolve, reject) => {
NativeNfcManager.getNdefMessage((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
getCachedNdefMessageAndroid() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.getCachedNdefMessage((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
makeReadOnlyAndroid() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.makeReadOnly((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// NfcTech.MifareClassic API
// -------------------------------------
mifareClassicAuthenticateA(sector, key) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
if (!key || !Array.isArray(key) || key.length !== 6) {
return Promise.reject('key should be an Array[6] of integers (0 - 255)');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicAuthenticateA(sector, key, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicAuthenticateB(sector, key) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
if (!key || !Array.isArray(key) || key.length !== 6) {
return Promise.reject('key should be an Array[6] of integers (0 - 255)');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicAuthenticateB(sector, key, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicGetBlockCountInSector(sector) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicGetBlockCountInSector(sector, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicGetSectorCount() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicGetSectorCount((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicSectorToBlock(sector) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicSectorToBlock(sector, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicReadBlock(block) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicReadBlock(block, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicReadSector(sector) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicReadSector(sector, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareClassicWriteBlock(block, data) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
if (!data || !Array.isArray(data) || data.length !== this.MIFARE_BLOCK_SIZE) {
return Promise.reject(`data should be a non-empty Array[${this.MIFARE_BLOCK_SIZE}] of integers (0 - 255)`);
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareClassicWriteBlock(block, data, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// NfcTech.MifareUltralight API
// -------------------------------------
mifareUltralightReadPages(pageOffset) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareUltralightReadPages(pageOffset, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
mifareUltralightWritePage(pageOffset, data) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
if (!data || !Array.isArray(data) || data.length !== this.MIFARE_ULTRALIGHT_PAGE_SIZE) {
return Promise.reject(`data should be a non-empty Array[${this.MIFARE_ULTRALIGHT_PAGE_SIZE}] of integers (0 - 255)`);
}
return new Promise((resolve, reject) => {
NativeNfcManager.mifareUltralightWritePage(pageOffset, data, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// setTimeout works for NfcA, NfcF, IsoDep, MifareClassic, MifareUltralight
// -------------------------------------
setTimeout(timeout) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.setTimeout(timeout, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
connect(techs) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.connect(techs, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
close() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.close((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// transceive works for NfcA, NfcB, NfcF, NfcV, IsoDep and MifareUltralight
// -------------------------------------
transceive(bytes) {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.transceive(bytes, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
getMaxTransceiveLength() {
if (Platform.OS === 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.getMaxTransceiveLength((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
}
// -------------------------------------
// iOS specific
// -------------------------------------
sendMifareCommandIOS(bytes) {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.sendMifareCommand(bytes, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
})
}
sendCommandAPDUIOS(bytes) {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
return new Promise((resolve, reject) => {
NativeNfcManager.sendCommandAPDU(bytes, (err, response, sw1, sw2) => {
if (err) {
reject(err);
} else {
resolve({response, sw1, sw2});
}
});
})
}
}
export default new NfcManager();
export {
ByteParser,
NdefParser,
NfcTech,
NfcEvents,
NfcAdapter,
Ndef,
}

324
README.md
View File

@ -8,316 +8,78 @@ Bring NFC feature to React Native. Inspired by [phonegap-nfc](https://github.com
Contributions are welcome!
Made with ❤️ by [whitedogg13](https://github.com/whitedogg13) and [revteltech](https://github.com/revtel)
## iOS 13 development is ongoing!
Ndef writing, get UID, send mifare command, and APDU exchange... Lots features come into iOS13!
Currently this work will be published in npm beta channel.
## Install
### javascript part
```shell
npm i --save react-native-nfc-manager
# RN >= 0.60, XCode 11 (for all fancy iOS 13 core nfc features!)
npm i --save react-native-nfc-manager@beta
```
### native part
This library use native-modules, so you will need to do `pod install` for iOS:
```shell
cd ios && pod install && cd ..
# RN >= 0.60, XCode 10
npm i --save react-native-nfc-manager@2.0.0-beta.1
```
For Android, it should be properly auto-linked, so you don't need to do anything.
```shell
# RN < 0.60, XCode 10
npm i --save react-native-nfc-manager@1.2.2
```
## Setup
Please see [here](setup.md)
### **Android 12**
We start to support Android 12 from `v3.11.1`, and you will need to update `compileSdkVersion` to `31`, otherwise the build will fail:
```
buildscript {
ext {
...
compileSdkVersion = 31
...
}
...
}
```shell
# RN >= 0.60, iOS
cd ios && pod install && cd ..
# ...then open ios/xxx.xcworkspace...
```
The reason for this is because Android puts new limitation on [PendingIntent](https://developer.android.com/reference/android/app/PendingIntent#FLAG_MUTABLE) which says `Starting with Build.VERSION_CODES.S, it will be required to explicitly specify the mutability of PendingIntents`
```shell
# RN >= 0.60, Android
# This module leverages autolink, so no extra steps are required
```
(see [here](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#autolinking) for more info about autolink)
> The original issue is [here](https://github.com/revtel/react-native-nfc-manager/issues/469)
BTW, if you don't care about **Android 12** for now, you can use **`v3.11.0`** as a short term solution.
### **[Demo App] NfcOpenReWriter**
We have a full featured NFC utility app using this library available for download.
<a href='https://apps.apple.com/tw/app/nfc-rewriter/id1551243964' target='_blank'>
<img alt="react-native-nfc-rewriter" src="./images/Apple-App-Store-Icon.png" width="250">
</a>
</br>
<a href='https://play.google.com/store/apps/details?id=com.washow.nfcopenrewriter' target='_blank'>
<img alt="react-native-nfc-rewriter" src="./images/google-play-icon.jpeg" width="250">
</a>
It also open sourced in this repo: [React Native NFC ReWriter App](https://github.com/revtel/react-native-nfc-rewriter)
## Learn
We have published a React Native NFC course with [newline.co](https://www.newline.co/), check it out!
- Free course (1 hour) about basic NFC setup and concept [here](https://www.youtube.com/watch?v=rAS-DvNUFck)
- Full course (3 hours) for more (NDEF, Deep Linking, NTAG password protection, signature with UID) [here](https://www.newline.co/courses/newline-guide-to-nfcs-with-react-native)
## Usage
The simplest (and most common) use case for this library is to read `NFC` tags containing `NDEF`, which can be achieved via the following codes:
```javascript
import React from 'react';
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
import NfcManager, {NfcTech} from 'react-native-nfc-manager';
// Pre-step, call this before any NFC operations
NfcManager.start();
function App() {
async function readNdef() {
try {
// register for the NFC tag with NDEF in it
await NfcManager.requestTechnology(NfcTech.Ndef);
// the resolved tag object will contain `ndefMessage` property
const tag = await NfcManager.getTag();
console.warn('Tag found', tag);
} catch (ex) {
console.warn('Oops!', ex);
} finally {
// stop the nfc scanning
NfcManager.cancelTechnologyRequest();
}
}
return (
<View style={styles.wrapper}>
<TouchableOpacity onPress={readNdef}>
<Text>Scan a Tag</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
```shell
# RN < 0.60, both platforms
react-native link react-native-nfc-manager
```
Pleaes notice when running above codes, iOS and Android has different behaviors:
## Extra iOS setup is required
- iOS will pop up a system scanning UI
- Android provides **NO** system scanning UI
You will need to setup some capabilities / entitlement / plist stuff to enable NFC development on your device, this repo explains these requirements very well:
Regarding the system scannning UI, both platforms should be able to scan your NFC tags succesfully and print out its content.
* https://github.com/hansemannn/iOS11-NFC-Example
### Old Style (registerTagEvent) To Scan NFC Tags
There's an alterntaive style to scan NFC tags through `NfcManager.registerTagEvent`, like this:
## Example
```javascript
import NfcManager, {NfcTech} from 'react-native-nfc-manager';
Look into `example` for the features you need.
// The following function resolves to a NFC Tag object using old event listener approach.
// You can call it like this:
// `const nfcTag = await listenToNfcEventOnce()`
**v2 examples**
function listenToNfcEventOnce() {
const cleanUp = () => {
NfcManager.setEventListener(NfcEvents.DiscoverTag, null);
NfcManager.setEventListener(NfcEvents.SessionClosed, null);
};
* [v2-ios+android-read-ndef](example/AppV2.js)
* [v2-ios+android-write-ndef](example/AppV2Ndef.js)
* [v2-ios+android-get-uid](example/AppV2Mifare.js)
* [v2-ios+android-mifare-custom-command](example/AppV2Mifare.js)
return new Promise((resolve) => {
let tagFound = null;
**v1 examples**
NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag) => {
tagFound = tag;
resolve(tagFound);
NfcManager.unregisterTagEvent();
});
* [v1-ios-read-ndef](example/App.js)
* [v1-android-read-write-ndef](example/App.js)
* [v1-android-mifare-classic](example/AndroidMifareClassic.js)
* [v1-android-read-write-ndef-with-ndef-tech](example/AndroidTechTestNdef.js)
NfcManager.setEventListener(NfcEvents.SessionClosed, () => {
cleanUp();
if (!tagFound) {
resolve();
}
});
## API Document
NfcManager.registerTagEvent();
});
}
```
* [v2 doc](APIv2.md)
* [v1 doc](APIv1.md)
As you can see, the above approach is more verbose and hard-to-read, so we recommend using `NfcManager.requestTechnology` instead of `NfcManager.registerTagEvent` in your application.
## Advanced Usage Concept
In higher level, there're 4 steps to use this library:
1. request your particular NFC technologies through `NfcManager.requestTechnology`, for example:
- `Ndef`
- `NfcA`
- `NfcB` (Android-only)
- `NfcF` (Android-only)
- `NfcV` (Android-only)
- `IsoDep`
- `MifareClassic` (Android-only)
- `MifareUltralight` (Android-only)
- `MifareIOS` (ios-only)
- `Iso15693IOS` (ios-only)
- `FelicaIOS` (ios-only)
2. select the proper NFC technology handler, which is implemented as getter in main `NfcManager` object, for example:
- `ndefHandler` (for `Ndef` tech)
- `nfcAHandler` (for `NfcA` tech)
- `isoDepHandler` (for `IsoDep` tech)
- `iso15693HandlerIOS` (for `Iso15693IOS` tech)
- `mifareClassicHandlerAndroid` (for `mifareClassic` tech)
- `mifareUltralightHandlerAndroid` (for `mifareUltralight` tech)
- ... and so on
3. call specific methods on the NFC technology handler (for example `NfcManager.ndefHandler.writeNdefMessage`). To view all available methods for some tech handler, check out the [API List](index.d.ts)
4. clean up your tech registration through `NfcManager.cancelTechnology`
## Advanced Usage Example: NDEF-Writing
For example, here's an example to write NDEF:
```javascript
import NfcManager, {NfcTech, Ndef} from 'react-native-nfc-manager';
async function writeNdef({type, value}) {
let result = false;
try {
// STEP 1
await NfcManager.requestTechnology(NfcTech.Ndef);
const bytes = Ndef.encodeMessage([Ndef.textRecord('Hello NFC')]);
if (bytes) {
await NfcManager.ndefHandler // STEP 2
.writeNdefMessage(bytes); // STEP 3
result = true;
}
} catch (ex) {
console.warn(ex);
} finally {
// STEP 4
NfcManager.cancelTechnologyRequest();
}
return result;
}
```
## Advanced Usage Example: Mifare Ultralight
Here's another example to read a Mifare Ultralight tag:
```javascript
async function readMifare() {
let mifarePages = [];
try {
// STEP 1
let reqMifare = await NfcManager.requestTechnology(
NfcTech.MifareUltralight,
);
const readLength = 60;
const mifarePagesRead = await Promise.all(
[...Array(readLength).keys()].map(async (_, i) => {
const pages = await NfcManager.mifareUltralightHandlerAndroid // STEP 2
.mifareUltralightReadPages(i * 4); // STEP 3
mifarePages.push(pages);
}),
);
} catch (ex) {
console.warn(ex);
} finally {
// STEP 4
NfcManager.cancelTechnologyRequest();
}
return mifarePages;
}
```
To see more examples, please see [React Native NFC ReWriter App](https://github.com/revtel/react-native-nfc-rewriter)
## API
Please see [here](index.d.ts)
## FAQ
Please see [here](FAQ.md)
## Expo
> This package cannot be used in the "Expo Go" app because [it requires custom native code](https://docs.expo.io/workflow/customizing/).
After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
```json
{
"expo": {
"plugins": ["react-native-nfc-manager"]
}
}
```
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
> Notice: This Config Plugin will ensure the minimum Android SDK version is 31.
#### Props
The plugin provides props for extra customization. Every time you change the props or plugins, you'll need to rebuild (and `prebuild`) the native app. If no extra properties are added, defaults will be used.
- `nfcPermission` (_string | false_): Sets the iOS `NFCReaderUsageDescription` permission message to the `Info.plist`. Setting `false` will skip adding the permission. Defaults to `Allow $(PRODUCT_NAME) to interact with nearby NFC devices` (Info.plist).
- `selectIdentifiers` (_string[]_): Sets the iOS [`com.apple.developer.nfc.readersession.iso7816.select-identifiers`](https://developer.apple.com/documentation/bundleresources/information_property_list/select-identifiers) to a list of supported application IDs (Info.plist).
- `systemCodes` (_string[]_): Sets the iOS [`com.apple.developer.nfc.readersession.felica.systemcodes`](https://developer.apple.com/documentation/bundleresources/information_property_list/systemcodes) to a user provided list of FeliCa™ system codes that the app supports (Info.plist). Each system code must be a discrete value. The wild card value (`0xFF`) isn't allowed.
#### Example
```json
{
"expo": {
"plugins": [
[
"react-native-nfc-manager",
{
"nfcPermission": "Custom permission message",
"selectIdentifiers": ["A0000002471001"],
"systemCodes": ["8008"]
}
]
]
}
}
```

View File

@ -1,17 +0,0 @@
const ReactNative = {};
let _os = 'ios';
const Platform = {
get OS() {
return _os;
},
setOS: (os) => {
_os = os;
},
};
ReactNative.Platform = Platform;
module.exports = ReactNative;

View File

@ -1,151 +0,0 @@
jest.mock('../src/NativeNfcManager');
import {Platform} from 'react-native';
import {
NativeNfcManager,
NfcManagerEmitter,
callNative,
} from '../src/NativeNfcManager';
import * as NfcError from '../src/NfcError';
describe('NfcManager (ios)', () => {
Platform.setOS('ios');
const NfcManagerModule = require('../src/index.js');
const NfcManager = NfcManagerModule.default;
const {NfcEvents, NfcErrorIOS, NfcTech} = NfcManagerModule;
const lastNativeCall = () =>
callNative.mock.calls[callNative.mock.calls.length - 1];
test('constructor', () => {
expect(Platform.OS).toBe('ios');
// the NfcManager instance doest exist
expect(!!NfcManager).toEqual(true);
});
test('register native events', () => {
for (const evtName of [NfcEvents.DiscoverTag, NfcEvents.SessionClosed]) {
let hit = false;
for (const mockCall of NfcManagerEmitter.addListener.mock.calls) {
if (mockCall[0] === evtName) {
hit = true;
break;
}
}
if (!hit) {
// this native event is not registered, treat as error
expect(true).toBe(false);
}
}
});
test('API capability', () => {
expect(typeof NfcManager.start).toBe('function');
expect(typeof NfcManager.isSupported).toBe('function');
expect(typeof NfcManager.setEventListener).toBe('function');
expect(typeof NfcManager.registerTagEvent).toBe('function');
expect(typeof NfcManager.unregisterTagEvent).toBe('function');
expect(typeof NfcManager.getTag).toBe('function');
expect(typeof NfcManager.requestTechnology).toBe('function');
expect(typeof NfcManager.cancelTechnologyRequest).toBe('function');
});
test('API: start', () => {
NfcManager.start();
expect(lastNativeCall()[0]).toEqual('start');
});
test('API: isSupported', () => {
NfcManager.isSupported('Ndef');
expect(lastNativeCall()[0]).toEqual('isSupported');
expect(lastNativeCall()[1]).toEqual(['Ndef']);
});
test('API: setEventListener', () => {
try {
NfcManager.setEventListener('no-such-event', () => 0);
expect(false).toBe(true);
} catch (ex) {
// should throw an ex if no such event
expect(true).toBe(true);
}
// can receive DiscoverTag event
const tag1 = {id: '3939889'};
let tag2 = null;
NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag) => {
tag2 = tag;
});
NfcManagerEmitter._testTriggerCallback(NfcEvents.DiscoverTag, tag1);
expect(tag2).toEqual(tag1);
// can receive SessionClosed event
let sessionClosed = false;
NfcManager.setEventListener(NfcEvents.SessionClosed, () => {
sessionClosed = true;
});
NfcManagerEmitter._testTriggerCallback(NfcEvents.SessionClosed, {
error: 'NFCError:200',
});
expect(sessionClosed).toBe(true);
});
test('API: registerTagEvent', () => {
NfcManager.registerTagEvent();
expect(lastNativeCall()[0]).toEqual('registerTagEvent');
const options = lastNativeCall()[1][0];
// check if we pass the default options into native
expect(options.alertMessage).toEqual('Please tap NFC tags');
expect(options.invalidateAfterFirstRead).toBe(false);
});
test('API: cancelTechnologyRequest', async () => {
// won't throw any error during cancellation by default
NativeNfcManager.setNextError('fake-error');
await NfcManager.cancelTechnologyRequest();
NativeNfcManager.setNextError('fake-error-again');
try {
// default can be overriden by throwOnError
await NfcManager.cancelTechnologyRequest({throwOnError: true});
} catch (ex) {
expect(ex.message).toEqual('fake-error-again');
}
});
test('API: setAlertMessage', () => {
NfcManager.setAlertMessageIOS('hello');
expect(lastNativeCall()[0]).toEqual('setAlertMessage');
expect(lastNativeCall()[1]).toEqual(['hello']);
NfcManager.setAlertMessage('hello');
expect(lastNativeCall()[0]).toEqual('setAlertMessage');
expect(lastNativeCall()[1]).toEqual(['hello']);
});
test('NfcErrorIOS', () => {
expect(NfcErrorIOS.parse({})).toEqual(NfcErrorIOS.errCodes.unknown);
expect(NfcErrorIOS.parse('nosucherror')).toEqual(
NfcErrorIOS.errCodes.unknown,
);
expect(NfcErrorIOS.parse('NFCError:200')).toEqual(
NfcErrorIOS.errCodes.userCancel,
);
});
test('NfcError', async () => {
try {
NativeNfcManager.setNextError('NFCError:200');
await NfcManager.requestTechnology(NfcTech.Ndef);
} catch (ex) {
if (!(ex instanceof NfcError.UserCancel)) {
expect(true).toBe(false);
}
// for backward capatible
if (NfcErrorIOS.parse(ex) !== NfcErrorIOS.errCodes.userCancel) {
expect(true).toBe(false);
}
}
});
});

View File

@ -1,73 +0,0 @@
jest.mock('../src/NativeNfcManager');
import {Platform} from 'react-native';
import {NativeNfcManager} from '../src/NativeNfcManager';
import * as NfcError from '../src/NfcError';
describe('NfcManager (android)', () => {
Platform.setOS('android');
const NfcManagerModule = require('../src/index.js');
const NfcManager = NfcManagerModule.default;
const {NfcTech} = NfcManagerModule;
test('constructor', () => {
expect(Platform.OS).toBe('android');
// the NfcManager instance doest exist
expect(!!NfcManager).toEqual(true);
});
test('mifareClassicHandler', async () => {
expect(!!NfcManager.mifareClassicHandlerAndroid).toBe(true);
try {
// should throw exception if the data is not an array of length 16
await NfcManager.mifareClassicHandlerAndroid.mifareClassicWriteBlock(1, [
1,
]);
expect(true).toBe(false);
} catch (ex) {}
// https://github.com/whitedogg13/react-native-nfc-manager/issues/371
await NfcManager.mifareClassicHandlerAndroid.mifareClassicWriteBlock(
5,
Array.from({length: 16}).map((_, i) => i),
);
});
test('mifareUltralightHandler', async () => {
expect(!!NfcManager.mifareUltralightHandlerAndroid).toBe(true);
try {
// should throw exception if the data is not an array of length 4
await NfcManager.mifareUltralightHandlerAndroid.mifareUltralightWritePage(
1,
[1],
);
expect(true).toBe(false);
} catch (ex) {}
// https://github.com/whitedogg13/react-native-nfc-manager/issues/386
// https://github.com/whitedogg13/react-native-nfc-manager/issues/387
await NfcManager.mifareUltralightHandlerAndroid.mifareUltralightWritePage(
5,
Array.from({length: 4}).map((_, i) => i),
);
});
test('API: setAlertMessage', async () => {
// test if the method stub exists and can be called without exception
await NfcManager.setAlertMessage();
expect(true).toBe(true);
});
test('NfcError', async () => {
try {
NativeNfcManager.setNextError('cancelled');
await NfcManager.requestTechnology(NfcTech.Ndef);
} catch (ex) {
if (!(ex instanceof NfcError.UserCancel)) {
expect(true).toBe(false);
}
}
});
});

View File

@ -1,169 +0,0 @@
const ndef = require('../ndef-lib');
const textMessageHelloWorld = [
209,
1,
15,
84,
2,
101,
110,
104,
101,
108,
108,
111,
44,
32,
119,
111,
114,
108,
100,
];
const urlMessageNodeJSorg = [
209,
1,
11,
85,
3,
110,
111,
100,
101,
106,
115,
46,
111,
114,
103,
];
const multipleRecordMessage = [
145,
1,
15,
84,
2,
101,
110,
104,
101,
108,
108,
111,
44,
32,
119,
111,
114,
108,
100,
17,
1,
11,
85,
3,
110,
111,
100,
101,
106,
115,
46,
111,
114,
103,
82,
9,
27,
116,
101,
120,
116,
47,
106,
115,
111,
110,
123,
34,
109,
101,
115,
115,
97,
103,
101,
34,
58,
32,
34,
104,
101,
108,
108,
111,
44,
32,
119,
111,
114,
108,
100,
34,
125,
];
test('build and parse text', () => {
const text = 'hello, world';
let message = [ndef.textRecord(text)];
let encoded = ndef.encodeMessage(message);
expect(encoded).toEqual(textMessageHelloWorld);
let decodedMessage = ndef.decodeMessage(encoded);
expect(message[0]).toEqual(decodedMessage[0]);
expect(ndef.text.decodePayload(message[0].payload)).toEqual(text);
});
test('build and parse uri', () => {
let message = [ndef.uriRecord('http://nodejs.org')];
let encoded = ndef.encodeMessage(message);
expect(encoded).toEqual(urlMessageNodeJSorg);
let decodedMessage = ndef.decodeMessage(encoded);
expect(message[0]).toEqual(decodedMessage[0]);
});
test('build and parse multiple records', () => {
var message = [
ndef.textRecord('hello, world'),
ndef.uriRecord('http://nodejs.org'),
ndef.mimeMediaRecord('text/json', '{"message": "hello, world"}'),
];
let encoded = ndef.encodeMessage(message);
expect(encoded).toEqual(multipleRecordMessage);
let decodedMessage = ndef.decodeMessage(encoded);
expect(message[0]).toEqual(decodedMessage[0]);
expect(message[1]).toEqual(decodedMessage[1]);
expect(message[2]).toEqual(decodedMessage[2]);
});
test('build and parse wifi simple payload', () => {
const wifiCredentials = {
ssid: 'my-wifi-ap',
networkKey: 'Abcabc123',
};
const payload = ndef.wifiSimple.encodePayload(wifiCredentials);
const parsed = ndef.wifiSimple.decodePayload(payload);
expect(parsed.ssid).toEqual(wifiCredentials.ssid);
expect(parsed.networkKey).toEqual(wifiCredentials.networkKey);
expect(parsed.authType).toEqual(ndef.wifiSimple.authTypes.WPA2_PSK);
});

View File

@ -4,7 +4,7 @@ def safeExtGet(prop, fallback) {
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
@ -14,7 +14,9 @@ buildscript {
apply plugin: 'com.android.library'
android {
compileSdkVersion safeExtGet('compileSdkVersion', 31)
compileSdkVersion safeExtGet('compileSdkVersion', 27)
buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3')
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16)
//noinspection OldTargetApi

View File

@ -106,8 +106,6 @@ public abstract class JsonConvert {
writableMap.putMap(key,jsonToReact(jsonObject.getJSONObject(key)));
} else if (value instanceof JSONArray){
writableMap.putArray(key, jsonToReact(jsonObject.getJSONArray(key)));
} else if ("true".equals(value.toString()) || "false".equals(value.toString())){
writableMap.putBoolean(key, "true".equals(value.toString()));
} else if (value == JSONObject.NULL){
writableMap.putNull(key);
}
@ -130,8 +128,6 @@ public abstract class JsonConvert {
writableArray.pushMap(jsonToReact(jsonArray.getJSONObject(i)));
} else if (value instanceof JSONArray){
writableArray.pushArray(jsonToReact(jsonArray.getJSONArray(i)));
} else if ("true".equals(value.toString()) || "false".equals(value.toString())){
writableArray.pushBoolean("true".equals(value.toString()));
} else if (value == JSONObject.NULL){
writableArray.pushNull();
}

View File

@ -49,7 +49,7 @@ class MifareUtil {
// able of reading/writing MIFARE Classic tags. I don't know why...
// https://github.com/ikarus23/MifareClassicTool/issues/152
boolean isLenovoP2 = Build.MANUFACTURER.equals("LENOVO")
&& Build.MODEL.equals("Lenovo P2a42");
&& Build.MODEL.equals("Lenovo P2a42");
File device = new File("/dev/bcm2079x-i2c");
if (!isLenovoP2 && device.exists()) {
return false;
@ -70,8 +70,8 @@ class MifareUtil {
File[] libs = libsFolder.listFiles();
for (File lib : libs) {
if (lib.isFile()
&& lib.getName().startsWith("libnfc")
&& lib.getName().contains("brcm")
&& lib.getName().startsWith("libnfc")
&& lib.getName().contains("brcm")
// Add here other non NXP NFC libraries.
) {
return false;

View File

@ -49,20 +49,9 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
private WriteNdefRequest writeNdefRequest = null;
private TagTechnologyRequest techRequest = null;
private Tag tag = null;
private WritableMap bgTag = null;
// Use NFC reader mode instead of listening to a dispatch
private Boolean isReaderModeEnabled = false;
private int readerModeFlags = 0;
private int readerModeDelay = 0;
private static final String ERR_CANCEL = "cancelled";
private static final String ERR_NOT_REGISTERED = "you should requestTagEvent first";
private static final String ERR_MULTI_REQ = "You can only issue one request at a time";
private static final String ERR_NO_TECH_REQ = "no tech request available";
private static final String ERR_NO_REFERENCE = "no reference available";
private static final String ERR_TRANSCEIVE_FAIL = "transceive fail";
private static final String ERR_API_NOT_SUPPORT = "unsupported tag api";
private static final String ERR_GET_ACTIVITY_FAIL = "fail to get current activity";
private static final String ERR_NO_NFC_SUPPORT = "no nfc support";
class WriteNdefRequest {
NdefMessage message;
@ -115,7 +104,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
if (techRequest != null) {
techRequest.close();
try {
techRequest.getPendingCallback().invoke(ERR_CANCEL);
techRequest.getPendingCallback().invoke("cancelled");
} catch (RuntimeException ex) {
// the pending callback might already been invoked when there is an ongoing
// connected tag, bypass this case explicitly
@ -133,12 +122,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
public void requestTechnology(ReadableArray techs, Callback callback) {
synchronized(this) {
if (!isForegroundEnabled) {
callback.invoke(ERR_NOT_REGISTERED);
callback.invoke("you should requestTagEvent first");
return;
}
if (hasPendingRequest()) {
callback.invoke(ERR_MULTI_REQ);
callback.invoke("You can only issue one request at a time");
} else {
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
}
@ -161,24 +150,19 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
@ReactMethod
public void getTag(Callback callback) {
synchronized (this) {
synchronized(this) {
if (techRequest != null) {
Tag tag = techRequest.getTagHandle();
if (tag != null) {
try {
TagTechnology tagTech = techRequest.getTechHandle();
Tag tag = tagTech.getTag();
WritableMap parsed = tag2React(tag);
if (Arrays.asList(tag.getTechList()).contains(Ndef.class.getName())) {
try {
Ndef ndef = Ndef.get(tag);
parsed = ndef2React(ndef, new NdefMessage[]{ndef.getCachedNdefMessage()});
} catch (Exception ex) {
}
}
callback.invoke(null, parsed);
} else {
callback.invoke(ERR_NO_REFERENCE);
} catch (Exception ex) {
Log.d(LOG_TAG, "getTag fail");
callback.invoke("getTag fail");
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -188,15 +172,15 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
synchronized(this) {
if (techRequest != null) {
try {
Ndef ndef = Ndef.get(techRequest.getTagHandle());
Ndef ndef = Ndef.get(techRequest.getTechHandle().getTag());
WritableMap parsed = ndef2React(ndef, new NdefMessage[] { ndef.getCachedNdefMessage() });
callback.invoke(null, parsed);
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
Log.d(LOG_TAG, "getCachedNdefMessage fail");
callback.invoke("getCachedNdefMessage fail");
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -206,39 +190,15 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
synchronized(this) {
if (techRequest != null) {
try {
Ndef ndef = Ndef.get(techRequest.getTagHandle());
Ndef ndef = Ndef.get(techRequest.getTechHandle().getTag());
WritableMap parsed = ndef2React(null, new NdefMessage[] { ndef.getNdefMessage() });
callback.invoke(null, parsed);
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
Log.d(LOG_TAG, "getNdefMessage fail");
callback.invoke("getNdefMessage fail");
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
}
}
}
@ReactMethod
public void getNdefStatus(Callback callback) {
synchronized(this) {
if (techRequest != null) {
WritableMap writableMap = Arguments.createMap();
try {
Ndef ndef = Ndef.get(techRequest.getTagHandle());
int maxSize = ndef.getMaxSize();
boolean isWritable = ndef.isWritable();
boolean canMakeReadOnly = ndef.canMakeReadOnly();
writableMap.putInt("maxSize", maxSize);
writableMap.putBoolean("isWritable", isWritable);
writableMap.putBoolean("canMakeReadOnly", canMakeReadOnly);
callback.invoke(null, writableMap);
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -249,49 +209,15 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
if (techRequest != null) {
try {
Ndef ndef = (Ndef)techRequest.getTechHandle();
if (ndef == null) {
callback.invoke(ERR_API_NOT_SUPPORT);
} else {
byte[] bytes = rnArrayToBytes(rnArray);
ndef.writeNdefMessage(new NdefMessage(bytes));
callback.invoke();
}
byte[] bytes = rnArrayToBytes(rnArray);
ndef.writeNdefMessage(new NdefMessage(bytes));
callback.invoke();
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
Log.d(LOG_TAG, "writeNdefMessage fail");
callback.invoke("writeNdefMessage fail");
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
}
}
}
@ReactMethod
public void formatNdef(ReadableArray rnArray, ReadableMap options, Callback callback) {
boolean readOnly = options.getBoolean("readOnly");
synchronized(this) {
if (techRequest != null) {
try {
NdefFormatable ndef = (NdefFormatable)techRequest.getTechHandle();
if (ndef == null) {
callback.invoke(ERR_API_NOT_SUPPORT);
} else {
byte[] bytes = rnArrayToBytes(rnArray);
NdefMessage msg = new NdefMessage(bytes);
if (readOnly) {
ndef.formatReadOnly(msg);
} else {
ndef.format(msg);
}
callback.invoke();
}
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -335,7 +261,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicAuthenticate fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
@ -375,7 +301,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicGetBlockCountInSector fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -397,7 +323,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicGetSectorCount fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -424,7 +350,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicSectorToBlock fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -457,7 +383,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicReadBlock fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -494,7 +420,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicReadSector fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -531,100 +457,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareClassicWriteBlock fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
}
}
}
@ReactMethod
public void mifareClassicIncrementBlock(int blockIndex, int value, Callback callback) {
synchronized(this) {
if (techRequest != null) {
try {
MifareClassic mifareTag = (MifareClassic) techRequest.getTechHandle();
if (mifareTag == null || mifareTag.getType() == MifareClassic.TYPE_UNKNOWN) {
// Not a mifare card, fail
callback.invoke("mifareClassicIncrementBlock fail: TYPE_UNKNOWN");
return;
} else if (blockIndex >= mifareTag.getBlockCount()) {
// Check if in range
String msg = String.format("mifareClassicIncrementBlock fail: invalid block %d (max %d)", blockIndex, mifareTag.getBlockCount());
callback.invoke(msg);
return;
}
mifareTag.increment(blockIndex, value);
callback.invoke(null, true);
} catch (TagLostException ex) {
callback.invoke("mifareClassicIncrementBlock fail: TAG_LOST");
} catch (Exception ex) {
callback.invoke("mifareClassicIncrementBlock fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
}
}
}
@ReactMethod
public void mifareClassicDecrementBlock(int blockIndex, int value, Callback callback) {
synchronized(this) {
if (techRequest != null) {
try {
MifareClassic mifareTag = (MifareClassic) techRequest.getTechHandle();
if (mifareTag == null || mifareTag.getType() == MifareClassic.TYPE_UNKNOWN) {
// Not a mifare card, fail
callback.invoke("mifareClassicDecrementBlock fail: TYPE_UNKNOWN");
return;
} else if (blockIndex >= mifareTag.getBlockCount()) {
// Check if in range
String msg = String.format("mifareClassicDecrementBlock fail: invalid block %d (max %d)", blockIndex, mifareTag.getBlockCount());
callback.invoke(msg);
return;
}
mifareTag.decrement(blockIndex, value);
callback.invoke(null, true);
} catch (TagLostException ex) {
callback.invoke("mifareClassicDecrementBlock fail: TAG_LOST");
} catch (Exception ex) {
callback.invoke("mifareClassicDecrementBlock fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
}
}
}
@ReactMethod
public void mifareClassicTransferBlock(int blockIndex, Callback callback) {
synchronized(this) {
if (techRequest != null) {
try {
MifareClassic mifareTag = (MifareClassic) techRequest.getTechHandle();
if (mifareTag == null || mifareTag.getType() == MifareClassic.TYPE_UNKNOWN) {
// Not a mifare card, fail
callback.invoke("mifareClassicTransferBlock fail: TYPE_UNKNOWN");
return;
} else if (blockIndex >= mifareTag.getBlockCount()) {
// Check if in range
String msg = String.format("mifareClassicTransferBlock fail: invalid block %d (max %d)", blockIndex, mifareTag.getBlockCount());
callback.invoke(msg);
return;
}
mifareTag.transfer(blockIndex);
callback.invoke(null, true);
} catch (TagLostException ex) {
callback.invoke("mifareClassicTransferBlock fail: TAG_LOST");
} catch (Exception ex) {
callback.invoke("mifareClassicTransferBlock fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -645,7 +478,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareUltralight fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -666,7 +499,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke("mifareUltralight fail: " + ex.toString());
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -680,11 +513,11 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
boolean result = ndef.makeReadOnly();
callback.invoke(null, result);
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
Log.d(LOG_TAG, "makeReadOnly fail");
callback.invoke("makeReadOnly fail");
}
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -725,13 +558,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
return;
}
Log.d(LOG_TAG, "setTimeout not supported");
callback.invoke(ERR_API_NOT_SUPPORT);
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
Log.d(LOG_TAG, "setTimeout fail");
}
callback.invoke("setTimeout fail");
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -739,27 +571,27 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
@ReactMethod
public void connect(ReadableArray techs, Callback callback){
synchronized(this) {
try {
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
techRequest.connect(this.tag);
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
try {
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
techRequest.connect(this.tag);
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
}
}
@ReactMethod
public void close(Callback callback){
synchronized(this) {
try {
techRequest.close();
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
try {
techRequest.close();
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
}
}
@ -818,13 +650,13 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
return;
}
Log.d(LOG_TAG, "transceive not supported");
callback.invoke(ERR_API_NOT_SUPPORT);
} catch (Exception ex) {
Log.d(LOG_TAG, "transceive fail: " + ex.toString());
callback.invoke(ERR_TRANSCEIVE_FAIL);
Log.d(LOG_TAG, "transceive fail");
}
callback.invoke("transceive fail");
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -871,13 +703,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
return;
}
Log.d(LOG_TAG, "getMaxTransceiveLength not supported");
callback.invoke(ERR_API_NOT_SUPPORT);
} catch (Exception ex) {
Log.d(LOG_TAG, "getMaxTransceiveLength fail");
callback.invoke(ex.toString());
}
callback.invoke("getMaxTransceiveLength fail");
} else {
callback.invoke(ERR_NO_TECH_REQ);
callback.invoke("no tech request available");
}
}
}
@ -886,11 +717,11 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
public void cancelNdefWrite(Callback callback) {
synchronized(this) {
if (writeNdefRequest != null) {
writeNdefRequest.callback.invoke(ERR_CANCEL);
writeNdefRequest.callback.invoke("cancelled");
writeNdefRequest = null;
callback.invoke();
} else {
callback.invoke(ERR_NOT_REGISTERED);
callback.invoke("no writing request available");
}
}
}
@ -899,12 +730,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
public void requestNdefWrite(ReadableArray rnArray, ReadableMap options, Callback callback) {
synchronized(this) {
if (!isForegroundEnabled) {
callback.invoke(ERR_NOT_REGISTERED);
callback.invoke("you should requestTagEvent first");
return;
}
if (hasPendingRequest()) {
callback.invoke(ERR_MULTI_REQ);
callback.invoke("You can only issue one request at a time");
} else {
boolean format = options.getBoolean("format");
boolean formatReadOnly = options.getBoolean("formatReadOnly");
@ -929,7 +760,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
formatReadOnly
);
} catch (FormatException e) {
callback.invoke(e.toString());
callback.invoke("Incorrect ndef format");
}
}
}
@ -954,11 +785,11 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
nfcAdapter.setNdefPushMessage(msgToPush, currentActivity);
callback.invoke();
} catch (Exception ex) {
Log.d(LOG_TAG, ex.toString());
callback.invoke(ex.toString());
Log.d(LOG_TAG, "sendNdefPushMessage fail, " + ex.getMessage());
callback.invoke("sendNdefPushMessage fail");
}
} else {
callback.invoke(ERR_MULTI_REQ);
callback.invoke("please first cancel existing tech or write request");
}
}
}
@ -972,18 +803,15 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
callback.invoke(ERR_GET_ACTIVITY_FAIL);
callback.invoke("fail to get current activity");
return;
}
currentActivity.registerReceiver(mReceiver, filter);
Intent launchIntent = currentActivity.getIntent();
// we consider the launching intent to be background
bgTag = parseNfcIntent(launchIntent);
callback.invoke();
} else {
Log.d(LOG_TAG, "not support in this device");
callback.invoke(ERR_NO_NFC_SUPPORT);
callback.invoke("no nfc support");
}
}
@ -992,7 +820,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
Log.d(LOG_TAG, "isSupported");
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
callback.invoke(ERR_GET_ACTIVITY_FAIL);
callback.invoke("fail to get current activity");
return;
}
@ -1030,23 +858,19 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
Log.d(LOG_TAG, "goToNfcSetting");
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
callback.invoke(ERR_GET_ACTIVITY_FAIL);
callback.invoke("fail to get current activity");
return;
}
try {
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
callback.invoke(null, true);
} catch (Exception ex) {
callback.invoke(null, false);
}
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
callback.invoke();
}
@ReactMethod
public void getLaunchTagEvent(Callback callback) {
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
callback.invoke(ERR_GET_ACTIVITY_FAIL);
callback.invoke("fail to get current activity");
return;
}
@ -1055,24 +879,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke(null, nfcTag);
}
@ReactMethod
public void getBackgroundTag(Callback callback) {
callback.invoke(null, bgTag);
}
@ReactMethod
public void clearBackgroundTag(Callback callback) {
bgTag = null;
callback.invoke();
}
@ReactMethod
private void registerTagEvent(ReadableMap options, Callback callback) {
isReaderModeEnabled = options.getBoolean("isReaderModeEnabled");
readerModeFlags = options.getInt("readerModeFlags");
readerModeDelay = options.getInt("readerModeDelay");
this.isReaderModeEnabled = options.getBoolean("isReaderModeEnabled");
this.readerModeFlags = options.getInt("readerModeFlags");
Log.d(LOG_TAG, "registerTagEvent");
Log.d(LOG_TAG, "registerTag");
isForegroundEnabled = true;
// capture all mime-based dispatch NDEF
@ -1099,36 +911,21 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
@ReactMethod
private void unregisterTagEvent(Callback callback) {
Log.d(LOG_TAG, "unregisterTagEvent");
Log.d(LOG_TAG, "registerTag");
isForegroundEnabled = false;
intentFilters.clear();
if (isResumed) {
enableDisableForegroundDispatch(false);
}
intentFilters.clear();
isForegroundEnabled = false;
isReaderModeEnabled = false;
readerModeFlags = 0;
readerModeDelay = 0;
callback.invoke();
}
@ReactMethod
private void hasTagEventRegistration(Callback callback) {
Log.d(LOG_TAG, "isSessionAvailable: " + isForegroundEnabled);
Log.d(LOG_TAG, "isSessionAvailable");
callback.invoke(null, isForegroundEnabled);
}
@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
@Override
public void onHostResume() {
Log.d(LOG_TAG, "onResume");
@ -1163,36 +960,25 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
}
if (enable) {
Log.i(LOG_TAG, "enableReaderMode: " + readerModeFlags);
Log.i(LOG_TAG, "enableReaderMode");
Bundle readerModeExtras = new Bundle();
readerModeExtras.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, readerModeDelay * 1000);
readerModeExtras.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 10000);
nfcAdapter.enableReaderMode(currentActivity, new NfcAdapter.ReaderCallback() {
@Override
public void onTagDiscovered(Tag tag) {
synchronized (this) {
manager.tag = tag;
Log.d(LOG_TAG, "readerMode onTagDiscovered");
WritableMap nfcTag = null;
// if the tag contains NDEF, we want to report the content
if (Arrays.asList(tag.getTechList()).contains(Ndef.class.getName())) {
Ndef ndef = Ndef.get(tag);
nfcTag = ndef2React(ndef, new NdefMessage[] { ndef.getCachedNdefMessage() });
} else {
nfcTag = tag2React(tag);
}
manager.tag = tag;
Log.d(LOG_TAG, "readerMode onTagDiscovered");
WritableMap nfcTag = null;
// if the tag contains NDEF, we want to report the content
if (Arrays.asList(tag.getTechList()).contains(Ndef.class.getName())) {
Ndef ndef = Ndef.get(tag);
nfcTag = ndef2React(ndef, new NdefMessage[] { ndef.getCachedNdefMessage() });
} else {
nfcTag = tag2React(tag);
}
if (nfcTag != null) {
sendEvent("NfcManagerDiscoverTag", nfcTag);
if (techRequest!= null && !techRequest.isConnected()) {
boolean result = techRequest.connect(tag);
if (result) {
techRequest.getPendingCallback().invoke(null, techRequest.getTechType());
} else {
// this indicates that we get a NFC tag, but none of the user required tech is matched
techRequest.getPendingCallback().invoke(null, null);
}
}
}
if (nfcTag != null) {
sendEvent("NfcManagerDiscoverTag", nfcTag);
}
}
}, readerModeFlags, readerModeExtras);
@ -1217,12 +1003,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
Activity activity = getCurrentActivity();
Intent intent = new Intent(activity, activity.getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
int flag = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flag = PendingIntent.FLAG_MUTABLE;
}
return PendingIntent.getActivity(activity, 0, intent, flag);
return PendingIntent.getActivity(activity, 0, intent, 0);
}
private IntentFilter[] getIntentFilters() {
@ -1296,16 +1077,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
Log.d(LOG_TAG, "onNewIntent " + intent);
WritableMap nfcTag = parseNfcIntent(intent);
if (nfcTag != null) {
if (isForegroundEnabled) {
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Intent intentNew=new Intent("TagFound");
intentNew.putExtra("tag",tagFromIntent);
context.sendBroadcast(intentNew);
sendEvent("NfcManagerDiscoverTag", nfcTag);
} else {
sendEvent("NfcManagerDiscoverBackgroundTag", nfcTag);
bgTag = nfcTag;
}
sendEvent("NfcManagerDiscoverTag", nfcTag);
}
}
@ -1337,8 +1109,8 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
if (result) {
techRequest.getPendingCallback().invoke(null, techRequest.getTechType());
} else {
// this indicates that we get a NFC tag, but none of the user required tech is matched
techRequest.getPendingCallback().invoke(null, null);
techRequest.getPendingCallback().invoke("fail to connect tag");
techRequest = null;
}
}
@ -1422,7 +1194,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
Log.d(LOG_TAG, "ready to writeNdef");
NdefFormatable formatable = NdefFormatable.get(tag);
if (formatable == null) {
callback.invoke(ERR_API_NOT_SUPPORT);
callback.invoke("fail to apply ndef formatable tech");
} else {
Log.d(LOG_TAG, "ready to format ndef, seriously");
formatable.connect();
@ -1434,14 +1206,14 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke();
}
} catch (Exception ex) {
callback.invoke(ex.toString());
callback.invoke("writeNdef fail: " + ex.getMessage());
}
} else {
try {
Log.d(LOG_TAG, "ready to writeNdef");
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
callback.invoke(ERR_API_NOT_SUPPORT);
callback.invoke("fail to apply ndef tech");
} else if (!ndef.isWritable()) {
callback.invoke("tag is not writeable");
} else if (ndef.getMaxSize() < message.toByteArray().length) {
@ -1453,7 +1225,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke();
}
} catch (Exception ex) {
callback.invoke(ex.toString());
callback.invoke("writeNdef fail: " + ex.getMessage());
}
}
}

View File

@ -11,7 +11,6 @@ import android.nfc.tech.NfcV;
import android.nfc.tech.NfcF;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.MifareUltralight;
import android.nfc.tech.NdefFormatable;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.facebook.react.bridge.*;
@ -42,10 +41,6 @@ class TagTechnologyRequest {
return mTech;
}
Tag getTagHandle() {
return mTag;
}
boolean isConnected() {
return mTech != null;
}
@ -56,8 +51,6 @@ class TagTechnologyRequest {
return false;
}
mTag = tag;
for (int i = 0; i < mTechTypes.size(); i++) {
String techType = (String)mTechTypes.get(i);
@ -77,8 +70,6 @@ class TagTechnologyRequest {
mTech = MifareClassic.get(tag);
} else if (techType.equals("MifareUltralight")) {
mTech = MifareUltralight.get(tag);
} else if (techType.equals("NdefFormatable")) {
mTech = NdefFormatable.get(tag);
}
if (mTech == null) {
@ -89,6 +80,7 @@ class TagTechnologyRequest {
Log.d(LOG_TAG, "connect to " + techType);
mTech.connect();
mTechType = techType;
mTag = tag;
return true;
} catch (Exception ex) {
Log.d(LOG_TAG, "fail to connect tech");
@ -98,6 +90,7 @@ class TagTechnologyRequest {
// not connected, restore to default
mTech = null;
mTechType = null;
mTag = null;
return false;
}

View File

@ -39,9 +39,9 @@ public class Util {
// mTag.getTagService(); of the Ndef object sometimes returns null
// see http://issues.mroland.at/index.php?do=details&task_id=47
try {
json.put("canMakeReadOnly", ndef.canMakeReadOnly());
json.put("canMakeReadOnly", ndef.canMakeReadOnly());
} catch (NullPointerException e) {
json.put("canMakeReadOnly", JSONObject.NULL);
json.put("canMakeReadOnly", JSONObject.NULL);
}
} catch (JSONException e) {
Log.e(TAG, "Failed to convert ndef into json: " + ndef.toString(), e);
@ -97,23 +97,22 @@ public class Util {
static JSONArray byteArrayToJSON(byte[] bytes) {
JSONArray json = new JSONArray();
for (byte aByte : bytes) {
int v = aByte & 0xFF;
json.put(v);
json.put(aByte);
}
return json;
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
return new String(hexChars);
}
static byte[] jsonToByteArray(JSONArray json) throws JSONException {

View File

@ -1,105 +0,0 @@
const {
AndroidConfig,
withInfoPlist,
withEntitlementsPlist,
} = require('@expo/config-plugins');
const NFC_READER = 'Interact with nearby NFC devices';
function withIosPermission(c, props = {}) {
const {nfcPermission} = props;
return withInfoPlist(c, (config) => {
// https://developer.apple.com/documentation/bundleresources/information_property_list/nfcreaderusagedescription?language=objc
config.modResults.NFCReaderUsageDescription =
nfcPermission ||
config.modResults.NFCReaderUsageDescription ||
NFC_READER;
return config;
});
}
function addValuesToArray(obj, key, values) {
if (!Array.isArray(values) || !values.length) {
return obj;
}
if (!Array.isArray(obj[key])) {
obj[key] = [];
}
// Add the required values
obj[key].push(...values);
// Remove duplicates
obj[key] = [...new Set(obj[key])];
// Prevent adding empty arrays to Info.plist or *.entitlements
if (!obj[key].length) {
delete obj[key];
}
return obj;
}
function withIosNfcEntitlement(c) {
return withEntitlementsPlist(c, (config) => {
// Add the required formats
config.modResults = addValuesToArray(
config.modResults,
'com.apple.developer.nfc.readersession.formats',
['NDEF', 'TAG'],
);
return config;
});
}
function withIosNfcSelectIdentifiers(c, {selectIdentifiers}) {
return withInfoPlist(c, (config) => {
// Add the user defined identifiers
config.modResults = addValuesToArray(
config.modResults,
// https://developer.apple.com/documentation/bundleresources/information_property_list/select-identifiers
'com.apple.developer.nfc.readersession.iso7816.select-identifiers',
selectIdentifiers || [],
);
return config;
});
}
function withIosNfcSystemCodes(c, {systemCodes}) {
return withInfoPlist(c, (config) => {
// Add the user defined identifiers
config.modResults = addValuesToArray(
config.modResults,
// https://developer.apple.com/documentation/bundleresources/information_property_list/systemcodes
'com.apple.developer.nfc.readersession.felica.systemcodes',
systemCodes || [],
);
return config;
});
}
function withNfc(config, props = {}) {
const {nfcPermission, selectIdentifiers, systemCodes} = props;
config = withIosNfcEntitlement(config);
config = withIosNfcSelectIdentifiers(config, {selectIdentifiers});
config = withIosNfcSystemCodes(config, {systemCodes});
// We start to support Android 12 from v3.11.1, and you will need to update compileSdkVersion to 31,
// otherwise the build will fail:
config = AndroidConfig.Version.withBuildScriptExtMinimumVersion(config, {
name: 'compileSdkVersion',
minVersion: 31,
});
if (nfcPermission !== false) {
config = withIosPermission(config, props);
config = AndroidConfig.Permissions.withPermissions(config, [
'android.permission.NFC',
]);
}
return config;
}
module.exports = withNfc;

View File

@ -0,0 +1,322 @@
import React, { Component } from 'react';
import {
View,
Text,
Platform,
TouchableOpacity,
TextInput,
ScrollView,
} from 'react-native';
import NfcManager, { ByteParser, NfcTech } from 'react-native-nfc-manager';
const KeyTypes = ['A', 'B'];
class App extends Component {
constructor(props) {
super(props);
this.state = {
supported: false,
enabled: false,
isDetecting: false,
mode: 'read',
keyAorB: KeyTypes[1], // 'B'
keyToUse: 'FFFFFFFFFFFF',
sector: 0,
tag: null,
sectorCount: null,
blocksInSector: null,
parsedText: null,
firstBlockInSector: null,
textToWrite: 'Hello, world!',
};
}
componentDidMount() {
NfcManager.isSupported(NfcTech.MifareClassic).then(supported => {
this.setState({ supported });
if (supported) {
this._startNfc();
}
});
}
componentWillUnmount() {
if (this._stateChangedSubscription) {
this._stateChangedSubscription.remove();
}
}
render() {
let {
supported,
enabled,
isDetecting,
keyAorB,
keyToUse,
sector,
tag,
sectorCount,
blocksInSector,
parsedText,
firstBlockInSector,
textToWrite,
} = this.state;
return (
<ScrollView style={{ flex: 1 }}>
{Platform.OS === 'ios' && <View style={{ height: 60 }} />}
<View
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
>
<Text>{`Is MifareClassic supported ? ${supported}`}</Text>
<Text>{`Is NFC enabled (Android only)? ${enabled}`}</Text>
{!isDetecting && (
<TouchableOpacity
style={{ margin: 10 }}
onPress={() => this._startDetection()}
>
<Text
style={{ color: 'blue', textAlign: 'center', fontSize: 20 }}
>
{`CLICK TO START DETECTING ${this.state.mode === 'read' ? 'READ' : 'WRITE'}`}
</Text>
</TouchableOpacity>
)}
{isDetecting && (
<TouchableOpacity
style={{ margin: 10 }}
onPress={() => this._stopDetection()}
>
<Text style={{ color: 'red', textAlign: 'center', fontSize: 20 }}>
{`CLICK TO STOP DETECTING ${this.state.mode === 'read' ? 'READ' : 'WRITE'}`}
</Text>
</TouchableOpacity>
)}
{
<View
style={{ padding: 10, marginTop: 20, backgroundColor: '#e0e0e0' }}
>
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
style={[{ flex: 1, alignItems: 'center' }, this.state.mode === 'read' ? {backgroundColor: '#cc0000'} : {backgroundColor: '#d0d0d0'}]}
onPress={() => this.setState({
tag: null,
mode: 'read',
sectorCount: null,
blocksInSector: null,
parsedText: null,
firstBlockInSector: null,
})}
>
<Text>READ</Text>
</TouchableOpacity>
<TouchableOpacity
style={[{ flex: 1, alignItems: 'center' }, this.state.mode !== 'read' ? {backgroundColor: '#cc0000'} : {backgroundColor: '#d0d0d0'}]}
onPress={() => this.setState({
tag: null,
mode: 'write',
sectorCount: null,
blocksInSector: null,
parsedText: null,
firstBlockInSector: null,
})}
>
<Text>WRITE</Text>
</TouchableOpacity>
</View>
<View style={{ flexDirection: 'row', marginTop: 10 }}>
<Text style={{ marginRight: 33 }}>Key to use:</Text>
{KeyTypes.map(key => (
<TouchableOpacity
key={key}
style={{ marginRight: 10 }}
onPress={() => this.setState({ keyAorB: key })}
>
<Text style={{ color: keyAorB === key ? 'blue' : '#aaa' }}>
Use key {key}
</Text>
</TouchableOpacity>
))}
</View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ marginRight: 35 }}>Key (hex):</Text>
<TextInput
style={{ width: 200 }}
value={keyToUse}
onChangeText={keyToUse => this.setState({ keyToUse })}
/>
</View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ marginRight: 10 }}>Sector (0-15):</Text>
<TextInput
style={{ width: 200 }}
value={sector.toString(10)}
onChangeText={sector => this.setState({ sector: sector })}
/>
</View>
{this.state.mode !== 'read' && (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ marginRight: 15 }}>Text to write:</Text>
<TextInput
style={{ width: 200 }}
value={textToWrite}
onChangeText={textToWrite => this.setState({ textToWrite })}
/>
</View>
)}
</View>
}
<View
style={{
alignItems: 'center',
justifyContent: 'center',
padding: 20,
marginTop: 20,
}}
>
<Text>{`Original tag content:`}</Text>
<Text style={{ marginTop: 5, color: 'grey' }}>{`${
tag ? `${JSON.stringify(tag)} (${sectorCount} sectors)` : '---'
}`}</Text>
{parsedText && (
<Text
style={{ marginTop: 5 }}
>{`Parsed Text:\n${parsedText}`}</Text>
)}
{firstBlockInSector && (
<Text
style={{ marginTop: 5 }}
>{`First block in sector:\n${firstBlockInSector} [${blocksInSector} blocks]`}</Text>
)}
</View>
<TouchableOpacity
style={{ marginTop: 20, alignItems: 'center' }}
onPress={this._clearMessages}
>
<Text style={{ color: 'blue' }}>Clear above message</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
}
_startDetection = () => {
const cleanUp = () => {
this.setState({ isDetecting: false });
NfcManager.closeTechnology();
NfcManager.unregisterTagEvent();
};
const read = () => {
return NfcManager.mifareClassicGetBlockCountInSector(parseInt(this.state.sector))
.then(blocksInSector => {
this.setState({ blocksInSector });
})
.then(() =>
NfcManager.mifareClassicReadSector(parseInt(this.state.sector)),
)
.then(tag => {
let parsedText = ByteParser.byteToHexString(tag);
this.setState({ parsedText });
})
.then(() =>
NfcManager.mifareClassicSectorToBlock(parseInt(this.state.sector)),
)
.then(block => NfcManager.mifareClassicReadBlock(block))
.then(data => {
const parsedText = ByteParser.byteToString(data);
this.setState({ firstBlockInSector: parsedText });
})
};
const write = () => {
return NfcManager.mifareClassicSectorToBlock(parseInt(this.state.sector))
.then(block => {
// Create 1 block
let data = [];
for (let i = 0; i < NfcManager.MIFARE_BLOCK_SIZE; i++) {
data.push(0);
}
// Fill the block with our text, but don't exceed the block size
for (let i = 0; i < this.state.textToWrite.length && i < NfcManager.MIFARE_BLOCK_SIZE; i++) {
data[i] = parseInt(this.state.textToWrite.charCodeAt(i));
}
return NfcManager.mifareClassicWriteBlock(block, data);
})
.then(read)
};
this.setState({ isDetecting: true });
NfcManager.registerTagEvent(tag => console.log(tag))
.then(() => NfcManager.requestTechnology(NfcTech.MifareClassic))
.then(() => NfcManager.getTag())
.then(tag => {
this.setState({ tag });
return NfcManager.mifareClassicGetSectorCount();
})
.then(sectorCount => {
this.setState({ sectorCount });
})
.then(() => {
let sector = parseInt(this.state.sector);
if (isNaN(sector)) {
this.setState({ sector: '0' });
sector = 0;
}
// Convert the key to a UInt8Array
const key = [];
for (let i = 0; i < this.state.keyToUse.length - 1; i += 2) {
key.push(parseInt(this.state.keyToUse.substring(i, i + 2), 16));
}
if (this.state.keyAorB === KeyTypes[0]) {
return NfcManager.mifareClassicAuthenticateA(sector, key);
} else {
return NfcManager.mifareClassicAuthenticateB(sector, key);
}
})
.then(() => { return this.state.mode === 'read' ? read() : write() })
.then(cleanUp)
.catch(err => {
console.warn(err);
cleanUp();
});
};
_stopDetection = () => {
NfcManager.cancelTechnologyRequest()
.then(() => this.setState({ isDetecting: false }))
.catch(err => console.warn(err));
};
_startNfc = () => {
NfcManager.start()
.then(() => NfcManager.isEnabled())
.then(enabled => this.setState({ enabled }))
.catch(err => {
console.warn(err);
this.setState({ enabled: false });
});
};
_clearMessages = () => {
this.setState({
tag: null,
sectorCount: null,
blocksInSector: null,
parsedText: null,
firstBlockInSector: null,
});
};
}
export default App;

View File

@ -0,0 +1,160 @@
import React, {Component} from 'react'
import {
View,
Text,
TouchableOpacity,
ScrollView,
TextInput,
} from 'react-native';
import NfcManager, {NdefParser, NfcTech} from 'react-native-nfc-manager';
function strToBytes(str) {
let result = [];
for (let i = 0; i < str.length; i++) {
result.push(str.charCodeAt(i));
}
return result;
}
function buildTextPayload(valueToWrite) {
const textBytes = strToBytes(valueToWrite);
// in this example. we always use `en`
const headerBytes = [0xD1, 0x01, (textBytes.length + 3), 0x54, 0x02, 0x65, 0x6e];
return [...headerBytes, ...textBytes];
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
supported: false,
enabled: false,
isTestRunning: false,
text: 'hi, nfc!',
parsedText: null,
tag: null,
}
}
componentDidMount() {
NfcManager.isSupported()
.then(supported => {
this.setState({ supported });
if (supported) {
this._startNfc();
}
})
}
render() {
let { supported, enabled, tag, text, parsedText, isTestRunning} = this.state;
return (
<ScrollView style={{flex: 1}}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
<Text>{`Is NFC supported ? ${supported}`}</Text>
<Text>{`Is NFC enabled (Android only)? ${enabled}`}</Text>
{
<View style={{padding: 20, marginTop: 20, backgroundColor: '#f0f0f0'}}>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text>Text to write:</Text>
<TextInput
value={text}
style={{marginLeft: 10, flex: 1}}
onChangeText={text => this.setState({text})}
/>
</View>
{!isTestRunning && (
<TouchableOpacity
style={{ margin: 10 }}
onPress={() => this._runTest(text)}
>
<Text style={{ color: 'blue', textAlign: 'center', fontSize: 20 }}>CLICK TO RUN TEST</Text>
</TouchableOpacity>
)}
{isTestRunning && (
<TouchableOpacity
style={{ margin: 10 }}
onPress={() => this._cancelTest()}
>
<Text style={{ color: 'red', textAlign: 'center', fontSize: 20 }}>CLICK TO CANCEL TEST</Text>
</TouchableOpacity>
)}
<Text style={{color: 'grey', textAlign: 'center'}}>
{`When the tag is available, this demo will:\n1. read original NdefMessage from the tag\n2. write a NdefMessage contains a RTD_TEXT into it `}
</Text>
</View>
}
<View style={{alignItems: 'center', justifyContent: 'center', padding: 20, marginTop: 20}}>
<Text >{`Original tag content:`}</Text>
<Text style={{marginTop: 5, color: 'grey'}}>{`${tag ? JSON.stringify(tag) : '---'}`}</Text>
{ parsedText && <Text style={{ marginTop: 5, }}>{`(Parsed Text: ${parsedText})`}</Text>}
</View>
<TouchableOpacity style={{ marginTop: 20, alignItems: 'center' }} onPress={this._clearMessages}>
<Text style={{color: 'blue'}}>Clear above message</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
_runTest = textToWrite => {
const cleanUp = () => {
this.setState({isTestRunning: false});
NfcManager.closeTechnology()
NfcManager.unregisterTagEvent();
}
const parseText = (tag) => {
if (tag.ndefMessage) {
return NdefParser.parseText(tag.ndefMessage[0]);
}
return null;
}
this.setState({isTestRunning: true});
NfcManager.registerTagEvent(tag => console.log(tag))
.then(() => NfcManager.requestTechnology(NfcTech.Ndef))
.then(() => NfcManager.getTag())
.then(tag => {
console.log(JSON.stringify(tag));
})
.then(() => NfcManager.getNdefMessage())
.then(tag => {
let parsedText = parseText(tag);
this.setState({tag, parsedText})
})
.then(() => NfcManager.writeNdefMessage(buildTextPayload(textToWrite)))
.then(cleanUp)
.catch(err => {
console.warn(err);
cleanUp();
})
}
_cancelTest = () => {
NfcManager.cancelTechnologyRequest()
.catch(err => console.warn(err));
}
_startNfc = () => {
NfcManager.start()
.then(() => NfcManager.isEnabled())
.then(enabled => this.setState({enabled}))
.catch(err => {
console.warn(err);
this.setState({enabled: false})
})
}
_clearMessages = () => {
this.setState({tag: null, parsedText: null});
}
}
export default App;

341
example/App.js Normal file
View File

@ -0,0 +1,341 @@
import React, { Component } from 'react';
import {
View,
Text,
Button,
Platform,
TouchableOpacity,
Linking,
TextInput,
ScrollView,
} from 'react-native';
import NfcManager, {Ndef} from 'react-native-nfc-manager';
const RtdType = {
URL: 0,
TEXT: 1,
};
function buildUrlPayload(valueToWrite) {
return Ndef.encodeMessage([
Ndef.uriRecord(valueToWrite),
]);
}
function buildTextPayload(valueToWrite) {
return Ndef.encodeMessage([
Ndef.textRecord(valueToWrite),
]);
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
supported: true,
enabled: false,
isWriting: false,
urlToWrite: 'https://www.google.com',
rtdType: RtdType.URL,
parsedText: null,
tag: {},
}
}
componentDidMount() {
NfcManager.isSupported()
.then(supported => {
this.setState({ supported });
if (supported) {
this._startNfc();
}
})
}
componentWillUnmount() {
if (this._stateChangedSubscription) {
this._stateChangedSubscription.remove();
}
}
render() {
let { supported, enabled, tag, isWriting, urlToWrite, parsedText, rtdType } = this.state;
return (
<ScrollView style={{flex: 1}}>
{ Platform.OS === 'ios' && <View style={{ height: 60 }} /> }
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{`Is NFC supported ? ${supported}`}</Text>
<Text>{`Is NFC enabled (Android only)? ${enabled}`}</Text>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._startDetection}>
<Text style={{ color: 'blue' }}>Start Tag Detection</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._stopDetection}>
<Text style={{ color: 'red' }}>Stop Tag Detection</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._clearMessages}>
<Text>Clear</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._goToNfcSetting}>
<Text >(android) Go to NFC setting</Text>
</TouchableOpacity>
{
<View style={{padding: 10, marginTop: 20, backgroundColor: '#e0e0e0'}}>
<Text>(android) Write NDEF Test</Text>
<View style={{flexDirection: 'row', marginTop: 10}}>
<Text style={{marginRight: 15}}>Types:</Text>
{
Object.keys(RtdType).map(
key => (
<TouchableOpacity
key={key}
style={{marginRight: 10}}
onPress={() => this.setState({rtdType: RtdType[key]})}
>
<Text style={{color: rtdType === RtdType[key] ? 'blue' : '#aaa'}}>
{key}
</Text>
</TouchableOpacity>
)
)
}
</View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TextInput
style={{width: 200}}
value={urlToWrite}
onChangeText={urlToWrite => this.setState({ urlToWrite })}
/>
</View>
<TouchableOpacity
style={{ marginTop: 20, borderWidth: 1, borderColor: 'blue', padding: 10 }}
onPress={isWriting ? this._cancelNdefWrite : this._requestNdefWrite}>
<Text style={{color: 'blue'}}>{`(android) ${isWriting ? 'Cancel' : 'Write NDEF'}`}</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ marginTop: 20, borderWidth: 1, borderColor: 'blue', padding: 10 }}
onPress={isWriting ? this._cancelNdefWrite : this._requestFormat}>
<Text style={{color: 'blue'}}>{`(android) ${isWriting ? 'Cancel' : 'Format'}`}</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ marginTop: 20, borderWidth: 1, borderColor: 'blue', padding: 10 }}
onPress={isWriting ? this._cancelAndroidBeam : this._requestAndroidBeam}>
<Text style={{color: 'blue'}}>{`${isWriting ? 'Cancel ' : ''}Android Beam`}</Text>
</TouchableOpacity>
</View>
}
<Text style={{ marginTop: 20 }}>{`Current tag JSON: ${JSON.stringify(tag)}`}</Text>
{ parsedText && <Text style={{ marginTop: 10, marginBottom: 20, fontSize: 18 }}>{`Parsed Text: ${parsedText}`}</Text>}
</View>
</ScrollView>
)
}
_requestFormat = () => {
let {isWriting} = this.state;
if (isWriting) {
return;
}
this.setState({isWriting: true});
NfcManager.requestNdefWrite(null, {format: true})
.then(() => console.log('format completed'))
.catch(err => console.warn(err))
.then(() => this.setState({isWriting: false}));
}
_requestNdefWrite = () => {
let {isWriting, urlToWrite, rtdType} = this.state;
if (isWriting) {
return;
}
let bytes;
if (rtdType === RtdType.URL) {
bytes = buildUrlPayload(urlToWrite);
} else if (rtdType === RtdType.TEXT) {
bytes = buildTextPayload(urlToWrite);
}
this.setState({isWriting: true});
NfcManager.requestNdefWrite(bytes)
.then(() => console.log('write completed'))
.catch(err => console.warn(err))
.then(() => this.setState({isWriting: false}));
}
_cancelNdefWrite = () => {
this.setState({isWriting: false});
NfcManager.cancelNdefWrite()
.then(() => console.log('write cancelled'))
.catch(err => console.warn(err))
}
_requestAndroidBeam = () => {
let {isWriting, urlToWrite, rtdType} = this.state;
if (isWriting) {
return;
}
let bytes;
if (rtdType === RtdType.URL) {
bytes = buildUrlPayload(urlToWrite);
} else if (rtdType === RtdType.TEXT) {
bytes = buildTextPayload(urlToWrite);
}
this.setState({isWriting: true});
NfcManager.setNdefPushMessage(bytes)
.then(() => console.log('beam request completed'))
.catch(err => console.warn(err))
}
_cancelAndroidBeam = () => {
this.setState({isWriting: false});
NfcManager.setNdefPushMessage(null)
.then(() => console.log('beam cancelled'))
.catch(err => console.warn(err))
}
_startNfc() {
NfcManager.start({
onSessionClosedIOS: () => {
console.log('ios session closed');
}
})
.then(result => {
console.log('start OK', result);
})
.catch(error => {
console.warn('start fail', error);
this.setState({supported: false});
})
if (Platform.OS === 'android') {
NfcManager.getLaunchTagEvent()
.then(tag => {
console.log('launch tag', tag);
if (tag) {
this.setState({ tag });
}
})
.catch(err => {
console.log(err);
})
NfcManager.isEnabled()
.then(enabled => {
this.setState({ enabled });
})
.catch(err => {
console.log(err);
})
NfcManager.onStateChanged(
event => {
if (event.state === 'on') {
this.setState({enabled: true});
} else if (event.state === 'off') {
this.setState({enabled: false});
} else if (event.state === 'turning_on') {
// do whatever you want
} else if (event.state === 'turning_off') {
// do whatever you want
}
}
)
.then(sub => {
this._stateChangedSubscription = sub;
// remember to call this._stateChangedSubscription.remove()
// when you don't want to listen to this anymore
})
.catch(err => {
console.warn(err);
})
}
}
_onTagDiscovered = tag => {
console.log('Tag Discovered', tag);
this.setState({ tag });
let url = this._parseUri(tag);
if (url) {
Linking.openURL(url)
.catch(err => {
console.warn(err);
})
}
let text = this._parseText(tag);
this.setState({parsedText: text});
}
_startDetection = () => {
NfcManager.registerTagEvent(this._onTagDiscovered)
.then(result => {
console.log('registerTagEvent OK', result)
})
.catch(error => {
console.warn('registerTagEvent fail', error)
})
}
_stopDetection = () => {
NfcManager.unregisterTagEvent()
.then(result => {
console.log('unregisterTagEvent OK', result)
})
.catch(error => {
console.warn('unregisterTagEvent fail', error)
})
}
_clearMessages = () => {
this.setState({tag: null});
}
_goToNfcSetting = () => {
if (Platform.OS === 'android') {
NfcManager.goToNfcSetting()
.then(result => {
console.log('goToNfcSetting OK', result)
})
.catch(error => {
console.warn('goToNfcSetting fail', error)
})
}
}
_parseUri = (tag) => {
try {
if (Ndef.isType(tag.ndefMessage[0], Ndef.TNF_WELL_KNOWN, Ndef.RTD_URI)) {
return Ndef.uri.decodePayload(tag.ndefMessage[0].payload);
}
} catch (e) {
console.log(e);
}
return null;
}
_parseText = (tag) => {
try {
if (Ndef.isType(tag.ndefMessage[0], Ndef.TNF_WELL_KNOWN, Ndef.RTD_TEXT)) {
return Ndef.text.decodePayload(tag.ndefMessage[0].payload);
}
} catch (e) {
console.log(e);
}
return null;
}
}
export default App;

64
example/AppMultiTech.js Normal file
View File

@ -0,0 +1,64 @@
import React from 'react';
import {
View,
Text,
Button,
Platform,
TouchableOpacity,
Linking,
TextInput,
ScrollView,
} from 'react-native';
import NfcManager, {NfcTech} from '../NfcManager';
class AppMultiTech extends React.Component {
componentDidMount() {
NfcManager.start();
}
componentWillUnmount() {
this._cleanUp();
}
render() {
return (
<View style={{padding: 20}}>
<Text>NFC Multi-tech Demo</Text>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._testMultiTech}
>
<Text>Test</Text>
</TouchableOpacity>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._cleanUp}
>
<Text>Cancel Test</Text>
</TouchableOpacity>
</View>
)
}
_cleanUp = () => {
NfcManager.cancelTechnologyRequest().catch(() => 0);
NfcManager.unregisterTagEvent().catch(() => 0);
}
_testMultiTech = async () => {
try {
await NfcManager.registerTagEvent()
let resp = await NfcManager.requestTechnology([NfcTech.IsoDep, NfcTech.Ndef]);
console.warn(resp);
let tag = await NfcManager.getTag();
console.warn(tag);
this._cleanUp();
} catch (ex) {
this._cleanUp();
}
}
}
export default AppMultiTech;

57
example/AppV2.js Normal file
View File

@ -0,0 +1,57 @@
import React from 'react'
import {
View, Text, TouchableOpacity
} from 'react-native'
import NfcManager, {NfcEvents} from '../NfcManager';
class AppV2 extends React.Component {
componentDidMount() {
NfcManager.start();
NfcManager.setEventListener(NfcEvents.DiscoverTag, tag => {
console.warn('tag', tag);
NfcManager.setAlertMessageIOS('I got your tag!');
NfcManager.unregisterTagEvent().catch(() => 0);
});
}
componentWillUnmount() {
NfcManager.setEventListener(NfcEvents.DiscoverTag, null);
NfcManager.unregisterTagEvent().catch(() => 0);
}
render() {
return (
<View style={{padding: 20}}>
<Text>NFC Demo</Text>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._test}
>
<Text>Test</Text>
</TouchableOpacity>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._cancel}
>
<Text>Cancel Test</Text>
</TouchableOpacity>
</View>
)
}
_cancel = () => {
NfcManager.unregisterTagEvent().catch(() => 0);
}
_test = async () => {
try {
await NfcManager.registerTagEvent();
} catch (ex) {
console.warn('ex', ex);
NfcManager.unregisterTagEvent().catch(() => 0);
}
}
}
export default AppV2

71
example/AppV2Mifare.js Normal file
View File

@ -0,0 +1,71 @@
import React from 'react';
import {
View,
Text,
TouchableOpacity,
Platform,
} from 'react-native';
import NfcManager, {NfcTech} from '../NfcManager';
class AppV2Mifare extends React.Component {
componentDidMount() {
NfcManager.start();
}
componentWillUnmount() {
this._cleanUp();
}
render() {
return (
<View style={{padding: 20}}>
<Text>NFC Demo</Text>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._test}
>
<Text>Test</Text>
</TouchableOpacity>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._cleanUp}
>
<Text>Cancel Test</Text>
</TouchableOpacity>
</View>
)
}
_cleanUp = () => {
NfcManager.cancelTechnologyRequest().catch(() => 0);
}
_test = async () => {
try {
let tech = Platform.OS === 'ios' ? NfcTech.MifareIOS : NfcTech.NfcA;
let resp = await NfcManager.requestTechnology(tech, {
alertMessage: 'Ready to do some custom Mifare cmd!'
});
console.warn(resp);
// the NFC uid can be found in tag.id
let tag = await NfcManager.getTag();
console.warn(tag);
if (Platform.OS === 'ios') {
resp = await NfcManager.sendMifareCommandIOS([0x30, 0x00]);
} else {
resp = await NfcManager.transceive([0x30, 0x00]);
}
console.warn(resp);
this._cleanUp();
} catch (ex) {
console.warn('ex', ex);
this._cleanUp();
}
}
}
export default AppV2Mifare;

69
example/AppV2Ndef.js Normal file
View File

@ -0,0 +1,69 @@
import React from 'react';
import {
View,
Text,
TouchableOpacity,
} from 'react-native';
import NfcManager, {Ndef, NfcTech} from '../NfcManager';
function buildUrlPayload(valueToWrite) {
return Ndef.encodeMessage([
Ndef.uriRecord(valueToWrite),
]);
}
class AppV2Ndef extends React.Component {
componentDidMount() {
NfcManager.start();
}
componentWillUnmount() {
this._cleanUp();
}
render() {
return (
<View style={{padding: 20}}>
<Text>NFC Demo</Text>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._testNdef}
>
<Text>Test Ndef</Text>
</TouchableOpacity>
<TouchableOpacity
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
onPress={this._cleanUp}
>
<Text>Cancel Test</Text>
</TouchableOpacity>
</View>
)
}
_cleanUp = () => {
NfcManager.cancelTechnologyRequest().catch(() => 0);
}
_testNdef = async () => {
try {
let resp = await NfcManager.requestTechnology(NfcTech.Ndef, {
alertMessage: 'Ready to write some NFC tags!'
});
console.warn(resp);
let ndef = await NfcManager.getNdefMessage();
console.warn(ndef);
let bytes = buildUrlPayload('https://www.revteltech.com');
await NfcManager.writeNdefMessage(bytes);
console.warn('successfully write ndef');
await NfcManager.setAlertMessageIOS('I got your tag!');
this._cleanUp();
} catch (ex) {
console.warn('ex', ex);
this._cleanUp();
}
}
}
export default AppV2Ndef;

215
example/MultiNdefRecord.js Normal file
View File

@ -0,0 +1,215 @@
import React, { Component } from 'react';
import {
View,
Text,
Button,
Platform,
TouchableOpacity,
Linking,
TextInput,
ScrollView,
} from 'react-native';
import NfcManager, {Ndef} from 'react-native-nfc-manager';
class App extends Component {
constructor(props) {
super(props);
this.state = {
supported: true,
enabled: false,
isWriting: false,
tag: {},
parsed: null,
}
}
componentDidMount() {
NfcManager.isSupported()
.then(supported => {
this.setState({ supported });
if (supported) {
this._startNfc();
}
})
}
componentWillUnmount() {
if (this._stateChangedSubscription) {
this._stateChangedSubscription.remove();
}
}
render() {
let { supported, enabled, tag, isWriting, parsed } = this.state;
return (
<ScrollView style={{flex: 1}}>
{ Platform.OS === 'ios' && <View style={{ height: 60 }} /> }
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{`Is NFC supported ? ${supported}`}</Text>
<Text>{`Is NFC enabled (Android only)? ${enabled}`}</Text>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._startDetection}>
<Text style={{ color: 'blue' }}>Start Tag Detection</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._stopDetection}>
<Text style={{ color: 'red' }}>Stop Tag Detection</Text>
</TouchableOpacity>
<TouchableOpacity style={{ marginTop: 20 }} onPress={this._clearMessages}>
<Text>Clear</Text>
</TouchableOpacity>
{
<View style={{padding: 10, marginTop: 20, backgroundColor: '#e0e0e0'}}>
<Text>(android) Write NDEF Test</Text>
<TouchableOpacity
style={{ marginTop: 20, borderWidth: 1, borderColor: 'blue', padding: 10 }}
onPress={isWriting ? this._cancelNdefWrite : this._requestNdefWrite}>
<Text style={{color: 'blue'}}>{`(android) ${isWriting ? 'Cancel' : 'Write NDEF'}`}</Text>
</TouchableOpacity>
</View>
}
<Text style={{ marginTop: 20 }}>{`Current tag JSON: ${JSON.stringify(tag)}`}</Text>
{ parsed && <Text style={{ marginTop: 10, marginBottom: 20, fontSize: 18 }}>{`Parsed: ${JSON.stringify(parsed)}`}</Text>}
</View>
</ScrollView>
)
}
_requestNdefWrite = () => {
let {isWriting} = this.state;
if (isWriting) {
return;
}
let bytes = Ndef.encodeMessage([
Ndef.textRecord("hello, world"),
Ndef.uriRecord("http://nodejs.org"),
]);
this.setState({isWriting: true});
NfcManager.requestNdefWrite(bytes)
.then(() => console.log('write completed'))
.catch(err => console.warn(err))
.then(() => this.setState({isWriting: false}));
}
_cancelNdefWrite = () => {
this.setState({isWriting: false});
NfcManager.cancelNdefWrite()
.then(() => console.log('write cancelled'))
.catch(err => console.warn(err))
}
_startNfc() {
NfcManager.start({
onSessionClosedIOS: () => {
console.log('ios session closed');
}
})
.then(result => {
console.log('start OK', result);
})
.catch(error => {
console.warn('start fail', error);
this.setState({supported: false});
})
if (Platform.OS === 'android') {
NfcManager.getLaunchTagEvent()
.then(tag => {
console.log('launch tag', tag);
if (tag) {
this.setState({ tag });
}
})
.catch(err => {
console.log(err);
})
NfcManager.isEnabled()
.then(enabled => {
this.setState({ enabled });
})
.catch(err => {
console.log(err);
})
NfcManager.onStateChanged(
event => {
if (event.state === 'on') {
this.setState({enabled: true});
} else if (event.state === 'off') {
this.setState({enabled: false});
} else if (event.state === 'turning_on') {
// do whatever you want
} else if (event.state === 'turning_off') {
// do whatever you want
}
}
)
.then(sub => {
this._stateChangedSubscription = sub;
// remember to call this._stateChangedSubscription.remove()
// when you don't want to listen to this anymore
})
.catch(err => {
console.warn(err);
})
}
}
_onTagDiscovered = tag => {
console.log('Tag Discovered', tag);
this.setState({ tag });
let parsed = null;
if (tag.ndefMessage && tag.ndefMessage.length > 0) {
// ndefMessage is actually an array of NdefRecords,
// and we can iterate through each NdefRecord, decode its payload
// according to its TNF & type
const ndefRecords = tag.ndefMessage;
function decodeNdefRecord(record) {
if (Ndef.isType(record, Ndef.TNF_WELL_KNOWN, Ndef.RTD_TEXT)) {
return ['text', Ndef.text.decodePayload(record.payload)];
} else if (Ndef.isType(record, Ndef.TNF_WELL_KNOWN, Ndef.RTD_URI)) {
return ['uri', Ndef.uri.decodePayload(record.payload)];
}
return ['unknown', '---']
}
parsed = ndefRecords.map(decodeNdefRecord);
}
this.setState({parsed});
}
_startDetection = () => {
NfcManager.registerTagEvent(this._onTagDiscovered)
.then(result => {
console.log('registerTagEvent OK', result)
})
.catch(error => {
console.warn('registerTagEvent fail', error)
})
}
_stopDetection = () => {
NfcManager.unregisterTagEvent()
.then(result => {
console.log('unregisterTagEvent OK', result)
})
.catch(error => {
console.warn('unregisterTagEvent fail', error)
})
}
_clearMessages = () => {
this.setState({tag: null, parsed: null});
}
}
export default App;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

397
index.d.ts vendored
View File

@ -5,11 +5,15 @@
declare module 'react-native-nfc-manager' {
export enum NfcEvents {
DiscoverTag = 'NfcManagerDiscoverTag',
DiscoverBackgroundTag = 'NfcManagerDiscoverBackgroundTag',
SessionClosed = 'NfcManagerSessionClosed',
StateChanged = 'NfcManagerStateChanged',
}
type OnDiscoverTag = (evt: TagEvent) => void;
type OnSessionClosed = (evt: {}) => void;
type OnStateChanged = (evt: {state: string}) => void;
type OnNfcEvents = OnDiscoverTag | OnSessionClosed | OnStateChanged;
export enum NfcTech {
Ndef = 'Ndef',
NfcA = 'NfcA',
@ -20,17 +24,9 @@ declare module 'react-native-nfc-manager' {
MifareClassic = 'MifareClassic',
MifareUltralight = 'MifareUltralight',
MifareIOS = 'mifare',
Iso15693IOS = 'iso15693',
FelicaIOS = 'felica',
NdefFormatable = 'NdefFormatable',
}
export enum NdefStatus {
NotSupported = 1,
ReadWrite = 2,
ReadOnly = 3,
}
/** [ANDROID ONLY] */
export enum NfcAdapter {
FLAG_READER_NFC_A = 0x1,
FLAG_READER_NFC_B = 0x2,
@ -41,32 +37,10 @@ declare module 'react-native-nfc-manager' {
FLAG_READER_NO_PLATFORM_SOUNDS = 0x100,
}
export enum Nfc15693RequestFlagIOS {
DualSubCarriers = 1 << 0,
HighDataRate = 1 << 1,
ProtocolExtension = 1 << 3,
Select = 1 << 4,
Address = 1 << 5,
Option = 1 << 6,
CommandSpecificBit8 = 1 << 7,
}
export enum Nfc15693ResponseFlagIOS {
Error = 1 << 0,
ResponseBufferValid = 1 << 1,
FinalResponse = 1 << 2,
ProtocolExtension = 1 << 3,
BlockSecurityStatusBit5 = 1 << 4,
BlockSecurityStatusBit6 = 1 << 5,
WaitTimeExtension = 1 << 6,
}
type TNF = 0x0 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07;
export interface NdefRecord {
id?: number[];
tnf: TNF;
type: number[] | string;
tnf: number;
type: number[];
payload: any[];
}
@ -75,223 +49,99 @@ declare module 'react-native-nfc-manager' {
maxSize?: number;
type?: string;
techTypes?: string[];
id?: string;
id?: number[];
}
export interface RegisterTagEventOpts {
interface RegisterTagEventOpts {
alertMessage?: string;
invalidateAfterFirstRead?: boolean;
isReaderModeEnabled?: boolean;
readerModeFlags?: number;
readerModeDelay?: number;
}
export interface CancelTechReqOpts {
throwOnError?: boolean = false;
delayMsAndroid?: number = 1000;
interface NdefWriteOpts {
format?: boolean;
formatReadOnly?: boolean;
}
interface NdefHandler {
interface NfcManager {
start(): Promise<void>;
isSupported(): Promise<boolean>;
registerTagEvent(options?: RegisterTagEventOpts): Promise<void>;
unregisterTagEvent(): Promise<void>;
setEventListener(name: NfcEvents, callback: OnNfcEvents): void;
requestTechnology: (tech: NfcTech) => Promise<NfcTech>;
cancelTechnologyRequest: () => Promise<void>;
getTag: () => Promise<any>;
writeNdefMessage: (bytes: number[]) => Promise<void>;
getNdefMessage: () => Promise<TagEvent | null>;
makeReadOnly: () => Promise<void>;
getNdefStatus: () => Promise<{
status: NdefStatus;
capacity: number;
}>;
getNdefMessage: (bytes: number[]) => Promise<TagEvent | null>;
/** [iOS ONLY] */
setAlertMessageIOS: (alertMessage: string) => Promise<void>;
/** [iOS ONLY] */
sendMifareCommandIOS: (bytes: number[]) => Promise<number[]>;
/** [iOS ONLY] */
sendCommandAPDUIOS: (
bytes: number[],
) => Promise<{response: number[]; sw1: number; sw2: number}>;
/** [ANDROID ONLY] */
isEnabled(): Promise<boolean>;
/** [ANDROID ONLY] */
goToNfcSetting(): Promise<any>;
/** [ANDROID ONLY] */
getLaunchTagEvent(): Promise<TagEvent | null>;
/** [ANDROID ONLY] */
requestNdefWrite(bytes: number[], opts?: NdefWriteOpts): Promise<any>;
/** [ANDROID ONLY] */
cancelNdefWrite(): Promise<any>;
/** [ANDROID ONLY] */
getCachedNdefMessageAndroid: () => Promise<TagEvent | null>;
}
interface NfcAHandler {
transceive: (bytes: number[]) => Promise<number[]>;
}
interface NfcVHandler {
transceive: (bytes: number[]) => Promise<number[]>;
}
interface IsoDepHandler {
transceive: (bytes: number[]) => Promise<number[]>;
}
interface MifareClassicHandlerAndroid {
/** [ANDROID ONLY] */
makeReadOnlyAndroid: () => Promise<boolean>;
/** [ANDROID ONLY] */
transceive(bytes: number[]): Promise<number[]>;
/** [ANDROID ONLY] */
getMaxTransceiveLength(): Promise<number>;
/** [ANDROID ONLY] */
mifareClassicSectorToBlock: (sector: number) => Promise<ArrayLike<number>>;
/** [ANDROID ONLY] */
mifareClassicReadBlock: (
block: ArrayLike<number>,
) => Promise<ArrayLike<number>>;
/** [ANDROID ONLY] */
mifareClassicWriteBlock: (
block: ArrayLike<number>,
simpliArr: any[],
) => Promise<void>;
mifareClassicIncrementBlock: (
block: ArrayLike<number>,
data: number,
) => Promise<void>;
mifareClassicDecrementBlock: (
block: ArrayLike<number>,
data: number,
) => Promise<void>;
mifareClassicTransferBlock: (
block: ArrayLike<number>,
) => Promise<void>;
/** [ANDROID ONLY] */
mifareClassicGetSectorCount: () => Promise<number>;
/** [ANDROID ONLY] */
mifareClassicAuthenticateA: (
sector: number,
keys: number[],
) => Promise<void>;
mifareClassicAuthenticateB: (
sector: number,
keys: number[],
) => Promise<void>;
}
interface MifareUltralightHandlerAndroid {
mifareUltralightReadPages: (offset: number) => Promise<ArrayLike<number>>;
mifareUltralightWritePage: (
offset: number,
data: number[],
) => Promise<void>;
}
interface NdefFormatableHandlerAndroid {
formatNdef: (bytes: number[], options?: { readOnly: boolean }) => Promise<void>;
}
/** [iOS ONLY] */
interface Iso15693HandlerIOS {
getSystemInfo: (
requestFloags: number,
) => Promise<{
dsfid: number;
afi: number;
blockSize: number;
blockCount: number;
icReference: number;
}>;
readSingleBlock: (params: {
flags: number;
blockNumber: number;
}) => Promise<number[]>;
readMultipleBlocks: (params: {
flags: number;
blockNumber: number;
blockCount: number;
}) => Promise<number[][]>;
writeSingleBlock: (params: {
flags: number;
blockNumber: number;
dataBlock: number[];
}) => Promise<void>;
lockBlock: (params: {flags: number; blockNumber: number}) => Promise<void>;
writeAFI: (params: {flags: number; afi: number}) => Promise<void>;
lockAFI: (params: {flags: number}) => Promise<void>;
writeDSFID: (params: {flags: number; dsfid: number}) => Promise<void>;
lockDSFID: (params: {flags: number}) => Promise<void>;
resetToReady: (params: {flags: number}) => Promise<void>;
select: (params: {flags: number}) => Promise<void>;
stayQuite: () => Promise<void>;
customCommand: (params: {
flags: number;
customCommandCode: number;
customRequestParameters: number[];
}) => Promise<number[]>;
sendRequest: (params: {
flags: number;
commandCode: number;
data: number[];
}) => Promise<[number, number[]]>;
extendedReadSingleBlock: (params: {
flags: number;
blockNumber: number;
}) => Promise<number[]>;
extendedWriteSingleBlock: (params: {
flags: number;
blockNumber: number;
dataBlock: number[];
}) => Promise<void>;
extendedLockBlock: (params: {
flags: number;
blockNumber: number;
}) => Promise<void>;
}
type OnDiscoverTag = (evt: TagEvent) => void;
type OnSessionClosed = (error?: NfcError.NfcErrorBase) => void;
type OnStateChanged = (evt: {state: string}) => void;
type OnNfcEvents = OnDiscoverTag | OnSessionClosed | OnStateChanged;
interface NfcManager {
start(): Promise<void>;
isSupported(): Promise<boolean>;
isEnabled(): Promise<boolean>;
registerTagEvent(options?: RegisterTagEventOpts): Promise<void>;
unregisterTagEvent(): Promise<void>;
setEventListener(name: NfcEvents, callback: OnNfcEvents | null): void;
requestTechnology(
tech: NfcTech | NfcTech[],
options?: RegisterTagEventOpts,
): Promise<NfcTech | null>;
cancelTechnologyRequest: (options?: CancelTechReqOpts) => Promise<void>;
getTag: () => Promise<TagEvent | null>;
getBackgroundTag: () => Promise<TagEvent | null>;
clearBackgroundTag: () => Promise<void>;
setAlertMessage: (alertMessage: string) => Promise<void>;
/**
* common tech handler getters for both iOS / Android
*/
ndefHandler: NdefHandler;
nfcAHandler: NfcAHandler;
nfcVHandler: NfcVHandler;
isoDepHandler: IsoDepHandler;
/**
* iOS only
*/
getBackgroundNdef: () => Promise<NdefRecord[] | null>;
setAlertMessageIOS: (alertMessage: string) => Promise<void>;
invalidateSessionIOS: () => Promise<void>;
invalidateSessionWithErrorIOS: (errorMessage: string) => Promise<void>;
sendMifareCommandIOS: (bytes: number[]) => Promise<number[]>;
sendFelicaCommandIOS: (bytes: number[]) => Promise<number[]>;
sendCommandAPDUIOS: (
bytesOrApdu:
| number[]
| {
cla: number;
ins: number;
p1: number;
p2: number;
data: number[];
le: number;
},
) => Promise<{response: number[]; sw1: number; sw2: number}>;
iso15693HandlerIOS: Iso15693HandlerIOS;
/**
* Android only
*/
goToNfcSetting(): Promise<boolean>;
getLaunchTagEvent(): Promise<TagEvent | null>;
transceive(bytes: number[]): Promise<number[]>;
getMaxTransceiveLength(): Promise<number>;
setTimeout(timeout: number): Promise<void>;
mifareClassicHandlerAndroid: MifareClassicHandlerAndroid;
mifareUltralightHandlerAndroid: MifareUltralightHandlerAndroid;
ndefFormatableHandlerAndroid: NdefFormatableHandlerAndroid;
}
const nfcManager: NfcManager;
export namespace NdefParser {
function parseUri(ndef: NdefRecord): {uri: string};
function parseText(ndef: NdefRecord): string | null;
}
type ISOLangCode = 'en' | string;
type URI = string;
export interface WifiSimpleCredentials {
ssid: string;
networkKey: string;
authType?: number[];
}
export const Ndef: {
TNF_EMPTY: 0x0;
TNF_WELL_KNOWN: 0x01;
@ -310,55 +160,6 @@ declare module 'react-native-nfc-manager' {
RTD_HANDOVER_REQUEST: 'Hr'; // [0x48, 0x72]
RTD_HANDOVER_SELECT: 'Hs'; // [0x48, 0x73]
RTD_BYTES_TEXT: [0x54];
RTD_BYTES_URI: [0x55];
RTD_BYTES_SMART_POSTER: [0x53, 0x70];
RTD_BYTES_ALTERNATIVE_CARRIER: [0x61, 0x63];
RTD_BYTES_HANDOVER_CARRIER: [0x48, 0x63];
RTD_BYTES_HANDOVER_REQUEST: [0x48, 0x72];
RTD_BYTES_HANDOVER_SELECT: [0x48, 0x73];
MIME_WFA_WSC: 'application/vnd.wfa.wsc';
RTD_URI_PROTOCOLS: [
'',
'http://www.',
'https://www.',
'http://',
'https://',
'tel:',
'mailto:',
'ftp://anonymous:anonymous@',
'ftp://ftp.',
'ftps://',
'sftp://',
'smb://',
'nfs://',
'ftp://',
'dav://',
'news:',
'telnet://',
'imap:',
'rtsp://',
'urn:',
'pop:',
'sip:',
'sips:',
'tftp:',
'btspp://',
'btl2cap://',
'btgoep://',
'tcpobex://',
'irdaobex://',
'file://',
'urn:epc:id:',
'urn:epc:tag:',
'urn:epc:pat:',
'urn:epc:raw:',
'urn:epc:',
'urn:nfc:',
];
text: {
encodePayload: (
text: string,
@ -371,10 +172,6 @@ declare module 'react-native-nfc-manager' {
encodePayload: (uri: URI) => NdefRecord;
decodePayload: (data: Uint8Array) => string;
};
wifiSimple: {
encodePayload: (credentials: WifiSimpleCredentials) => NdefRecord;
decodePayload: (data: Uint8Array) => WifiSimpleCredentials;
};
util: {
stringToBytes: (string: string) => any[];
bytesToString: (bytes: any) => string;
@ -382,63 +179,13 @@ declare module 'react-native-nfc-manager' {
toHex: (i: any) => any;
toPrintable: (i: any) => string;
};
isType(record: NdefRecord, tnf: TNF, type: string): boolean;
isType(record: NdefRecord, tnf: number, type: string): boolean;
stringify(data: number[], separator: string): string;
encodeMessage(records: NdefRecord[]): number[];
decodeMessage(bytes: number[]): NdefRecord[];
encodeMessage(records: NdefRecord[]): Uint8Array;
decodeMessage(bytes: any[] | Buffer): NdefRecord[];
textRecord(text: string, lang?: ISOLangCode, encoding?: any): NdefRecord;
uriRecord(uri: URI, id?: any): NdefRecord;
wifiSimpleRecord(credentials: WifiSimpleCredentials, id?: any): NdefRecord;
androidApplicationRecord(pkgName: string): NdefRecord;
record(
tnf: TNF,
type: string,
id: string | any[],
payload: string | any[],
): NdefRecord;
};
export const NfcErrorIOS: {
errCodes: {
unknown: -1;
userCancel: 200;
timeout: 201;
unexpected: 202;
systemBusy: 203;
firstNdefInvalid: 204;
};
parse(errorString: string): number;
}
export namespace NfcError {
export class NfcErrorBase extends Error {}
export class UnsupportedFeature extends NfcErrorBase {}
export class SecurityViolation extends NfcErrorBase {}
export class InvalidParameter extends NfcErrorBase {}
export class InvalidParameterLength extends NfcErrorBase {}
export class ParameterOutOfBound extends NfcErrorBase {}
export class RadioDisabled extends NfcErrorBase {}
// transceive errors
export class TagConnectionLost extends NfcErrorBase {}
export class RetryExceeded extends NfcErrorBase {}
export class TagResponseError extends NfcErrorBase {}
export class SessionInvalidated extends NfcErrorBase {}
export class TagNotConnected extends NfcErrorBase {}
export class PacketTooLong extends NfcErrorBase {}
// reader session errors
export class UserCancel extends NfcErrorBase {}
export class Timeout extends NfcErrorBase {}
export class Unexpected extends NfcErrorBase {}
export class SystemBusy extends NfcErrorBase {}
export class FirstNdefInvalid extends NfcErrorBase {}
// tag command configuration errors
export class InvalidConfiguration extends NfcErrorBase {}
// ndef reader session error
export class TagNotWritable extends NfcErrorBase {}
export class TagUpdateFailure extends NfcErrorBase {}
export class TagSizeTooSmall extends NfcErrorBase {}
export class ZeroLengthMessage extends NfcErrorBase {}
}
}
export default nfcManager;

View File

@ -9,29 +9,10 @@
#endif
#import <CoreNFC/CoreNFC.h>
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 12000) /* __IPHONE_12_0 */
#import <UIKit/UIUserActivity.h>
#endif
@interface NfcManager : RCTEventEmitter <RCTBridgeModule, NFCNDEFReaderSessionDelegate, NFCTagReaderSessionDelegate> {
}
@property (strong, nonatomic) NFCNDEFReaderSession *session;
@property (strong, nonatomic) NFCTagReaderSession *tagSession;
+ (BOOL)application:(nonnull UIApplication *)application
continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 12000) /* __IPHONE_12_0 */
(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> *_Nullable))restorationHandler;
#else
(nonnull void (^)(NSArray *_Nullable))restorationHandler;
#endif
@property (strong, nonatomic) NFCTagReaderSession *sessionEx;
@end
extern NSString* _Nonnull getHexString(NSData * _Nonnull);
extern NSString* _Nonnull getErrorMessage(NSError * _Nonnull);
extern NSData * _Nonnull arrayToData(NSArray * _Nonnull);
extern NSArray * _Nonnull dataToArray(NSData * _Nonnull);

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +0,0 @@
//
// Util.m
// react-native-nfc-manager
//
// Created by Richie Hsieh on 2022/1/27.
//
#import <Foundation/Foundation.h>
NSString* getHexString(NSData *data) {
NSUInteger capacity = data.length * 2;
NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity];
const unsigned char *buf = data.bytes;
NSInteger i;
for (i=0; i<data.length; ++i) {
[sbuf appendFormat:@"%02lX", (unsigned long)buf[i]];
}
return sbuf;
}
NSString* getErrorMessage(NSError *error) {
NSDictionary *userInfo = [error userInfo];
NSError *underlyingError = [userInfo objectForKey:NSUnderlyingErrorKey];
if (underlyingError != nil) {
return [NSString stringWithFormat:@"%@:%ld,%@:%ld",
[error domain], (long)[error code],
[underlyingError domain], (long)[underlyingError code]];
}
return [NSString stringWithFormat:@"%@:%ld",
[error domain], (long)[error code]];
}
NSData * arrayToData(NSArray *array) {
Byte bytes[[array count]];
for (int i = 0; i < [array count]; i++) {
bytes[i] = [[array objectAtIndex:i] integerValue];
}
NSData *payload = [[NSData alloc] initWithBytes:bytes length:[array count]];
return payload;
}
NSArray * dataToArray(NSData *data) {
const unsigned char *dataBuffer = data ? (const unsigned char *)[data bytes] : NULL;
if (!dataBuffer)
return @[];
NSUInteger dataLength = [data length];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:dataLength];
for (int i = 0; i < dataLength; ++i)
[array addObject:[NSNumber numberWithInteger:dataBuffer[i]]];
return array;
}

View File

@ -1,69 +0,0 @@
const constants = {
TNF_EMPTY: 0x0,
TNF_WELL_KNOWN: 0x01,
TNF_MIME_MEDIA: 0x02,
TNF_ABSOLUTE_URI: 0x03,
TNF_EXTERNAL_TYPE: 0x04,
TNF_UNKNOWN: 0x05,
TNF_UNCHANGED: 0x06,
TNF_RESERVED: 0x07,
RTD_TEXT: 'T',
RTD_URI: 'U',
RTD_SMART_POSTER: 'Sp',
RTD_ALTERNATIVE_CARRIER: 'ac',
RTD_HANDOVER_CARRIER: 'Hc',
RTD_HANDOVER_REQUEST: 'Hr',
RTD_HANDOVER_SELECT: 'Hs',
RTD_BYTES_TEXT: [0x54],
RTD_BYTES_URI: [0x55],
RTD_BYTES_SMART_POSTER: [0x53, 0x70],
RTD_BYTES_ALTERNATIVE_CARRIER: [0x61, 0x63],
RTD_BYTES_HANDOVER_CARRIER: [0x48, 0x63],
RTD_BYTES_HANDOVER_REQUEST: [0x48, 0x72],
RTD_BYTES_HANDOVER_SELECT: [0x48, 0x73],
MIME_WFA_WSC: 'application/vnd.wfa.wsc',
RTD_URI_PROTOCOLS: [
'',
'http://www.',
'https://www.',
'http://',
'https://',
'tel:',
'mailto:',
'ftp://anonymous:anonymous@',
'ftp://ftp.',
'ftps://',
'sftp://',
'smb://',
'nfs://',
'ftp://',
'dav://',
'news:',
'telnet://',
'imap:',
'rtsp://',
'urn:',
'pop:',
'sip:',
'sips:',
'tftp:',
'btspp://',
'btl2cap://',
'btgoep://',
'tcpobex://',
'irdaobex://',
'file://',
'urn:epc:id:',
'urn:epc:tag:',
'urn:epc:pat:',
'urn:epc:raw:',
'urn:epc:',
'urn:nfc:',
],
};
module.exports = constants;

View File

@ -1,122 +1,570 @@
// originally from phonegap-nfc.js by Don Coleman
// adapted for react-native-nfc-manager by Richie Hsieh
// ndef.js
// Copyright 2013 Don Coleman
//
// This code is from phonegap-nfc.js https://github.com/don/phonegap-nfc
const {
createNdefRecord,
encodeNdefMessage,
decodeNdefMessage,
equalToRecordType,
} = require('./ndef');
const constants = require('./constants');
const util = require('./util');
const textHelper = require('./ndef-text');
const uriHelper = require('./ndef-uri');
const wifiSimpleHelper = require('./ndef-wifi-simple');
const stringifier = require('./stringifier');
var util = require('./ndef-util'),
textHelper = require('./ndef-text'),
uriHelper = require('./ndef-uri');
const PrimitiveRecord = {
emptyRecord() {
return createNdefRecord(constants.TNF_EMPTY, [], [], []);
},
var ndef = {
absoluteUriRecord(uri, payload = [], id = []) {
return createNdefRecord(constants.TNF_ABSOLUTE_URI, uri, id, payload);
},
// see android.nfc.NdefRecord for documentation about constants
// http://developer.android.com/reference/android/nfc/NdefRecord.html
TNF_EMPTY: 0x0,
TNF_WELL_KNOWN: 0x01,
TNF_MIME_MEDIA: 0x02,
TNF_ABSOLUTE_URI: 0x03,
TNF_EXTERNAL_TYPE: 0x04,
TNF_UNKNOWN: 0x05,
TNF_UNCHANGED: 0x06,
TNF_RESERVED: 0x07,
mimeMediaRecord(mimeType, payload, id = []) {
return createNdefRecord(constants.TNF_MIME_MEDIA, mimeType, id, payload);
},
RTD_TEXT: "T", // [0x54]
RTD_URI: "U", // [0x55]
RTD_SMART_POSTER: "Sp", // [0x53, 0x70]
RTD_ALTERNATIVE_CARRIER: "ac", //[0x61, 0x63]
RTD_HANDOVER_CARRIER: "Hc", // [0x48, 0x63]
RTD_HANDOVER_REQUEST: "Hr", // [0x48, 0x72]
RTD_HANDOVER_SELECT: "Hs", // [0x48, 0x73]
externalTypeRecord(externalType, payload, id = []) {
return createNdefRecord(
constants.TNF_EXTERNAL_TYPE,
externalType,
id,
payload,
);
},
};
/**
* Creates a JSON representation of a NDEF Record.
*
* @tnf 3-bit TNF (Type Name Format) - use one of the TNF_* constants
* @type byte array, containing zero to 255 bytes, must not be null
* @id byte array, containing zero to 255 bytes, must not be null
* @payload byte array, containing zero to (2 ** 32 - 1) bytes, must not be null
*
* @returns JSON representation of a NDEF record
*
* @see Ndef.textRecord, Ndef.uriRecord and Ndef.mimeMediaRecord for examples
*/
record: function (tnf, type, id, payload) {
const WellKnownRecord = {
textRecord(text, languageCode, id = []) {
return createNdefRecord(
constants.TNF_WELL_KNOWN,
constants.RTD_TEXT,
id,
textHelper.encodePayload(text, languageCode),
);
},
// handle null values
if (!tnf) { tnf = ndef.TNF_EMPTY; }
if (!type) { type = []; }
if (!id) { id = []; }
if (!payload) { payload = []; }
uriRecord(uri, id = []) {
return createNdefRecord(
constants.TNF_WELL_KNOWN,
constants.RTD_URI,
id,
uriHelper.encodePayload(uri),
);
},
// store type as String so it's easier to compare
if(type instanceof Array) {
type = util.bytesToString(type);
}
smartPoster(ndefRecords, id = []) {
let payload = [];
// in the future, id could be a String
if (!(id instanceof Array)) {
id = util.stringToBytes(id);
}
// Payload must be binary
if (!(payload instanceof Array)) {
payload = util.stringToBytes(payload);
}
var record = {
tnf: tnf,
type: type,
id: id,
payload: payload
};
// Experimental feature
// Convert payload to text for Text and URI records
if (tnf === ndef.TNF_WELL_KNOWN) {
switch(record.type) {
case ndef.RTD_TEXT:
record.value = ndef.text.decodePayload(record.payload);
break;
case ndef.RTD_URI:
record.value = ndef.uri.decodePayload(record.payload);
break;
}
}
return record;
},
/**
* Helper that creates an NDEF record containing plain text.
*
* @text String of text to encode
* @languageCode ISO/IANA language code. Examples: fi, en-US, fr-CA, jp. (optional)
* @id byte[] (optional)
*/
textRecord: function (text, languageCode, id) {
var payload = textHelper.encodePayload(text, languageCode);
if (!id) { id = []; }
return ndef.record(ndef.TNF_WELL_KNOWN, ndef.RTD_TEXT, id, payload);
},
/**
* Helper that creates a NDEF record containing a URI.
*
* @uri String
* @id byte[] (optional)
*/
uriRecord: function (uri, id) {
var payload = uriHelper.encodePayload(uri);
if (!id) { id = []; }
return ndef.record(ndef.TNF_WELL_KNOWN, ndef.RTD_URI, id, payload);
},
/**
* Helper that creates a NDEF record containing an absolute URI.
*
* An Absolute URI record means the URI describes the payload of the record.
*
* For example a SOAP message could use "http://schemas.xmlsoap.org/soap/envelope/"
* as the type and XML content for the payload.
*
* Absolute URI can also be used to write LaunchApp records for Windows.
*
* See 2.4.2 Payload Type of the NDEF Specification
* http://www.nfc-forum.org/specs/spec_list#ndefts
*
* Note that by default, Android will open the URI defined in the type
* field of an Absolute URI record (TNF=3) and ignore the payload.
* BlackBerry and Windows do not open the browser for TNF=3.
*
* To write a URI as the payload use ndef.uriRecord(uri)
*
* @uri String
* @payload byte[] or String
* @id byte[] (optional)
*/
absoluteUriRecord: function (uri, payload, id) {
if (!id) { id = []; }
if (!payload) { payload = []; }
return ndef.record(ndef.TNF_ABSOLUTE_URI, uri, id, payload);
},
/**
* Helper that creates a NDEF record containing an mimeMediaRecord.
*
* @mimeType String
* @payload byte[]
* @id byte[] (optional)
*/
mimeMediaRecord: function (mimeType, payload, id) {
if (!id) { id = []; }
return ndef.record(ndef.TNF_MIME_MEDIA, mimeType, id, payload);
},
/**
* Helper that creates an NDEF record containing an Smart Poster.
*
* @ndefRecords array of NDEF Records
* @id byte[] (optional)
*/
smartPoster: function (ndefRecords, id) {
var payload = [];
if (!id) { id = []; }
if (ndefRecords)
{
// make sure we have an array of something like NDEF records before encoding
if (ndefRecords[0] instanceof Object && ndefRecords[0].hasOwnProperty('tnf')) {
payload = ndef.encodeMessage(ndefRecords);
} else {
// assume the caller has already encoded the NDEF records into a byte array
payload = ndefRecords;
}
} else {
console.log("WARNING: Expecting an array of NDEF records");
}
return ndef.record(ndef.TNF_WELL_KNOWN, ndef.RTD_SMART_POSTER, id, payload);
},
/**
* Helper that creates an empty NDEF record.
*
*/
emptyRecord: function() {
return ndef.record(ndef.TNF_EMPTY, [], [], []);
},
/**
* Helper that creates an Android Application Record (AAR).
* http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#aar
*
*/
androidApplicationRecord: function(packageName) {
return ndef.record(ndef.TNF_EXTERNAL_TYPE, "android.com:pkg", [], packageName);
},
/**
* Encodes an NDEF Message into bytes that can be written to a NFC tag.
*
* @ndefRecords an Array of NDEF Records
*
* @returns byte array
*
* @see NFC Data Exchange Format (NDEF) http://www.nfc-forum.org/specs/spec_list/
*/
encodeMessage: function (ndefRecords) {
var encoded = [],
tnf_byte,
record_type,
payload_length,
id_length,
i,
mb, me, // messageBegin, messageEnd
cf = false, // chunkFlag TODO implement
sr, // boolean shortRecord
il; // boolean idLengthFieldIsPresent
for(i = 0; i < ndefRecords.length; i++) {
mb = (i === 0);
me = (i === (ndefRecords.length - 1));
sr = (ndefRecords[i].payload.length < 0xFF);
il = (ndefRecords[i].id.length > 0);
tnf_byte = ndef.encodeTnf(mb, me, cf, sr, il, ndefRecords[i].tnf);
encoded.push(tnf_byte);
// type is stored as String, converting to bytes for storage
record_type = util.stringToBytes(ndefRecords[i].type);
encoded.push(record_type.length);
if (sr) {
payload_length = ndefRecords[i].payload.length;
encoded.push(payload_length);
} else {
payload_length = ndefRecords[i].payload.length;
// 4 bytes
encoded.push((payload_length >> 24));
encoded.push((payload_length >> 16));
encoded.push((payload_length >> 8));
encoded.push((payload_length & 0xFF));
}
if (il) {
id_length = ndefRecords[i].id.length;
encoded.push(id_length);
}
encoded = encoded.concat(record_type);
if (il) {
encoded = encoded.concat(ndefRecords[i].id);
}
encoded = encoded.concat(ndefRecords[i].payload);
}
return encoded;
},
/**
* Decodes an array bytes into an NDEF Message
*
* @ndefBytes an array bytes or Buffer that was read from a NFC tag
*
* @returns array of NDEF Records
*
* @see NFC Data Exchange Format (NDEF) http://www.nfc-forum.org/specs/spec_list/
*/
decodeMessage: function (ndefBytes) {
// ndefBytes can be an array of bytes e.g. [0x03, 0x31, 0xd1] or a Buffer
var bytes;
// if (ndefBytes instanceof Buffer) {
// // get an array of bytes
// bytes = Array.prototype.slice.call(ndefBytes, 0);
// } else if (ndefBytes instanceof Array) {
if (ndefBytes instanceof Array) {
bytes = ndefBytes.slice(0);
} else {
throw new Error('ndef.decodeMessage requires a Buffer or an Array of bytes');
}
var bytes = bytes.slice(0), // clone since parsing is destructive
ndef_message = [],
tnf_byte,
header,
type_length = 0,
payload_length = 0,
id_length = 0,
record_type = [],
id = [],
payload = [];
while(bytes.length) {
tnf_byte = bytes.shift();
header = ndef.decodeTnf(tnf_byte);
type_length = bytes.shift();
if (header.sr) {
payload_length = bytes.shift();
} else {
// next 4 bytes are length
payload_length = ((0xFF & bytes.shift()) << 24) |
((0xFF & bytes.shift()) << 16) |
((0xFF & bytes.shift()) << 8) |
(0xFF & bytes.shift());
}
id_length = header.il ? bytes.shift() : 0;
record_type = bytes.splice(0, type_length);
id = bytes.splice(0, id_length);
payload = bytes.splice(0, payload_length);
ndef_message.push(
ndef.record(header.tnf, record_type, id, payload)
);
if (header.me) break; // last message
}
return ndef_message;
},
/**
* Decode the bit flags from a TNF Byte.
*
* @returns object with decoded data
*
* See NFC Data Exchange Format (NDEF) Specification Section 3.2 RecordLayout
*/
decodeTnf: function (tnf_byte) {
return {
mb: (tnf_byte & 0x80) !== 0,
me: (tnf_byte & 0x40) !== 0,
cf: (tnf_byte & 0x20) !== 0,
sr: (tnf_byte & 0x10) !== 0,
il: (tnf_byte & 0x8) !== 0,
tnf: (tnf_byte & 0x7)
};
},
/**
* Encode NDEF bit flags into a TNF Byte.
*
* @returns tnf byte
*
* See NFC Data Exchange Format (NDEF) Specification Section 3.2 RecordLayout
*/
encodeTnf: function (mb, me, cf, sr, il, tnf) {
var value = tnf;
if (mb) {
value = value | 0x80;
}
if (me) {
value = value | 0x40;
}
// note if cf: me, mb, li must be false and tnf must be 0x6
if (cf) {
value = value | 0x20;
}
if (sr) {
value = value | 0x10;
}
if (il) {
value = value | 0x8;
}
return value;
},
// TODO test with byte[] and string
isType: function(record, tnf, type) {
if (record.tnf === tnf) {
return (s(record.type) === s(type));
}
return false;
if (ndefRecords) {
// make sure we have an array of something like NDEF records before encoding
if (
ndefRecords[0] instanceof Object &&
ndefRecords[0].hasOwnProperty('tnf')
) {
payload = encodeNdefMessage(ndefRecords);
} else {
// assume the caller has already encoded the NDEF records into a byte array
payload = ndefRecords;
}
} else {
console.log('WARNING: Expecting an array of NDEF records');
}
return createNdefRecord(
constants.TNF_WELL_KNOWN,
constants.RTD_SMART_POSTER,
id,
payload,
);
},
};
const ExtraTypeRecord = {
androidApplicationRecord(packageName, id = []) {
return PrimitiveRecord.externalTypeRecord(
'android.com:pkg',
packageName,
id,
);
},
function tnfToString(tnf) {
var value = tnf;
wifiSimpleRecord: function (credentials, id = []) {
let payload = wifiSimpleHelper.encodePayload(credentials);
return PrimitiveRecord.mimeMediaRecord(constants.MIME_WFA_WSC, payload, id);
},
switch (tnf) {
case ndef.TNF_EMPTY:
value = "Empty";
break;
case ndef.TNF_WELL_KNOWN:
value = "Well Known";
break;
case ndef.TNF_MIME_MEDIA:
value = "Mime Media";
break;
case ndef.TNF_ABSOLUTE_URI:
value = "Absolute URI";
break;
case ndef.TNF_EXTERNAL_TYPE:
value = "External";
break;
case ndef.TNF_UNKNOWN:
value = "Unknown";
break;
case ndef.TNF_UNCHANGED:
value = "Unchanged";
break;
case ndef.TNF_RESERVED:
value = "Reserved";
break;
}
return value;
}
// Convert NDEF records and messages to strings
// This works OK for demos, but real code proably needs
// a custom implementation. It would be nice to make
// smarter record objects that can print themselves
var stringifier = {
stringify: function (data, separator) {
if (Array.isArray(data)) {
if (typeof data[0] === 'number') {
// guessing this message bytes
data = ndef.decodeMessage(data);
}
return stringifier.printRecords(data, separator);
} else {
return stringifier.printRecord(data, separator);
}
},
// @message - NDEF Message (array of NDEF Records)
// @separator - line separator, optional, defaults to \n
// @returns string with NDEF Message
printRecords: function (message, separator) {
if(!separator) { separator = "\n"; }
result = "";
// Print out the payload for each record
message.forEach(function(record) {
result += stringifier.printRecord(record, separator);
result += separator;
});
return result.slice(0, (-1 * separator.length));
},
// @record - NDEF Record
// @separator - line separator, optional, defaults to \n
// @returns string with NDEF Record
printRecord: function (record, separator) {
var result = "";
if(!separator) { separator = "\n"; }
switch(record.tnf) {
case ndef.TNF_EMPTY:
result += "Empty Record";
result += separator;
break;
case ndef.TNF_WELL_KNOWN:
result += stringifier.printWellKnown(record, separator);
break;
case ndef.TNF_MIME_MEDIA:
result += "MIME Media";
result += separator;
result += s(record.type);
result += separator;
result += s(record.payload); // might be binary
break;
case ndef.TNF_ABSOLUTE_URI:
result += "Absolute URI";
result += separator;
result += s(record.type); // the URI is the type
result += separator;
result += s(record.payload); // might be binary
break;;
case ndef.TNF_EXTERNAL_TYPE:
// AAR contains strings, other types could
// contain binary data
result += "External";
result += separator;
result += s(record.type);
result += separator;
result += s(record.payload);
break;
default:
result += s("Can't process TNF " + record.tnf);
}
result += separator;
return result;
},
printWellKnown: function (record, separator) {
var result = "";
if (record.tnf !== ndef.TNF_WELL_KNOWN) {
return "ERROR expecting TNF Well Known";
}
switch(record.type) {
case ndef.RTD_TEXT:
result += "Text Record";
result += separator;
result += (ndef.text.decodePayload(record.payload));
break;
case ndef.RTD_URI:
result += "URI Record";
result += separator;
result += (ndef.uri.decodePayload(record.payload));
break;
case ndef.RTD_SMART_POSTER:
result += "Smart Poster";
result += separator;
// the payload of a smartposter is a NDEF message
result += stringifier.printRecords(ndef.decodeMessage(record.payload));
break;
default:
// attempt to display other types
result += record.type + " Record";
result += separator;
result += s(record.payload);
}
return result;
}
};
const NDEF = {
...constants,
...PrimitiveRecord,
...WellKnownRecord,
...ExtraTypeRecord,
// convert bytes to a String
function s(bytes) {
if (typeof(bytes) === 'string') {
return bytes;
}
record: createNdefRecord,
encodeMessage: encodeNdefMessage,
decodeMessage: decodeNdefMessage,
isType: equalToRecordType,
return bytes.reduce(function (acc, byte) {
return acc + String.fromCharCode(byte);
}, '')
// return new Buffer(bytes).toString();
}
// individual record type helpers
text: textHelper,
uri: uriHelper,
wifiSimple: wifiSimpleHelper,
// expose helper objects
ndef.text = textHelper;
ndef.uri = uriHelper;
ndef.tnfToString = tnfToString;
ndef.util = util;
ndef.stringify = stringifier.stringify;
// other helpers
util,
stringify: stringifier.stringify,
};
module.exports = NDEF;
module.exports = ndef;

View File

@ -1,33 +1,33 @@
var util = require('./util');
var util = require('./ndef-util');
// decode text bytes from ndef record payload
// @returns a string
function decode(data) {
var languageCodeLength = data[0] & 0x3f; // 6 LSBs
// languageCode = data.slice(1, 1 + languageCodeLength),
// utf16 = (data[0] & 0x80) !== 0; // assuming UTF-16BE
// TODO need to deal with UTF in the future
// console.log("lang " + languageCode + (utf16 ? " utf16" : " utf8"));
var languageCodeLength = (data[0] & 0x3F), // 6 LSBs
languageCode = data.slice(1, 1 + languageCodeLength),
utf16 = (data[0] & 0x80) !== 0; // assuming UTF-16BE
return util.bytesToString(data.slice(languageCodeLength + 1));
// TODO need to deal with UTF in the future
// console.log("lang " + languageCode + (utf16 ? " utf16" : " utf8"));
return util.bytesToString(data.slice(languageCodeLength + 1));
}
// encode text payload
// @returns an array of bytes
function encode(text, lang, encoding) {
// ISO/IANA language code, but we're not enforcing
if (!lang) {
lang = 'en';
}
var encoded = util.stringToBytes(lang + text);
encoded.unshift(lang.length);
// ISO/IANA language code, but we're not enforcing
if (!lang) { lang = 'en'; }
return encoded;
var encoded = util.stringToBytes(lang + text);
encoded.unshift(lang.length);
return encoded;
}
module.exports = {
encodePayload: encode,
decodePayload: decode,
};
encodePayload: encode,
decodePayload: decode
}

View File

@ -1,44 +1,49 @@
const util = require('./util');
const protocols = require('./constants').RTD_URI_PROTOCOLS;
var util = require('./ndef-util');
// URI identifier codes from URI Record Type Definition NFCForum-TS-RTD_URI_1.0 2006-07-24
// index in array matches code in the spec
var protocols = [ "", "http://www.", "https://www.", "http://", "https://", "tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.", "ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://", "news:", "telnet://", "imap:", "rtsp://", "urn:", "pop:", "sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://", "tcpobex://", "irdaobex://", "file://", "urn:epc:id:", "urn:epc:tag:", "urn:epc:pat:", "urn:epc:raw:", "urn:epc:", "urn:nfc:" ]
// decode a URI payload bytes
// @returns a string
function decode(data) {
var prefix = protocols[data[0]];
if (!prefix) {
// 36 to 255 should be ""
prefix = '';
}
return prefix + util.bytesToString(data.slice(1));
}
var prefix = protocols[data[0]];
if (!prefix) { // 36 to 255 should be ""
prefix = "";
}
return prefix + util.bytesToString(data.slice(1));
}
// shorten a URI with standard prefix
// @returns an array of bytes
function encode(uri) {
var prefix, protocolCode, encoded;
var prefix,
protocolCode,
encoded;
// check each protocol, unless we've found a match
// "urn:" is the one exception where we need to keep checking
// slice so we don't check ""
protocols.slice(1).forEach(function (protocol) {
if ((!prefix || prefix === 'urn:') && uri.indexOf(protocol) === 0) {
prefix = protocol;
// check each protocol, unless we've found a match
// "urn:" is the one exception where we need to keep checking
// slice so we don't check ""
protocols.slice(1).forEach(function(protocol) {
if ((!prefix || prefix === "urn:") && uri.indexOf(protocol) === 0) {
prefix = protocol;
}
});
if (!prefix) {
prefix = "";
}
});
if (!prefix) {
prefix = '';
}
encoded = util.stringToBytes(uri.slice(prefix.length));
protocolCode = protocols.indexOf(prefix);
// prepend protocol code
encoded.unshift(protocolCode);
return encoded;
encoded = util.stringToBytes(uri.slice(prefix.length));
protocolCode = protocols.indexOf(prefix);
// prepend protocol code
encoded.unshift(protocolCode);
return encoded;
}
module.exports = {
encodePayload: encode,
decodePayload: decode,
};
encodePayload: encode,
decodePayload: decode
}

131
ndef-lib/ndef-util.js Normal file
View File

@ -0,0 +1,131 @@
// ndef-util.js
// Copyright 2013 Don Coleman
//
// https://gist.github.com/joni/3760795
function _utf8ArrayToStr(data) {
var str = '',
i;
for (i = 0; i < data.length; i++) {
var value = data[i];
if (value < 0x80) {
str += String.fromCharCode(value);
} else if (value > 0xBF && value < 0xE0) {
str += String.fromCharCode((value & 0x1F) << 6 | data[i + 1] & 0x3F);
i += 1;
} else if (value > 0xDF && value < 0xF0) {
str += String.fromCharCode((value & 0x0F) << 12 | (data[i + 1] & 0x3F) << 6 | data[i + 2] & 0x3F);
i += 2;
} else {
// surrogate pair
var charCode = ((value & 0x07) << 18 | (data[i + 1] & 0x3F) << 12 | (data[i + 2] & 0x3F) << 6 | data[i + 3] & 0x3F) - 0x010000;
str += String.fromCharCode(charCode >> 10 | 0xD800, charCode & 0x03FF | 0xDC00);
i += 3;
}
}
return str;
}
// https://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
function _toUTF8Array(str) {
var out = [], p = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
} else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
} else if (
((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
} else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
};
function stringToBytes(string) {
return _toUTF8Array(string);
// var bytes = Buffer(string).toJSON();
// if (bytes.hasOwnProperty('data')) {
// // Node 0.12.x
// return bytes.data;
// } else {
// // Node 0.10.x
// return bytes;
// }
}
function bytesToString(bytes) {
if (typeof(bytes) === 'string') {
return bytes;
}
return _utf8ArrayToStr(bytes);
// return Buffer(bytes).toString();
}
// useful for readable version of Tag UID
function bytesToHexString(bytes) {
var dec, hexstring, bytesAsHexString = "";
for (var i = 0; i < bytes.length; i++) {
if (bytes[i] >= 0) {
dec = bytes[i];
} else {
dec = 256 + bytes[i];
}
hexstring = dec.toString(16);
// zero padding
if (hexstring.length == 1) {
hexstring = "0" + hexstring;
}
bytesAsHexString += hexstring;
}
return bytesAsHexString;
}
// i must be <= 256
function toHex(i) {
var hex;
if (i < 0) {
i += 256;
}
hex = i.toString(16);
// zero padding
if (hex.length == 1) {
hex = "0" + hex;
}
return hex;
}
function toPrintable(i) {
if (i >= 0x20 & i <= 0x7F) {
return String.fromCharCode(i);
} else {
return '.';
}
}
module.exports = {
stringToBytes: stringToBytes,
bytesToString: bytesToString,
bytesToHexString: bytesToHexString,
toHex: toHex,
toPrintable: toPrintable
};

View File

@ -1,114 +0,0 @@
var util = require('./util');
const CREDENTIAL_FIELD_ID = [0x10, 0x0e];
const SSID_FIELD_ID = [0x10, 0x45];
const AUTH_TYPE_FIELD_ID = [0x10, 0x03];
const NETWORK_KEY_FIELD_ID = [0x10, 0x27];
const AUTH_TYPES = {
OPEN: [0x00, 0x00],
WPA2_PSK: [0x00, 0x20],
};
function _getLengthBytes(valueBytes) {
if (valueBytes.length > 255) {
return [Math.floor(valueBytes.length / 256), valueBytes.length % 256];
}
return [0x0, valueBytes.length];
}
function _arrayEqual(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
function _getNextTLV(bytes) {
const type = bytes.slice(0, 2);
const length = bytes.slice(2, 4);
const value = bytes.slice(4, 4 + (length[0] * 256 + length[1]));
return {
type,
length,
value,
};
}
// @returns an string of wifi credentials
function decode(bytes) {
let result = {};
while (bytes.length > 0) {
let {type, value} = _getNextTLV(bytes);
bytes = bytes.slice(4 + value.length, bytes.length);
if (_arrayEqual(CREDENTIAL_FIELD_ID, type)) {
let credential = value;
while (credential.length > 0) {
let tlv = _getNextTLV(credential);
credential = credential.slice(4 + tlv.value.length, credential.length);
if (_arrayEqual(AUTH_TYPE_FIELD_ID, tlv.type)) {
result.authType = tlv.value;
} else if (_arrayEqual(SSID_FIELD_ID, tlv.type)) {
result.ssid = util.bytesToString(tlv.value);
} else if (_arrayEqual(NETWORK_KEY_FIELD_ID, tlv.type)) {
result.networkKey = util.bytesToString(tlv.value);
}
}
}
}
return result;
}
// encode wifi object payload
// @returns an array of bytes
function encode({ssid, networkKey, authType = AUTH_TYPES.WPA2_PSK}) {
if (typeof ssid !== 'string' || typeof networkKey !== 'string') {
throw new Error('');
}
ssid = util.stringToBytes(ssid);
networkKey = util.stringToBytes(networkKey);
// build seperated TLV
const authTypeTLV = [
...AUTH_TYPE_FIELD_ID,
0x0,
authType.length,
...authType,
];
const ssidTLV = [...SSID_FIELD_ID, 0x0, ssid.length, ...ssid];
const networkKeyTLV = [
...NETWORK_KEY_FIELD_ID,
0x0,
networkKey.length,
...networkKey,
];
// build credential TLV
const credentialValue = [...authTypeTLV, ...ssidTLV, ...networkKeyTLV];
const credentialTLV = [
...CREDENTIAL_FIELD_ID,
..._getLengthBytes(credentialValue),
...credentialValue,
];
return credentialTLV;
}
module.exports = {
encodePayload: encode,
decodePayload: decode,
authTypes: AUTH_TYPES,
};

View File

@ -1,197 +0,0 @@
const util = require('./util');
function createNdefRecord(tnf, type, id, payload) {
if (
tnf === undefined ||
type === undefined ||
id === undefined ||
payload === undefined
) {
throw new Error('missing required param');
}
// store type as String so it's easier to compare
if (type instanceof Array) {
type = util.bytesToString(type);
}
// in the future, id could be a String
if (!(id instanceof Array)) {
id = util.stringToBytes(id);
}
// Payload must be binary
if (!(payload instanceof Array)) {
payload = util.stringToBytes(payload);
}
return {
tnf: tnf,
type: type,
id: id,
payload: payload,
};
}
function encodeNdefMessage(ndefRecords) {
const encodeTnf = ({mb, me, cf, sr, il, tnf}) => {
let value = tnf;
if (mb) {
value = value | 0x80;
}
if (me) {
value = value | 0x40;
}
// note if cf: me, mb, li must be false and tnf must be 0x6
if (cf) {
value = value | 0x20;
}
if (sr) {
value = value | 0x10;
}
if (il) {
value = value | 0x8;
}
return value;
};
let encoded = [],
tnf_byte,
record_type,
payload_length,
id_length,
i,
mb,
me, // messageBegin, messageEnd
cf = false, // chunkFlag TODO implement
sr, // boolean shortRecord
il; // boolean idLengthFieldIsPresent
for (i = 0; i < ndefRecords.length; i++) {
mb = i === 0;
me = i === ndefRecords.length - 1;
sr = ndefRecords[i].payload.length < 0xff;
il = ndefRecords[i].id.length > 0;
tnf_byte = encodeTnf({mb, me, cf, sr, il, tnf: ndefRecords[i].tnf});
encoded.push(tnf_byte);
// type is stored as String, converting to bytes for storage
record_type = util.stringToBytes(ndefRecords[i].type);
encoded.push(record_type.length);
if (sr) {
payload_length = ndefRecords[i].payload.length;
encoded.push(payload_length);
} else {
payload_length = ndefRecords[i].payload.length;
// 4 bytes
encoded.push(payload_length >> 24);
encoded.push(payload_length >> 16);
encoded.push(payload_length >> 8);
encoded.push(payload_length & 0xff);
}
if (il) {
id_length = ndefRecords[i].id.length;
encoded.push(id_length);
}
encoded = encoded.concat(record_type);
if (il) {
encoded = encoded.concat(ndefRecords[i].id);
}
encoded = encoded.concat(ndefRecords[i].payload);
}
return encoded;
}
function decodeNdefMessage(ndefBytes) {
const decodeTnf = (tnf_byte) => ({
mb: (tnf_byte & 0x80) !== 0,
me: (tnf_byte & 0x40) !== 0,
cf: (tnf_byte & 0x20) !== 0,
sr: (tnf_byte & 0x10) !== 0,
il: (tnf_byte & 0x8) !== 0,
tnf: tnf_byte & 0x7,
});
// ndefBytes can be an array of bytes e.g. [0x03, 0x31, 0xd1] or a Buffer
let bytes;
if (ndefBytes instanceof Array) {
bytes = ndefBytes.slice(0);
} else {
throw new Error(
'ndef.decodeMessage requires a Buffer or an Array of bytes',
);
}
bytes = bytes.slice(0); // clone since parsing is destructive
let ndef_message = [],
tnf_byte,
header,
type_length = 0,
payload_length = 0,
id_length = 0,
record_type = [],
id = [],
payload = [];
while (bytes.length) {
tnf_byte = bytes.shift();
header = decodeTnf(tnf_byte);
type_length = bytes.shift();
if (header.sr) {
payload_length = bytes.shift();
} else {
// next 4 bytes are length
payload_length =
((0xff & bytes.shift()) << 24) |
((0xff & bytes.shift()) << 16) |
((0xff & bytes.shift()) << 8) |
(0xff & bytes.shift());
}
id_length = header.il ? bytes.shift() : 0;
record_type = bytes.splice(0, type_length);
id = bytes.splice(0, id_length);
payload = bytes.splice(0, payload_length);
ndef_message.push(createNdefRecord(header.tnf, record_type, id, payload));
if (header.me) {
break;
} // last message
}
return ndef_message;
}
function equalToRecordType(record, tnf, type) {
if (record.tnf === tnf) {
if (Array.isArray(record.type)) {
return util.bytesToString(record.type) === type;
} else {
return record.type === type;
}
}
return record.tnf === tnf && record.type === type;
}
module.exports = {
createNdefRecord,
encodeNdefMessage,
decodeNdefMessage,
equalToRecordType,
};

View File

@ -1,166 +0,0 @@
const ndef = require('./ndef');
const constants = require('./constants');
// Convert NDEF records and messages to strings
// This works OK for demos, but real code proably needs
// a custom implementation. It would be nice to make
// smarter record objects that can print themselves
let stringifier = {
stringify: function (data, separator) {
if (Array.isArray(data)) {
if (typeof data[0] === 'number') {
// guessing this message bytes
data = ndef.decodeMessage(data);
}
return stringifier.printRecords(data, separator);
} else {
return stringifier.printRecord(data, separator);
}
},
// @message - NDEF Message (array of NDEF Records)
// @separator - line separator, optional, defaults to \n
// @returns string with NDEF Message
printRecords: function (message, separator) {
if (!separator) {
separator = '\n';
}
let result = '';
// Print out the payload for each record
message.forEach(function (record) {
result += stringifier.printRecord(record, separator);
result += separator;
});
return result.slice(0, -1 * separator.length);
},
// @record - NDEF Record
// @separator - line separator, optional, defaults to \n
// @returns string with NDEF Record
printRecord: function (record, separator) {
let result = '';
if (!separator) {
separator = '\n';
}
switch (record.tnf) {
case constants.TNF_EMPTY:
result += 'Empty Record';
result += separator;
break;
case constants.TNF_WELL_KNOWN:
result += stringifier.printWellKnown(record, separator);
break;
case constants.TNF_MIME_MEDIA:
result += 'MIME Media';
result += separator;
result += s(record.type);
result += separator;
result += s(record.payload); // might be binary
break;
case constants.TNF_ABSOLUTE_URI:
result += 'Absolute URI';
result += separator;
result += s(record.type); // the URI is the type
result += separator;
result += s(record.payload); // might be binary
break;
case constants.TNF_EXTERNAL_TYPE:
// AAR contains strings, other types could
// contain binary data
result += 'External';
result += separator;
result += s(record.type);
result += separator;
result += s(record.payload);
break;
default:
result += s("Can't process TNF " + record.tnf);
}
result += separator;
return result;
},
printWellKnown: function (record, separator) {
let result = '';
if (record.tnf !== constants.TNF_WELL_KNOWN) {
return 'ERROR expecting TNF Well Known';
}
switch (record.type) {
case constants.RTD_TEXT:
result += 'Text Record';
result += separator;
result += ndef.text.decodePayload(record.payload);
break;
case constants.RTD_URI:
result += 'URI Record';
result += separator;
result += ndef.uri.decodePayload(record.payload);
break;
case constants.RTD_SMART_POSTER:
result += 'Smart Poster';
result += separator;
// the payload of a smartposter is a NDEF message
result += stringifier.printRecords(ndef.decodeMessage(record.payload));
break;
default:
// attempt to display other types
result += record.type + ' Record';
result += separator;
result += s(record.payload);
}
return result;
},
tnfToString: function (tnf) {
let value = tnf;
switch (tnf) {
case constants.TNF_EMPTY:
value = 'Empty';
break;
case constants.TNF_WELL_KNOWN:
value = 'Well Known';
break;
case constants.TNF_MIME_MEDIA:
value = 'Mime Media';
break;
case constants.TNF_ABSOLUTE_URI:
value = 'Absolute URI';
break;
case constants.TNF_EXTERNAL_TYPE:
value = 'External';
break;
case constants.TNF_UNKNOWN:
value = 'Unknown';
break;
case constants.TNF_UNCHANGED:
value = 'Unchanged';
break;
case constants.TNF_RESERVED:
value = 'Reserved';
break;
}
return value;
},
};
function s(bytes) {
if (typeof bytes === 'string') {
return bytes;
}
return bytes.reduce(function (acc, byte) {
return acc + String.fromCharCode(byte);
}, '');
}
module.exports = stringifier;

View File

@ -1,139 +0,0 @@
// ndef-util.js
// Copyright 2013 Don Coleman
//
// https://weblog.rogueamoeba.com/2017/02/27/javascript-correctly-converting-a-byte-array-to-a-utf-8-string/
function _utf8ArrayToStr(data) {
const extraByteMap = [1, 1, 1, 1, 2, 2, 3, 0];
var count = data.length;
var str = '';
for (var index = 0; index < count; ) {
var ch = data[index++];
if (ch & 0x80) {
var extra = extraByteMap[(ch >> 3) & 0x07];
if (!(ch & 0x40) || !extra || index + extra > count) {
return null;
}
ch = ch & (0x3f >> extra);
for (; extra > 0; extra -= 1) {
var chx = data[index++];
if ((chx & 0xc0) !== 0x80) {
return null;
}
ch = (ch << 6) | (chx & 0x3f);
}
}
str += String.fromCharCode(ch);
}
return str;
}
// https://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
function _toUTF8Array(str) {
var out = [],
p = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
} else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
} else if (
(c & 0xfc00) === 0xd800 &&
i + 1 < str.length &&
(str.charCodeAt(i + 1) & 0xfc00) === 0xdc00
) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
} else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
}
function stringToBytes(string) {
return _toUTF8Array(string);
// var bytes = Buffer(string).toJSON();
// if (bytes.hasOwnProperty('data')) {
// // Node 0.12.x
// return bytes.data;
// } else {
// // Node 0.10.x
// return bytes;
// }
}
function bytesToString(bytes) {
if (typeof bytes === 'string') {
return bytes;
}
return _utf8ArrayToStr(bytes);
// return Buffer(bytes).toString();
}
// useful for readable version of Tag UID
function bytesToHexString(bytes) {
var dec,
hexstring,
bytesAsHexString = '';
for (var i = 0; i < bytes.length; i++) {
if (bytes[i] >= 0) {
dec = bytes[i];
} else {
dec = 256 + bytes[i];
}
hexstring = dec.toString(16);
// zero padding
if (hexstring.length === 1) {
hexstring = '0' + hexstring;
}
bytesAsHexString += hexstring;
}
return bytesAsHexString;
}
// i must be <= 256
function toHex(i) {
var hex;
if (i < 0) {
i += 256;
}
hex = i.toString(16);
// zero padding
if (hex.length === 1) {
hex = '0' + hex;
}
return hex;
}
function toPrintable(i) {
if ((i >= 0x20) & (i <= 0x7f)) {
return String.fromCharCode(i);
} else {
return '.';
}
}
module.exports = {
stringToBytes: stringToBytes,
bytesToString: bytesToString,
bytesToHexString: bytesToHexString,
toHex: toHex,
toPrintable: toPrintable,
};

57
ndef.test.js Normal file
View File

@ -0,0 +1,57 @@
const ndef = require("./ndef-lib");
const textHelper = require("./ndef-lib/ndef-text");
const textMessageHelloWorld = [ 209, 1, 15, 84, 2, 101, 110, 104, 101, 108, 108, 111,
44, 32, 119, 111, 114, 108, 100 ];
const urlMessageNodeJSorg = [ 209, 1, 11, 85, 3, 110, 111, 100, 101, 106, 115, 46,
111, 114, 103 ];
const multipleRecordMessage = [ 145, 1, 15, 84, 2, 101, 110, 104, 101, 108, 108, 111,
44, 32, 119, 111, 114, 108, 100, 17, 1, 11, 85, 3, 110, 111, 100, 101,
106, 115, 46, 111, 114, 103, 82, 9, 27, 116, 101, 120, 116, 47, 106, 115,
111, 110, 123, 34, 109, 101, 115, 115, 97, 103, 101, 34, 58, 32, 34, 104,
101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 34, 125 ];
test('build and parse text', () => {
const text = "hello, world";
let message = [
ndef.textRecord(text)
];
let encoded = ndef.encodeMessage(message);
expect(encoded).toEqual(textMessageHelloWorld);
let decodedMessage = ndef.decodeMessage(encoded);
expect(message[0]).toEqual(decodedMessage[0]);
expect(textHelper.decodePayload(message[0].payload)).toEqual(text);
});
test('build and parse uri', () => {
let message = [
ndef.uriRecord("http://nodejs.org")
];
let encoded = ndef.encodeMessage(message);
expect(encoded).toEqual(urlMessageNodeJSorg);
let decodedMessage = ndef.decodeMessage(encoded);
expect(message[0]).toEqual(decodedMessage[0]);
});
test('build and parse multiple records', () => {
var message = [
ndef.textRecord("hello, world"),
ndef.uriRecord("http://nodejs.org"),
ndef.mimeMediaRecord("text/json", '{"message": "hello, world"}')
];
let encoded = ndef.encodeMessage(message);
expect(encoded).toEqual(multipleRecordMessage);
let decodedMessage = ndef.decodeMessage(encoded);
expect(message[0]).toEqual(decodedMessage[0]);
expect(message[1]).toEqual(decodedMessage[1]);
expect(message[2]).toEqual(decodedMessage[2]);
});

34445
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{
"name": "react-native-nfc-manager",
"version": "3.13.5",
"version": "2.0.0-beta.4",
"description": "A NFC module for react native.",
"main": "src/index.js",
"main": "NfcManager",
"repository": {
"type": "git",
"url": "https://github.com/whitedogg13/react-native-nfc-manager.git"
@ -17,15 +17,18 @@
"nfc"
],
"files": [
"src",
"ByteParser.js",
"NfcManager.js",
"NativeNfcManager.js",
"NdefParser.js",
"ndef-lib",
"index.d.ts",
"android",
"ios",
"react-native-nfc-manager.podspec",
"app.plugin.js"
"example"
],
"license": "MIT",
"license": "Apache-2.0",
"author": {
"name": "Richie",
"url": "https://github.com/whitedogg13"
@ -33,41 +36,14 @@
"jest": {
"testEnvironment": "node"
},
"babel": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime"
]
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@react-native-community/eslint-config": "^2.0.0",
"@release-it/conventional-changelog": "^2.0.1",
"dotenv-cli": "^4.0.0",
"eslint": "^7.19.0",
"jest": "^26.6.3",
"jest-cli": "^26.6.3",
"prettier": "^2.2.1",
"react": "^16.13.1",
"react-native": "^0.63.0",
"release-it": "^14.3.0",
"typescript": "^4.1.3"
},
"dependencies": {
"@expo/config-plugins": "^4.0.16"
"babel-core": "^6.26.0",
"babel-jest": "^22.4.3",
"babel-preset-env": "^1.6.1",
"jest": "^22.4.3",
"jest-cli": "^22.4.3"
},
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"test": "jest -i",
"release": "dotenv release-it --"
"test": "jest"
}
}

View File

@ -14,9 +14,5 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m}"
s.platform = :ios, "8.0"
s.dependency "React-Core"
s.xcconfig = {
'OTHER_LDFLAGS': '-weak_framework CoreNFC',
}
s.dependency "React"
end

View File

@ -1,49 +0,0 @@
## iOS
1. In [apple developer site](https://developer.apple.com/), enable capability for NFC
![enable capability](./images/enable-capability.png "enable capability")
2. in Xcode, add `NFCReaderUsageDescription` into your `info.plist`, for example:
```
<key>NFCReaderUsageDescription</key>
<string>We need to use NFC</string>
```
More info on Apple's [doc](https://developer.apple.com/documentation/bundleresources/information_property_list/nfcreaderusagedescription?language=objc)
Additionally, if writing ISO7816 tags add application identifiers (aid) into your `info.plist` as needed like this.
```
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>D2760000850100</string>
<string>D2760000850101</string>
</array>
```
More info on Apple's [doc](https://developer.apple.com/documentation/corenfc/nfciso7816tag)
An incomplete list of aid's can be found here. [Application identifier](https://www.eftlab.com/knowledge-base/211-emv-aid-rid-pix/)
3. in Xcode's `Signing & Capabilities` tab, make sure `Near Field Communication Tag Reading` capability had been added, like this:
![xcode-add-capability](./images/xcode-capability.png "xcode capability")
If this is the first time you toggle the capabilities, the Xcode will generate a `<your-project>.entitlement` file for you:
![xcode-add-entitlement](./images/xcode-entitlement.png "xcode entitlement")
4. in Xcode, review the generated entitlement. It should look like this:
![edit entitlement](./images/edit-entitlement.png "edit entitlement")
More info on Apple's [doc](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_nfc_readersession_formats?language=objc)
## Android
Simple add `uses-permission` into your `AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.NFC" />
```

View File

@ -1,33 +0,0 @@
'use strict';
import {NativeModules, NativeEventEmitter} from 'react-native';
const NativeNfcManager = NativeModules.NfcManager;
const NfcManagerEmitter = new NativeEventEmitter(NativeNfcManager);
function callNative(name, params = []) {
const nativeMethod = NativeNfcManager[name];
if (!nativeMethod) {
throw new Error(`no such native method: "${name}"`);
}
if (!Array.isArray(params)) {
throw new Error('params must be an array');
}
const createCallback = (resolve, reject) => (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
};
return new Promise((resolve, reject) => {
const callback = createCallback(resolve, reject);
const inputParams = [...params, callback];
nativeMethod(...inputParams);
});
}
export {NativeNfcManager, NfcManagerEmitter, callNative};

View File

@ -1,166 +0,0 @@
import {Platform} from 'react-native';
export class NfcErrorBase extends Error {}
export class UnsupportedFeature extends NfcErrorBase {}
export class SecurityViolation extends NfcErrorBase {}
export class InvalidParameter extends NfcErrorBase {}
export class InvalidParameterLength extends NfcErrorBase {}
export class ParameterOutOfBound extends NfcErrorBase {}
export class RadioDisabled extends NfcErrorBase {}
// transceive errors
export class TagConnectionLost extends NfcErrorBase {}
export class RetryExceeded extends NfcErrorBase {}
export class TagResponseError extends NfcErrorBase {}
export class SessionInvalidated extends NfcErrorBase {}
export class TagNotConnected extends NfcErrorBase {}
export class PacketTooLong extends NfcErrorBase {}
// reader session errors
export class UserCancel extends NfcErrorBase {}
export class Timeout extends NfcErrorBase {}
export class Unexpected extends NfcErrorBase {}
export class SystemBusy extends NfcErrorBase {}
export class FirstNdefInvalid extends NfcErrorBase {}
// tag command configuration errors
export class InvalidConfiguration extends NfcErrorBase {}
// ndef reader session error
export class TagNotWritable extends NfcErrorBase {}
export class TagUpdateFailure extends NfcErrorBase {}
export class TagSizeTooSmall extends NfcErrorBase {}
export class ZeroLengthMessage extends NfcErrorBase {}
export const NfcErrorIOS = {
errCodes: {
unknown: -1,
unsupportedFeature: 1,
securityViolation: 2,
invalidParameter: 3,
invalidParameterLength: 4,
parameterOutOfBound: 5,
radioDisabled: 6,
// transceive errors
tagConnectionLost: 100,
retryExceeded: 101,
tagResponseError: 102,
sessionInvalidated: 103,
tagNotConnected: 104,
packetTooLong: 105,
// reader session errors
userCancel: 200,
timeout: 201,
unexpected: 202,
systemBusy: 203,
firstNdefInvalid: 204,
// tag command configuration errors
invalidConfiguration: 300,
// ndef reader session error
tagNotWritable: 400,
tagUpdateFailure: 401,
tagSizeTooSmall: 402,
zeroLengthMessage: 403,
},
parse: (error) => {
if (typeof error === 'string') {
const [domainError] = error.split(',');
if (domainError) {
const [nfcError, nfcErrorCode] = domainError.split(':');
if (nfcError === 'NFCError') {
return parseInt(nfcErrorCode, 10);
}
}
} else if (error instanceof UserCancel) {
// this is for backward capability only
console.warn('API Deprecated: please use NfcError.UserCancel instead');
return NfcErrorIOS.errCodes.userCancel;
}
return NfcErrorIOS.errCodes.unknown;
},
};
export function buildNfcExceptionIOS(error) {
const [domainError] = error.split(',');
if (domainError) {
const [nfcError, nfcErrorCode] = domainError.split(':');
if (nfcError === 'NFCError') {
const code = parseInt(nfcErrorCode, 10);
if (code === NfcErrorIOS.errCodes.unsupportedFeature) {
return new UnsupportedFeature();
} else if (code === NfcErrorIOS.errCodes.securityViolation) {
return new SecurityViolation();
} else if (code === NfcErrorIOS.errCodes.invalidParameter) {
return new InvalidParameter();
} else if (code === NfcErrorIOS.errCodes.invalidParameterLength) {
return new InvalidParameterLength();
} else if (code === NfcErrorIOS.errCodes.parameterOutOfBound) {
return new ParameterOutOfBound();
} else if (code === NfcErrorIOS.errCodes.tagConnectionLost) {
return new TagConnectionLost();
} else if (code === NfcErrorIOS.errCodes.retryExceeded) {
return new RetryExceeded();
} else if (code === NfcErrorIOS.errCodes.tagResponseError) {
return new TagResponseError();
} else if (code === NfcErrorIOS.errCodes.sessionInvalidated) {
return new SessionInvalidated();
} else if (code === NfcErrorIOS.errCodes.tagNotConnected) {
return new TagNotConnected();
} else if (code === NfcErrorIOS.errCodes.packetTooLong) {
return new PacketTooLong();
} else if (code === NfcErrorIOS.errCodes.userCancel) {
return new UserCancel();
} else if (code === NfcErrorIOS.errCodes.timeout) {
return new Timeout();
} else if (code === NfcErrorIOS.errCodes.unexpected) {
return new Unexpected();
} else if (code === NfcErrorIOS.errCodes.systemBusy) {
return new SystemBusy();
} else if (code === NfcErrorIOS.errCodes.firstNdefInvalid) {
return new FirstNdefInvalid();
} else if (code === NfcErrorIOS.errCodes.invalidConfiguration) {
return new InvalidConfiguration();
} else if (code === NfcErrorIOS.errCodes.tagNotWritable) {
return new TagNotWritable();
} else if (code === NfcErrorIOS.errCodes.tagUpdateFailure) {
return new TagUpdateFailure();
} else if (code === NfcErrorIOS.errCodes.tagSizeTooSmall) {
return new TagSizeTooSmall();
} else if (code === NfcErrorIOS.errCodes.zeroLengthMessage) {
return new ZeroLengthMessage();
}
}
}
return new NfcErrorBase(error);
}
export function buildNfcExceptionAndroid(error) {
if (error === 'cancelled') {
return new UserCancel();
}
return new NfcErrorBase(error);
}
export async function handleNativeException(
callNativePromise,
ignoreError = false,
) {
try {
return await callNativePromise;
} catch (err) {
if (!ignoreError) {
// the error from the native side will always be a string
if (typeof err === 'string') {
if (Platform.OS === 'ios') {
throw buildNfcExceptionIOS(err);
} else if (Platform.OS === 'android') {
throw buildNfcExceptionAndroid(err);
}
}
// unexpected condition, simply throws them out without conversion
throw err;
}
}
}

View File

@ -1,227 +0,0 @@
'use strict';
import {Platform} from 'react-native';
import {
NativeNfcManager,
NfcManagerEmitter,
callNative,
} from './NativeNfcManager';
import {NdefHandler, NdefStatus} from './NfcTech/NdefHandler';
import {NfcAHandler} from './NfcTech/NfcAHandler';
import {NfcVHandler} from './NfcTech/NfcVHandler';
import {IsoDepHandler} from './NfcTech/IsoDepHandler';
import {
handleNativeException,
buildNfcExceptionIOS,
UserCancel,
} from './NfcError';
const NfcEvents = {
DiscoverTag: 'NfcManagerDiscoverTag',
DiscoverBackgroundTag: 'NfcManagerDiscoverBackgroundTag',
SessionClosed: 'NfcManagerSessionClosed',
StateChanged: 'NfcManagerStateChanged',
};
const NfcTech = {
Ndef: 'Ndef',
NfcA: 'NfcA',
NfcB: 'NfcB',
NfcF: 'NfcF',
NfcV: 'NfcV',
IsoDep: 'IsoDep',
MifareClassic: 'MifareClassic',
MifareUltralight: 'MifareUltralight',
MifareIOS: 'mifare',
Iso15693IOS: 'iso15693',
FelicaIOS: 'felica',
NdefFormatable: 'NdefFormatable',
};
const DEFAULT_REGISTER_TAG_EVENT_OPTIONS = {
alertMessage: 'Please tap NFC tags',
invalidateAfterFirstRead: false,
isReaderModeEnabled: false,
readerModeFlags: 0,
readerModeDelay: 10,
};
function NotImpl() {
throw new Error('not implemented');
}
async function DoNothing() {
// allow derived class to not implment it
}
class NfcManagerBase {
constructor() {
this._subscribeNativeEvents();
}
async start() {
return handleNativeException(callNative('start'));
}
async isSupported(tech = '') {
return handleNativeException(callNative('isSupported', [tech]));
}
async registerTagEvent(options = {}) {
const optionsWithDefault = {
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
...options,
};
return handleNativeException(
callNative('registerTagEvent', [optionsWithDefault]),
);
}
async unregisterTagEvent() {
return handleNativeException(callNative('unregisterTagEvent'));
}
async getTag() {
return handleNativeException(callNative('getTag'));
}
setEventListener(name, callback) {
const allNfcEvents = Object.keys(NfcEvents).map((k) => NfcEvents[k]);
if (allNfcEvents.indexOf(name) === -1) {
throw new Error('no such event');
}
this._clientListeners[name] = callback;
}
requestTechnology = NotImpl;
cancelTechnologyRequest = NotImpl;
getBackgroundTag = NotImpl;
clearBackgroundTag = NotImpl;
setAlertMessage = DoNothing;
async writeNdefMessage(bytes) {
return handleNativeException(callNative('writeNdefMessage', [bytes]));
}
async getNdefMessage() {
return handleNativeException(callNative('getNdefMessage'));
}
get ndefHandler() {
if (!this._ndefHandler) {
this._ndefHandler = new NdefHandler();
}
return this._ndefHandler;
}
get nfcAHandler() {
if (!this._nfcAHandler) {
this._nfcAHandler = new NfcAHandler();
}
return this._nfcAHandler;
}
get nfcVHandler() {
if (!this._nfcVHandler) {
this._nfcVHandler = new NfcVHandler();
}
return this._nfcVHandler;
}
get isoDepHandler() {
if (!this._isoDepHandler) {
this._isoDepHandler = new IsoDepHandler();
}
return this._isoDepHandler;
}
get MIFARE_BLOCK_SIZE() {
return NativeNfcManager.MIFARE_BLOCK_SIZE;
}
get MIFARE_ULTRALIGHT_PAGE_SIZE() {
return NativeNfcManager.MIFARE_ULTRALIGHT_PAGE_SIZE;
}
get MIFARE_ULTRALIGHT_TYPE() {
return NativeNfcManager.MIFARE_ULTRALIGHT_TYPE;
}
get MIFARE_ULTRALIGHT_TYPE_C() {
return NativeNfcManager.MIFARE_ULTRALIGHT_TYPE_C;
}
get MIFARE_ULTRALIGHT_TYPE_UNKNOWN() {
return NativeNfcManager.MIFARE_ULTRALIGHT_TYPE_UNKNOWN;
}
_onDiscoverTag = (tag) => {
const callback = this._clientListeners[NfcEvents.DiscoverTag];
if (callback) {
callback(tag);
}
};
_onDiscoverBackgroundTag = (tag) => {
const callback = this._clientListeners[NfcEvents.DiscoverBackgroundTag];
if (callback) {
callback(tag);
}
};
_onSessionClosedIOS = (resp) => {
const callback = this._clientListeners[NfcEvents.SessionClosed];
if (callback) {
const error = buildNfcExceptionIOS(resp.error);
callback(error instanceof UserCancel ? null : error);
}
};
_onStateChangedAndroid = (state) => {
const callback = this._clientListeners[NfcEvents.StateChanged];
if (callback) {
callback(state);
}
};
_subscribeNativeEvents = () => {
this._subscriptions = {};
this._clientListeners = {};
this._subscriptions[NfcEvents.DiscoverTag] = NfcManagerEmitter.addListener(
NfcEvents.DiscoverTag,
this._onDiscoverTag,
);
this._subscriptions[NfcEvents.DiscoverBackgroundTag] = NfcManagerEmitter.addListener(
NfcEvents.DiscoverBackgroundTag,
this._onDiscoverBackgroundTag,
);
if (Platform.OS === 'ios') {
this._subscriptions[
NfcEvents.SessionClosed
] = NfcManagerEmitter.addListener(
NfcEvents.SessionClosed,
this._onSessionClosedIOS,
);
}
if (Platform.OS === 'android') {
this._subscriptions[
NfcEvents.StateChanged
] = NfcManagerEmitter.addListener(
NfcEvents.StateChanged,
this._onStateChangedAndroid,
);
}
};
}
export {
NfcTech,
NfcEvents,
NfcManagerBase,
NdefStatus,
DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
};

View File

@ -1,135 +0,0 @@
import {callNative} from './NativeNfcManager';
import {NfcManagerBase} from './NfcManager';
import {MifareClassicHandlerAndroid} from './NfcTech/MifareClassicHandlerAndroid';
import {MifareUltralightHandlerAndroid} from './NfcTech/MifareUltralightHandlerAndroid';
import {NdefFormatableHandlerAndroid} from './NfcTech/NdefFormatableHandlerAndroid';
import {handleNativeException, buildNfcExceptionAndroid} from './NfcError';
const NfcAdapter = {
FLAG_READER_NFC_A: 0x1,
FLAG_READER_NFC_B: 0x2,
FLAG_READER_NFC_F: 0x4,
FLAG_READER_NFC_V: 0x8,
FLAG_READER_NFC_BARCODE: 0x10,
FLAG_READER_SKIP_NDEF_CHECK: 0x80,
FLAG_READER_NO_PLATFORM_SOUNDS: 0x100,
};
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
class NfcManagerAndroid extends NfcManagerBase {
constructor() {
super();
this.cleanUpTagRegistration = false;
}
requestTechnology = async (tech, options = {}) => {
try {
if (typeof tech === 'string') {
tech = [tech];
}
const sessionAvailable = await this._hasTagEventRegistrationAndroid();
// make sure we do register for tag event
if (!sessionAvailable) {
await this.registerTagEvent(options);
this.cleanUpTagRegistration = true;
}
return await callNative('requestTechnology', [tech]);
} catch (ex) {
throw buildNfcExceptionAndroid(ex);
}
};
cancelTechnologyRequest = async (options = {}) => {
const {throwOnError = false, delayMsAndroid = 1000} = options;
try {
await callNative('cancelTechnologyRequest');
if (this.cleanUpTagRegistration) {
await delay(delayMsAndroid);
await this.unregisterTagEvent();
this.cleanUpTagRegistration = false;
}
} catch (ex) {
if (throwOnError) {
throw buildNfcExceptionAndroid(ex);
}
}
};
getBackgroundTag = () =>
handleNativeException(callNative('getBackgroundTag'));
clearBackgroundTag = () =>
handleNativeException(callNative('clearBackgroundTag'));
// -------------------------------------
// public only for Android
// -------------------------------------
isEnabled = () => handleNativeException(callNative('isEnabled'));
goToNfcSetting = () => handleNativeException(callNative('goToNfcSetting'));
getLaunchTagEvent = () =>
handleNativeException(callNative('getLaunchTagEvent'));
setNdefPushMessage = (bytes) =>
handleNativeException(callNative('setNdefPushMessage', [bytes]));
setTimeout = (timeout) =>
handleNativeException(callNative('setTimeout', [timeout]));
connect = (techs) => handleNativeException(callNative('connect', [techs]));
close = () => handleNativeException(callNative('close'));
transceive = (bytes) =>
handleNativeException(callNative('transceive', [bytes]));
getMaxTransceiveLength = () =>
handleNativeException(callNative('getMaxTransceiveLength'));
// -------------------------------------
// (android) NfcTech.MifareClassic API
// -------------------------------------
get mifareClassicHandlerAndroid() {
if (!this._mifareClassicHandlerAndroid) {
this._mifareClassicHandlerAndroid = new MifareClassicHandlerAndroid(this);
}
return this._mifareClassicHandlerAndroid;
}
// -------------------------------------
// (android) NfcTech.MifareUltralight API
// -------------------------------------
get mifareUltralightHandlerAndroid() {
if (!this._mifareUltralightHandlerAndroid) {
this._mifareUltralightHandlerAndroid = new MifareUltralightHandlerAndroid(
this,
);
}
return this._mifareUltralightHandlerAndroid;
}
// -------------------------------------
// (android) NfcTech.NdefFormatable API
// -------------------------------------
get ndefFormatableHandlerAndroid() {
if (!this._ndefFormatableHandlerAndroid) {
this._ndefFormatableHandlerAndroid = new NdefFormatableHandlerAndroid(this);
}
return this._ndefFormatableHandlerAndroid;
}
// -------------------------------------
// Android private
// -------------------------------------
_hasTagEventRegistrationAndroid = () =>
handleNativeException(callNative('hasTagEventRegistration'));
}
export {NfcAdapter, NfcManagerAndroid};

View File

@ -1,151 +0,0 @@
'use strict';
import {Platform} from 'react-native';
import {NativeNfcManager, callNative} from './NativeNfcManager';
import {
NfcTech,
NfcManagerBase,
DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
} from './NfcManager';
import {
Nfc15693RequestFlagIOS,
Nfc15693ResponseFlagIOS,
Iso15693HandlerIOS,
} from './NfcTech/Iso15693HandlerIOS';
import {handleNativeException} from './NfcError';
class NfcManagerIOS extends NfcManagerBase {
constructor() {
super();
}
isEnabled = async () => {
return true;
};
requestTechnology = async (tech, options = {}) => {
if (typeof tech === 'string') {
tech = [tech];
}
const techList = [];
for (const t of tech) {
if (t === NfcTech.NfcA) {
techList.push(NfcTech.MifareIOS);
} else if (t === NfcTech.NfcV) {
techList.push(NfcTech.Iso15693IOS);
} else {
techList.push(t);
}
}
return handleNativeException(
callNative('requestTechnology', [
techList,
{
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
...options,
},
]),
);
};
cancelTechnologyRequest = async (options = {}) => {
const {throwOnError = false} = options;
return handleNativeException(
callNative('cancelTechnologyRequest'),
!throwOnError,
);
};
getBackgroundTag = async () => {
// iOS doesn't report the full tag, only the ndefMessage part
const ndefMessage = await handleNativeException(callNative('getBackgroundNdef'));
return ndefMessage ? { ndefMessage } : null;
};
clearBackgroundTag = async () => callNative('clearBackgroundNdef');
// -------------------------------------
// public only for iOS
// -------------------------------------
getBackgroundNdef = () =>
handleNativeException(callNative('getBackgroundNdef'));
setAlertMessage = (alertMessage) =>
handleNativeException(callNative('setAlertMessage', [alertMessage]));
setAlertMessageIOS = (alertMessage) =>
handleNativeException(callNative('setAlertMessage', [alertMessage]));
invalidateSessionIOS = () =>
handleNativeException(callNative('invalidateSession'));
invalidateSessionWithErrorIOS = (errorMessage = 'Error') =>
handleNativeException(
callNative('invalidateSessionWithError', [errorMessage]),
);
// -------------------------------------
// (iOS) NfcTech.MifareIOS API
// -------------------------------------
sendMifareCommandIOS = (bytes) =>
handleNativeException(callNative('sendMifareCommand', [bytes]));
// -------------------------------------
// (iOS) NfcTech.FelicaIOS API
// -------------------------------------
sendFelicaCommandIOS = (bytes) =>
handleNativeException(callNative('sendFelicaCommand', [bytes]));
// -------------------------------------
// (iOS) NfcTech.IsoDep API
// -------------------------------------
sendCommandAPDUIOS = (bytesOrApdu) => {
if (Platform.OS !== 'ios') {
return Promise.reject('not implemented');
}
if (Array.isArray(bytesOrApdu)) {
const bytes = bytesOrApdu;
return handleNativeException(
new Promise((resolve, reject) => {
NativeNfcManager.sendCommandAPDUBytes(
bytes,
(err, response, sw1, sw2) => {
if (err) {
reject(err);
} else {
resolve({response, sw1, sw2});
}
},
);
}),
);
} else {
const apdu = bytesOrApdu;
return handleNativeException(
new Promise((resolve, reject) => {
NativeNfcManager.sendCommandAPDU(apdu, (err, response, sw1, sw2) => {
if (err) {
reject(err);
} else {
resolve({response, sw1, sw2});
}
});
}),
);
}
};
// -------------------------------------
// (iOS) NfcTech.Iso15693IOS API
// -------------------------------------
get iso15693HandlerIOS() {
if (!this._iso15693HandlerIOS) {
this._iso15693HandlerIOS = new Iso15693HandlerIOS();
}
return this._iso15693HandlerIOS;
}
}
export {NfcManagerIOS, Nfc15693RequestFlagIOS, Nfc15693ResponseFlagIOS};

View File

@ -1,131 +0,0 @@
import {callNative} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
const Nfc15693RequestFlagIOS = {
DualSubCarriers: 1 << 0,
HighDataRate: 1 << 1,
ProtocolExtension: 1 << 3,
Select: 1 << 4,
Address: 1 << 5,
Option: 1 << 6,
CommandSpecificBit8: 1 << 7,
};
const Nfc15693ResponseFlagIOS = {
Error: 1 << 0,
ResponseBufferValid: 1 << 1,
FinalResponse: 1 << 2,
ProtocolExtension: 1 << 3,
BlockSecurityStatusBit5: 1 << 4,
BlockSecurityStatusBit6: 1 << 5,
WaitTimeExtension: 1 << 6
};
class Iso15693HandlerIOS {
getSystemInfo(requestFlag) {
return handleNativeException(
callNative('iso15693_getSystemInfo', [requestFlag]),
);
}
readSingleBlock({flags, blockNumber}) {
return handleNativeException(
callNative('iso15693_readSingleBlock', [{flags, blockNumber}]),
);
}
readMultipleBlocks({flags, blockNumber, blockCount}) {
return handleNativeException(
callNative('iso15693_readMultipleBlocks', [
{flags, blockNumber, blockCount},
]),
);
}
writeSingleBlock({flags, blockNumber, dataBlock}) {
return handleNativeException(
callNative('iso15693_writeSingleBlock', [
{flags, blockNumber, dataBlock},
]),
);
}
lockBlock({flags, blockNumber}) {
return handleNativeException(
callNative('iso15693_lockBlock', [{flags, blockNumber}]),
);
}
writeAFI({flags, afi}) {
return handleNativeException(
callNative('iso15693_writeAFI', [{flags, afi}]),
);
}
lockAFI({flags}) {
return handleNativeException(callNative('iso15693_lockAFI', [{flags}]));
}
writeDSFID({flags, dsfid}) {
return handleNativeException(
callNative('iso15693_writeDSFID', [{flags, dsfid}]),
);
}
lockDSFID({flags}) {
return handleNativeException(callNative('iso15693_lockDSFID', [{flags}]));
}
resetToReady({flags}) {
return handleNativeException(
callNative('iso15693_resetToReady', [{flags}]),
);
}
select({flags}) {
return handleNativeException(callNative('iso15693_select', [{flags}]));
}
stayQuite() {
return handleNativeException(callNative('iso15693_stayQuiet'));
}
customCommand({flags, customCommandCode, customRequestParameters}) {
return handleNativeException(
callNative('iso15693_customCommand', [
{flags, customCommandCode, customRequestParameters},
]),
);
}
// https://developer.apple.com/documentation/corenfc/nfciso15693tag/3551933-sendrequestwithflag?language=objc
sendRequest({flags, commandCode, data}) {
return handleNativeException(
callNative('iso15693_sendRequest', [
{flags, commandCode, data},
]),
);
}
extendedReadSingleBlock({flags, blockNumber}) {
return handleNativeException(
callNative('iso15693_extendedReadSingleBlock', [{flags, blockNumber}]),
);
}
extendedWriteSingleBlock({flags, blockNumber, dataBlock}) {
return handleNativeException(
callNative('iso15693_extendedWriteSingleBlock', [
{flags, blockNumber, dataBlock},
]),
);
}
extendedLockBlock({flags, blockNumber}) {
return handleNativeException(
callNative('iso15693_extendedLockBlock', [{flags, blockNumber}]),
);
}
}
export {Nfc15693RequestFlagIOS, Nfc15693ResponseFlagIOS, Iso15693HandlerIOS};

View File

@ -1,35 +0,0 @@
import {Platform} from 'react-native';
import {callNative, NativeNfcManager} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
class IsoDepHandler {
async transceive(bytes) {
if (!Array.isArray(bytes)) {
throw new Error(
'IsoDepHandler.transceive only takes input as a byte array',
);
}
if (Platform.OS === 'ios') {
return handleNativeException(
new Promise((resolve, reject) => {
NativeNfcManager.sendCommandAPDUBytes(
bytes,
(err, response, sw1, sw2) => {
if (err) {
reject(err);
} else {
// TODO: make following data the same format as Android
resolve([...response, sw1, sw2]);
}
},
);
}),
);
}
return handleNativeException(callNative('transceive', [bytes]));
}
}
export {IsoDepHandler};

View File

@ -1,114 +0,0 @@
import {callNative} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
class MifareClassicHandlerAndroid {
constructor(nfcManager) {
this.nfcManager = nfcManager;
}
async mifareClassicAuthenticateA(sector, key) {
if (!key || !Array.isArray(key) || key.length !== 6) {
throw new Error('key should be an Array[6] of integers (0 - 255)');
}
return handleNativeException(
callNative('mifareClassicAuthenticateA', [sector, key]),
);
}
async mifareClassicAuthenticateB(sector, key) {
if (!key || !Array.isArray(key) || key.length !== 6) {
throw new Error('key should be an Array[6] of integers (0 - 255)');
}
return handleNativeException(
callNative('mifareClassicAuthenticateB', [sector, key]),
);
}
async mifareClassicGetBlockCountInSector(sector) {
return handleNativeException(
callNative('mifareClassicGetBlockCountInSector', [sector]),
);
}
async mifareClassicGetSectorCount() {
return handleNativeException(callNative('mifareClassicGetSectorCount'));
}
async mifareClassicSectorToBlock(sector) {
return handleNativeException(
callNative('mifareClassicSectorToBlock', [sector]),
);
}
async mifareClassicReadBlock(block) {
return handleNativeException(callNative('mifareClassicReadBlock', [block]));
}
async mifareClassicReadSector(sector) {
return handleNativeException(
callNative('mifareClassicReadSector', [sector]),
);
}
async mifareClassicWriteBlock(block, data) {
if (
!data ||
!Array.isArray(data) ||
data.length !== this.nfcManager.MIFARE_BLOCK_SIZE
) {
throw new Error(
`data should be a non-empty Array[${this.nfcManager.MIFARE_BLOCK_SIZE}] of integers (0 - 255)`,
);
}
return handleNativeException(
callNative('mifareClassicWriteBlock', [block, data]),
);
}
async mifareClassicIncrementBlock(block, value) {
if (
!value ||
Number.isNaN(value)) {
throw new Error(
`value should be a number`,
);
}
return handleNativeException(
callNative('mifareClassicIncrementBlock', [block, value]),
);
}
async mifareClassicDecrementBlock(block, value) {
if (
!value ||
Number.isNaN(value)) {
throw new Error(
`value should be a number`,
);
}
return handleNativeException(
callNative('mifareClassicDecrementBlock', [block, value]),
);
}
async mifareClassicTransferBlock(block) {
if (
!block ||
Number.isNaN(block)) {
throw new Error(
`block should be a number`,
);
}
return handleNativeException(
callNative('mifareClassicTransferBlock', [block]),
);
}
}
export {MifareClassicHandlerAndroid};

View File

@ -1,32 +0,0 @@
import {callNative} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
class MifareUltralightHandlerAndroid {
constructor(nfcManager) {
this.nfcManager = nfcManager;
}
async mifareUltralightReadPages(pageOffset) {
return handleNativeException(
callNative('mifareUltralightReadPages', [pageOffset]),
);
}
async mifareUltralightWritePage(pageOffset, data) {
if (
!data ||
!Array.isArray(data) ||
data.length !== this.nfcManager.MIFARE_ULTRALIGHT_PAGE_SIZE
) {
throw new Error(
`data should be a non-empty Array[${this.nfcManager.MIFARE_ULTRALIGHT_PAGE_SIZE}] of integers (0 - 255)`,
);
}
return handleNativeException(
callNative('mifareUltralightWritePage', [pageOffset, data]),
);
}
}
export {MifareUltralightHandlerAndroid};

View File

@ -1,16 +0,0 @@
import {callNative} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
class NdefFormatableHandlerAndroid {
async formatNdef(bytes, options={}) {
const defaultOptions = { readOnly: false };
return handleNativeException(
callNative('formatNdef', [
bytes,
{...defaultOptions, ...options}
])
);
}
}
export {NdefFormatableHandlerAndroid};

View File

@ -1,50 +0,0 @@
import {Platform} from 'react-native';
import {callNative} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
const NdefStatus = {
NotSupported: 1,
ReadWrite: 2,
ReadOnly: 3,
};
class NdefHandler {
async writeNdefMessage(bytes) {
return handleNativeException(callNative('writeNdefMessage', [bytes]));
}
async getNdefMessage() {
return handleNativeException(callNative('getNdefMessage'));
}
async makeReadOnly() {
return handleNativeException(callNative('makeReadOnly'));
}
async getNdefStatus() {
if (Platform.OS === 'ios') {
return handleNativeException(callNative('queryNDEFStatus'));
} else {
try {
const result = await handleNativeException(callNative('getNdefStatus'));
return {
status: result.isWritable
? NdefStatus.ReadWrite
: NdefStatus.ReadOnly,
capacity: result.maxSize,
};
} catch (ex) {
return {
status: NdefStatus.NotSupported,
capacity: 0,
};
}
}
}
async getCachedNdefMessageAndroid() {
return handleNativeException(callNative('getCachedNdefMessage'));
}
}
export {NdefHandler, NdefStatus};

View File

@ -1,14 +0,0 @@
import {Platform} from 'react-native';
import {callNative} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
class NfcAHandler {
async transceive(bytes) {
if (Platform.OS === 'ios') {
return handleNativeException(callNative('sendMifareCommand', [bytes]));
}
return handleNativeException(callNative('transceive', [bytes]));
}
}
export {NfcAHandler};

View File

@ -1,39 +0,0 @@
import {Platform} from 'react-native';
import {callNative, NativeNfcManager} from '../NativeNfcManager';
import {handleNativeException} from '../NfcError';
class NfcVHandler {
async transceive(bytes) {
if (!Array.isArray(bytes)) {
throw new Error(
'IsoDepHandler.transceive only takes input as a byte array',
);
}
if (Platform.OS === 'ios') {
const [flags, commandCode, ...data] = bytes;
return handleNativeException(
new Promise((resolve, reject) => {
NativeNfcManager.iso15693_sendRequest(
{
flags,
commandCode,
data,
},
(err, responseFlag, response) => {
if (err) {
reject(err);
} else {
resolve([responseFlag, ...response]);
}
},
);
}),
);
}
return handleNativeException(callNative('transceive', [bytes]));
}
}
export {NfcVHandler};

View File

@ -1,35 +0,0 @@
let _nextError = null;
let _nextErrorMethod = null;
const NativeNfcManager = {
MIFARE_BLOCK_SIZE: 16,
MIFARE_ULTRALIGHT_PAGE_SIZE: 4,
setNextError: (err, nativeMethodName = null) => {
_nextError = err;
_nextErrorMethod = nativeMethodName;
},
};
const NfcManagerEmitterListener = {};
const NfcManagerEmitter = {
addListener: jest.fn((name, callback) => {
NfcManagerEmitterListener[name] = callback;
}),
_testTriggerCallback: (name, data) => {
NfcManagerEmitterListener[name](data);
},
};
const callNative = jest.fn((...args) => {
// eslint-disable-next-line no-unused-vars
const [methodName, ...rest] = args;
if (_nextError && (!methodName || methodName === _nextErrorMethod)) {
const err = _nextError;
_nextError = null;
_nextErrorMethod = null;
return Promise.reject(err);
}
});
export {NativeNfcManager, NfcManagerEmitter, callNative};

View File

@ -1,35 +0,0 @@
import {Platform} from 'react-native';
import Ndef from '../ndef-lib';
import {NfcEvents, NfcTech, NdefStatus} from './NfcManager';
import {NfcAdapter, NfcManagerAndroid} from './NfcManagerAndroid';
import {
Nfc15693RequestFlagIOS,
Nfc15693ResponseFlagIOS,
NfcManagerIOS,
} from './NfcManagerIOS';
import * as NfcError from './NfcError';
const nfcManager = (() => {
if (Platform.OS === 'ios') {
return new NfcManagerIOS();
} else {
return new NfcManagerAndroid();
}
})();
// only for backward-capability
const NfcErrorIOS = NfcError.NfcErrorIOS;
export default nfcManager;
export {
NfcTech,
NfcEvents,
NfcAdapter,
Nfc15693RequestFlagIOS,
Nfc15693ResponseFlagIOS,
Ndef,
NdefStatus,
NfcError,
NfcErrorIOS,
};