Compare commits
309 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee0dbed93c | ||
|
|
648aeb95ca | ||
|
|
0ea6a38721 | ||
|
|
9607b509b5 | ||
|
|
899856dc4c | ||
|
|
eaa7f7ad22 | ||
|
|
2572d6271a | ||
|
|
43a89b88a6 | ||
|
|
0b7a9622b6 | ||
|
|
8184c65e6f | ||
|
|
02b7a9e157 | ||
|
|
6dadd629f9 | ||
|
|
e829e3693a | ||
|
|
c9b9013c6d | ||
|
|
e8f8863436 | ||
|
|
527bebd15f | ||
|
|
5aef17bcd6 | ||
|
|
eff40abc01 | ||
|
|
5c02feea32 | ||
|
|
aebfa93318 | ||
|
|
884120826c | ||
|
|
f4a05a08d3 | ||
|
|
90394fd4a0 | ||
|
|
a190d06482 | ||
|
|
f4e0615d35 | ||
|
|
543b733e54 | ||
|
|
228d1fbb44 | ||
|
|
f4a31269e9 | ||
|
|
abd84bb113 | ||
|
|
83eeda33be | ||
|
|
38d1f4e8ad | ||
|
|
c396c553ff | ||
|
|
2f7c87182a | ||
|
|
cd0ea79f70 | ||
|
|
7003eebbff | ||
|
|
da661500e6 | ||
|
|
391f229a40 | ||
|
|
f6fe75306e | ||
|
|
14d49fc28b | ||
|
|
ba676d9ea5 | ||
|
|
198c03e238 | ||
|
|
d1c79c79ad | ||
|
|
d385f16067 | ||
|
|
8687bc49f9 | ||
|
|
8b90348f33 | ||
|
|
d35545ccd4 | ||
|
|
99c195847c | ||
|
|
90fa88623c | ||
|
|
e1fabc6f3d | ||
|
|
1d1eecef19 | ||
|
|
b3a6fc2780 | ||
|
|
9cd1b7e50e | ||
|
|
6361cc630a | ||
|
|
94874b3216 | ||
|
|
ce60aa983f | ||
|
|
8c077e492b | ||
|
|
bb6c5767aa | ||
|
|
7f6627d452 | ||
|
|
fa55d62958 | ||
|
|
37d02c03d1 | ||
|
|
42baa33bef | ||
|
|
67b4cde9c1 | ||
|
|
3c2cb1e758 | ||
|
|
18a74dbe8c | ||
|
|
35b904c62a | ||
|
|
9dba218f09 | ||
|
|
d5e00c6dca | ||
|
|
14251db428 | ||
|
|
ca01f31a84 | ||
|
|
adb65e895f | ||
|
|
22402a2eb0 | ||
|
|
59bc7f58e3 | ||
|
|
a8760cfb39 | ||
|
|
280f3a2310 | ||
|
|
1072faab45 | ||
|
|
863ac928f4 | ||
|
|
5e0978fd46 | ||
|
|
2f027ef344 | ||
|
|
fd3a25c342 | ||
|
|
dc7e0deda8 | ||
|
|
eb74466bb1 | ||
|
|
374b8bef95 | ||
|
|
484314e4fc | ||
|
|
8b751ad5c2 | ||
|
|
adc847b1f6 | ||
|
|
7ab86f4481 | ||
|
|
c272b0fb21 | ||
|
|
e369429326 | ||
|
|
4cce6275c3 | ||
|
|
2274106ea3 | ||
|
|
650d1186d5 | ||
|
|
245c1c4f63 | ||
|
|
588df7b9f1 | ||
|
|
a06b96325d | ||
|
|
9c89f7fdf8 | ||
|
|
0d9be43ea4 | ||
|
|
de0c2c0684 | ||
|
|
1aae79383d | ||
|
|
d7e1047b8d | ||
|
|
02c942ad08 | ||
|
|
00629a6f5a | ||
|
|
0259f4b009 | ||
|
|
b2cfe2efe5 | ||
|
|
3c44eabd60 | ||
|
|
e190066be6 | ||
|
|
b6c3b0d319 | ||
|
|
c7eeb9934d | ||
|
|
6ae8d02112 | ||
|
|
43fd209335 | ||
|
|
4f3427b84a | ||
|
|
160dfd70c4 | ||
|
|
d039d0df59 | ||
|
|
b8f3345906 | ||
|
|
d5397d55af | ||
|
|
18f3f4427d | ||
|
|
91899feb1b | ||
|
|
0c35b51525 | ||
|
|
0f7b9856d1 | ||
|
|
1cf21205a1 | ||
|
|
44c4a20c5f | ||
|
|
3a78967673 | ||
|
|
2a8291e392 | ||
|
|
09e792f615 | ||
|
|
f33bc989fd | ||
|
|
ec0d0fb2b5 | ||
|
|
58c551be3d | ||
|
|
fd38edde10 | ||
|
|
d1eea850a7 | ||
|
|
ad1e45d2bc | ||
|
|
354e77d290 | ||
|
|
993013ab31 | ||
|
|
5c8cfd4820 | ||
|
|
a0bb82cd67 | ||
|
|
c410ff438e | ||
|
|
5834344efc | ||
|
|
363dc8fc30 | ||
|
|
d700071ba8 | ||
|
|
bca427fc80 | ||
|
|
8b7f8629a8 | ||
|
|
6c640721c8 | ||
|
|
0a28937ec5 | ||
|
|
b1364197c8 | ||
|
|
084d511f0f | ||
|
|
5f72f0cf04 | ||
|
|
6d8ae7ed81 | ||
|
|
26516a7ce8 | ||
|
|
3b197f3eec | ||
|
|
9068abf4f2 | ||
|
|
8b45c911ef | ||
|
|
f019aa6234 | ||
|
|
b3e42b1506 | ||
|
|
824854837d | ||
|
|
934d983a77 | ||
|
|
2ccc038f85 | ||
|
|
32c6d2965d | ||
|
|
5a83f66e09 | ||
|
|
bbe12ef834 | ||
|
|
4dd0b4b771 | ||
|
|
5f3607ee07 | ||
|
|
54d90d2e9f | ||
|
|
545c3a3899 | ||
|
|
104292bd69 | ||
|
|
4c94b1554b | ||
|
|
a4b4a0dd46 | ||
|
|
0032408af2 | ||
|
|
4cc68ce233 | ||
|
|
32dd3be296 | ||
|
|
64115f80f8 | ||
|
|
b7984a59ea | ||
|
|
9af9ed31b5 | ||
|
|
312aed2659 | ||
|
|
c95cc76a0a | ||
|
|
2d6150d652 | ||
|
|
14851a5865 | ||
|
|
0ae08ae1c3 | ||
|
|
ad29dfe0cc | ||
|
|
5b19ff03f8 | ||
|
|
ba08fe673d | ||
|
|
49ef6815ae | ||
|
|
92f2d12c8f | ||
|
|
f5dc6f8b70 | ||
|
|
1281d0b5f7 | ||
|
|
7f44e067b8 | ||
|
|
9d17ff41bb | ||
|
|
86200ff53e | ||
|
|
587efbf723 | ||
|
|
c83750e686 | ||
|
|
94eae4c591 | ||
|
|
70f8ead083 | ||
|
|
f86e63ed1a | ||
|
|
f9b9f41488 | ||
|
|
8b7b7f620f | ||
|
|
f5f9741685 | ||
|
|
2468eada87 | ||
|
|
d46de6f96d | ||
|
|
aecd7fd689 | ||
|
|
5e1586d1e0 | ||
|
|
82c9b519cf | ||
|
|
889c33c1d7 | ||
|
|
b944436e8c | ||
|
|
d0e163ed3a | ||
|
|
f8fac99c0d | ||
|
|
2d60e596d3 | ||
|
|
d8150c2d64 | ||
|
|
560abfcc85 | ||
|
|
5080b1ba70 | ||
|
|
324c95a0d3 | ||
|
|
300b8fc1b1 | ||
|
|
995fb88f92 | ||
|
|
b805f3a6e3 | ||
|
|
c4594e61ee | ||
|
|
8572cabfca | ||
|
|
27ab072aa6 | ||
|
|
65273ce0ac | ||
|
|
5935bd67f2 | ||
|
|
67b3d5b9a2 | ||
|
|
a6371d1693 | ||
|
|
7e0b478b4c | ||
|
|
aaf385fb5c | ||
|
|
ec9bc133f7 | ||
|
|
053e4de272 | ||
|
|
a10ddb7fe2 | ||
|
|
2cd94cecaf | ||
|
|
ce61b3f265 | ||
|
|
ad32b07218 | ||
|
|
54d55d18c4 | ||
|
|
3331bf9e4c | ||
|
|
5c249a7f20 | ||
|
|
7f5af65631 | ||
|
|
c6035b3703 | ||
|
|
7915b98132 | ||
|
|
126e66f7b4 | ||
|
|
64c9cbd05e | ||
|
|
6ddbb0814f | ||
|
|
73859fbac8 | ||
|
|
53ff1787e6 | ||
|
|
581e7c4da7 | ||
|
|
995e2f0315 | ||
|
|
1c086af0cb | ||
|
|
f32f92f16a | ||
|
|
65a3306a4e | ||
|
|
13715334ea | ||
|
|
6fc257e61e | ||
|
|
550c24a2a1 | ||
|
|
03b29872af | ||
|
|
741a37a447 | ||
|
|
99995b7a1e | ||
|
|
968e8a8cfe | ||
|
|
e274a60213 | ||
|
|
78b6c69906 | ||
|
|
1c0ae74786 | ||
|
|
198e1de6eb | ||
|
|
3779ca21cd | ||
|
|
adcc966c8e | ||
|
|
e04d8da0f1 | ||
|
|
f26806e451 | ||
|
|
77746767aa | ||
|
|
31ab99e062 | ||
|
|
f9cff64855 | ||
|
|
b15488d9d6 | ||
|
|
0e1bcca9c1 | ||
|
|
210be517aa | ||
|
|
e51b429f5f | ||
|
|
7be1cbdc36 | ||
|
|
a3f5b08e46 | ||
|
|
c8bc318dcc | ||
|
|
fc725ebf87 | ||
|
|
92cbf7b941 | ||
|
|
f9ca22de32 | ||
|
|
03700daa71 | ||
|
|
f71860dcab | ||
|
|
91a9db350a | ||
|
|
942ff6aaf9 | ||
|
|
bf37d83a93 | ||
|
|
dac6ee1871 | ||
|
|
c631110b64 | ||
|
|
3b7b1058c8 | ||
|
|
69b5edf663 | ||
|
|
9059a99861 | ||
|
|
3a138db137 | ||
|
|
e95ea41e7d | ||
|
|
74557f964a | ||
|
|
7afb96a48e | ||
|
|
6b9ca24cd5 | ||
|
|
839dee36f3 | ||
|
|
348d2d6e3d | ||
|
|
6668850fbc | ||
|
|
462d453327 | ||
|
|
7a63c47c4b | ||
|
|
87fe4bce9c | ||
|
|
cde8b02a67 | ||
|
|
6eb74b5f02 | ||
|
|
bcb85d070e | ||
|
|
c8b0f14326 | ||
|
|
aaaeb7abf7 | ||
|
|
12497af6f0 | ||
|
|
f24bf4108a | ||
|
|
c1ad95de52 | ||
|
|
31a3ae6e14 | ||
|
|
d5a3220fc9 | ||
|
|
65e8f02b83 | ||
|
|
dcb26b85a9 | ||
|
|
1a20ef2682 | ||
|
|
d0555127d5 | ||
|
|
472d273533 | ||
|
|
34cc60ec78 | ||
|
|
c2912c27bc | ||
|
|
f16a5ca1ec | ||
|
|
9ce6596741 |
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
dist/
|
||||
lib/
|
||||
example/
|
||||
index.d.ts
|
||||
6
.eslintrc.js
Normal file
6
.eslintrc.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: ['@react-native-community'],
|
||||
rules: {
|
||||
'no-bitwise': 0,
|
||||
},
|
||||
};
|
||||
20
.github/workflows/stale.yml
vendored
Normal file
20
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
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
3
.gitignore
vendored
@ -38,3 +38,6 @@ yarn-error.log
|
||||
# Vim
|
||||
.ctags
|
||||
tags
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
6
.prettierrc.js
Normal file
6
.prettierrc.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
bracketSpacing: false,
|
||||
jsxBracketSameLine: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
};
|
||||
17
.release-it.json
Normal file
17
.release-it.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"git": {
|
||||
"commitMessage": "chore: release ${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": true
|
||||
},
|
||||
"github": {
|
||||
"release": true
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/conventional-changelog": {
|
||||
"preset": "angular"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
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,
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
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("");
|
||||
});
|
||||
98
CHANGELOG.md
Normal file
98
CHANGELOG.md
Normal file
@ -0,0 +1,98 @@
|
||||
## [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
Normal file
28
FAQ.md
Normal file
@ -0,0 +1,28 @@
|
||||
# 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
215
LICENSE
@ -1,202 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
MIT License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) Richie Hsieh and Revteltech.
|
||||
|
||||
1. Definitions.
|
||||
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:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of 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.
|
||||
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.
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
import {
|
||||
NativeModules,
|
||||
NativeEventEmitter,
|
||||
} from 'react-native'
|
||||
|
||||
const NativeNfcManager = NativeModules.NfcManager;
|
||||
const NfcManagerEmitter = new NativeEventEmitter(NativeNfcManager);
|
||||
|
||||
export {
|
||||
NativeNfcManager,
|
||||
NfcManagerEmitter,
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
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,
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
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);
|
||||
});
|
||||
744
NfcManager.js
744
NfcManager.js
@ -1,744 +0,0 @@
|
||||
'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 = {
|
||||
invalidateAfterFirstRead: false,
|
||||
isReaderModeEnabled: false,
|
||||
readerModeFlags: 0,
|
||||
};
|
||||
|
||||
const Events = {
|
||||
DiscoverTag: 'NfcManagerDiscoverTag',
|
||||
SessionClosed: 'NfcManagerSessionClosed',
|
||||
StateChanged: 'NfcManagerStateChanged',
|
||||
}
|
||||
|
||||
const NfcTech = {
|
||||
Ndef: 'Ndef',
|
||||
NfcA: 'NfcA',
|
||||
NfcB: 'NfcB',
|
||||
NfcF: 'NfcF',
|
||||
NfcV: 'NfcV',
|
||||
IsoDep: 'IsoDep',
|
||||
MifareClassic: 'MifareClassic',
|
||||
MifareUltralight: 'MifareUltralight',
|
||||
}
|
||||
|
||||
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 LOG = 'NfcManagerJs';
|
||||
|
||||
class NfcManager {
|
||||
constructor() {
|
||||
this._clientTagDiscoveryListener = null;
|
||||
this._clientSessionClosedListener = null;
|
||||
this._session = null;
|
||||
this._subscription = null;
|
||||
}
|
||||
|
||||
// Constants, by the lack of ES7 we do it with getters
|
||||
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({ onSessionClosedIOS } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.start((err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
if (Platform.OS === 'ios') {
|
||||
this._clientSessionClosedListener = onSessionClosedIOS;
|
||||
this._session = NfcManagerEmitter.addListener(Events.SessionClosed, this._handleSessionClosed);
|
||||
} else {
|
||||
this._session = {
|
||||
remove: () => { },
|
||||
};
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this._session) {
|
||||
this._session.remove();
|
||||
this._session = null;
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
isSupported(tech = ''){
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.isSupported(tech, (err,result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
registerTagEvent(listener, alertMessage = '', options = {}) {
|
||||
// Support legacy `invalidateAfterFirstRead` boolean
|
||||
if (options === true || options === false) {
|
||||
options = {
|
||||
invalidateAfterFirstRead: options,
|
||||
};
|
||||
}
|
||||
|
||||
options = {
|
||||
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (!this._subscription) {
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.registerTagEvent(
|
||||
alertMessage,
|
||||
options,
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
this._clientTagDiscoveryListener = listener;
|
||||
this._subscription = NfcManagerEmitter.addListener(
|
||||
Events.DiscoverTag,
|
||||
this._handleDiscoverTag,
|
||||
);
|
||||
resolve(result);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
unregisterTagEvent() {
|
||||
if (this._subscription) {
|
||||
this._clientTagDiscoveryListener = null;
|
||||
this._subscription.remove();
|
||||
this._subscription = null;
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.unregisterTagEvent((err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
registerTagEventEx(listener, alertMessage = '', options = {}) {
|
||||
if (Platform.OS === 'android') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
// Support legacy `invalidateAfterFirstRead` boolean
|
||||
if (options === true || options === false) {
|
||||
options = {
|
||||
invalidateAfterFirstRead: options,
|
||||
};
|
||||
}
|
||||
|
||||
options = {
|
||||
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (!this._subscription) {
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.registerTagEventEx(
|
||||
alertMessage,
|
||||
options,
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
this._clientTagDiscoveryListener = listener;
|
||||
this._subscription = NfcManagerEmitter.addListener(
|
||||
Events.DiscoverTag,
|
||||
this._handleDiscoverTag,
|
||||
);
|
||||
resolve(result);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
unregisterTagEventEx() {
|
||||
if (Platform.OS === 'android') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
if (this._subscription) {
|
||||
this._clientTagDiscoveryListener = null;
|
||||
this._subscription.remove();
|
||||
this._subscription = null;
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.unregisterTagEventEx((err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
_handleDiscoverTag = tag => {
|
||||
if (this._clientTagDiscoveryListener) {
|
||||
this._clientTagDiscoveryListener(tag);
|
||||
}
|
||||
}
|
||||
|
||||
_handleSessionClosed = () => {
|
||||
if (this._subscription) {
|
||||
this._subscription.remove();
|
||||
this._subscription = null;
|
||||
}
|
||||
this._clientTagDiscoveryListener = null;
|
||||
this._clientSessionClosedListener && this._clientSessionClosedListener();
|
||||
}
|
||||
|
||||
onStateChanged(listener) {
|
||||
if (Platform.OS === 'ios') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
return Promise.resolve(NfcManagerEmitter.addListener(Events.StateChanged, listener));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
// Ndef Writing request API
|
||||
// -------------------------------------
|
||||
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(tech) {
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.requestTechnology(tech, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
requestTechnologies(techs) {
|
||||
if (Platform.OS === 'ios') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.requestTechnologies(techs, (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);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
closeTechnology() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.closeTechnology((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);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
// NfcTech.Ndef API
|
||||
// -------------------------------------
|
||||
writeNdefMessage(bytes) {
|
||||
if (Platform.OS === 'ios') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.writeNdefMessage(bytes, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getNdefMessage() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return Promise.reject('not implemented');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
NativeNfcManager.getNdefMessage((err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getCachedNdefMessage() {
|
||||
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);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
makeReadOnly() {
|
||||
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);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new NfcManager();
|
||||
|
||||
export {
|
||||
ByteParser,
|
||||
NdefParser,
|
||||
NfcTech,
|
||||
NfcAdapter,
|
||||
Ndef,
|
||||
}
|
||||
861
README.md
861
README.md
@ -8,701 +8,316 @@ Bring NFC feature to React Native. Inspired by [phonegap-nfc](https://github.com
|
||||
|
||||
Contributions are welcome!
|
||||
|
||||
## iOS 13 development is ongoing!
|
||||
|
||||
Ndef writing, get UID, send mifare command, and APDU exchange... Lots features come into iOS13!
|
||||
|
||||
If you're interested in helping this, please check the `v2` branch.
|
||||
|
||||
Currently this work will be published in npm beta channel.
|
||||
|
||||
## [IMPORTANT!!] React Native Version Compatibility
|
||||
|
||||
* `XCode 11 beta + RN60`: all `2.0.0-beta.X` version should be fine
|
||||
* `XCode 10 + RN60`: please use `2.0.0-beta.1`
|
||||
* `XCode 10 + RN59 and below`: please use `1.2.2`
|
||||
|
||||
**IMPORTANT** For RN 0.60 users, this module will leverage [autolink](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#autolinking), which means:
|
||||
1. for both iOS/android, you don't need to perform `react-native link` like before.
|
||||
2. for iOS, you should do `cd ios && pod install && cd ..`
|
||||
|
||||
## Supported Platforms
|
||||
- Android (API 10+)
|
||||
- iOS (iOS11 with iPhone 7/7+, 8/8+, 10)
|
||||
|
||||
## iOS Setup
|
||||
|
||||
You will need to setup some capabilities / entitlement / plist stuff to enable NFC development on your device, this repo explains these requirements very well:
|
||||
|
||||
* https://github.com/hansemannn/iOS11-NFC-Example
|
||||
Made with ❤️ by [whitedogg13](https://github.com/whitedogg13) and [revteltech](https://github.com/revtel)
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
# XCode 11 beta + RN >= 0.60
|
||||
npm i --save react-native-nfc-manager@beta
|
||||
```
|
||||
### javascript part
|
||||
|
||||
```shell
|
||||
# XCode 10 + RN >= 0.60
|
||||
npm i --save react-native-nfc-manager@2.0.0-beta.1
|
||||
npm i --save react-native-nfc-manager
|
||||
```
|
||||
|
||||
### native part
|
||||
|
||||
This library use native-modules, so you will need to do `pod install` for iOS:
|
||||
|
||||
```shell
|
||||
# XCode 10 + RN < 0.60
|
||||
npm i --save react-native-nfc-manager@1.2.2
|
||||
cd ios && pod install && cd ..
|
||||
```
|
||||
|
||||
### Link Native Library with `react-native link` (RN version < 0.60)
|
||||
For Android, it should be properly auto-linked, so you don't need to do anything.
|
||||
|
||||
## 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:
|
||||
|
||||
```shell
|
||||
react-native link react-native-nfc-manager
|
||||
```
|
||||
|
||||
### Install with cocopods
|
||||
Include this line inside of your Podfile
|
||||
```shell
|
||||
pod 'react-native-nfc-manager', :path => '../node_modules/react-native-nfc-manager/'
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Look into `example/App.js` as a starting point.
|
||||
|
||||
The easiest way to test is simple make your `AppRegistry` point to our example component, like this:
|
||||
```javascript
|
||||
// in your index.ios.js or index.android.js
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
AppRegistry,
|
||||
} from 'react-native';
|
||||
import App from 'react-native-nfc-manager/example/App'
|
||||
|
||||
AppRegistry.registerComponent('NfcManagerDev', () => App);
|
||||
```
|
||||
|
||||
## 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');
|
||||
buildscript {
|
||||
ext {
|
||||
...
|
||||
compileSdkVersion = 31
|
||||
...
|
||||
}
|
||||
})
|
||||
.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:
|
||||
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`
|
||||
|
||||
### writeNdefMessage(bytes) [Android only]
|
||||
Request writing **NdefMessage** (constructed by `bytes` array you passed) into the tag.
|
||||
> The original issue is [here](https://github.com/revtel/react-native-nfc-manager/issues/469)
|
||||
|
||||
> 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.
|
||||
BTW, if you don't care about **Android 12** for now, you can use **`v3.11.0`** as a short term solution.
|
||||
|
||||
__Arguments__
|
||||
- `bytes` - `array` - the full NdefMessage, which is an array of bytes
|
||||
### **[Demo App] NfcOpenReWriter**
|
||||
|
||||
### getNdefMessage() [Android only]
|
||||
Read current NdefMessage inside the tag.
|
||||
We have a full featured NFC utility app using this library available for download.
|
||||
|
||||
> 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.
|
||||
<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>
|
||||
|
||||
### getCachedNdefMessage() [Android only]
|
||||
Read cached NdefMessage inside the tag, no further IO operation occurs.
|
||||
</br>
|
||||
|
||||
> 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.
|
||||
<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>
|
||||
|
||||
### makeReadOnly() [Android only]
|
||||
Make the tag become read-only.
|
||||
It also open sourced in this repo: [React Native NFC ReWriter App](https://github.com/revtel/react-native-nfc-rewriter)
|
||||
|
||||
> 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.
|
||||
## Learn
|
||||
|
||||
## Generic NfcTech API [Android only]
|
||||
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)
|
||||
|
||||
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:
|
||||
## Usage
|
||||
|
||||
### 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.
|
||||
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:
|
||||
|
||||
> 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.
|
||||
```javascript
|
||||
import React from 'react';
|
||||
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
|
||||
import NfcManager, {NfcTech} from 'react-native-nfc-manager';
|
||||
|
||||
__Arguments__
|
||||
- `bytes` - `array` - the raw data you want to send, which is an array of bytes
|
||||
// Pre-step, call this before any NFC operations
|
||||
NfcManager.start();
|
||||
|
||||
### 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.
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
> 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.
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<TouchableOpacity onPress={readNdef}>
|
||||
<Text>Scan a Tag</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
### 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... */
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
### mifareClassicGetBlockCountInSector(sector) [Android only]
|
||||
Returns a promise with the number of blocks in a given sector.
|
||||
Pleaes notice when running above codes, iOS and Android has different behaviors:
|
||||
|
||||
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.
|
||||
- iOS will pop up a system scanning UI
|
||||
- Android provides **NO** system scanning UI
|
||||
|
||||
__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)
|
||||
Regarding the system scannning UI, both platforms should be able to scan your NFC tags succesfully and print out its content.
|
||||
|
||||
__Return value__
|
||||
- `blocks` - `number` - the number of blocks
|
||||
### Old Style (registerTagEvent) To Scan NFC Tags
|
||||
|
||||
### mifareClassicGetSectorCount() [Android only]
|
||||
Returns a promise with the number of sectors on the card.
|
||||
There's an alterntaive style to scan NFC tags through `NfcManager.registerTagEvent`, like this:
|
||||
|
||||
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.
|
||||
```javascript
|
||||
import NfcManager, {NfcTech} from 'react-native-nfc-manager';
|
||||
|
||||
__Return value__
|
||||
- `sectors` - `number` - the number of sectors
|
||||
// The following function resolves to a NFC Tag object using old event listener approach.
|
||||
// You can call it like this:
|
||||
// `const nfcTag = await listenToNfcEventOnce()`
|
||||
|
||||
### mifareClassicSectorToBlock(sector) [Android only]
|
||||
Returns a promise with the blockIndex for a given sector.
|
||||
function listenToNfcEventOnce() {
|
||||
const cleanUp = () => {
|
||||
NfcManager.setEventListener(NfcEvents.DiscoverTag, null);
|
||||
NfcManager.setEventListener(NfcEvents.SessionClosed, null);
|
||||
};
|
||||
|
||||
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.
|
||||
return new Promise((resolve) => {
|
||||
let tagFound = null;
|
||||
|
||||
__Arguments__
|
||||
- `sector` - `number` - the Mifare Classic sector to get the blockIndex from (the number of blocks might depend on the detected card type)
|
||||
NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag) => {
|
||||
tagFound = tag;
|
||||
resolve(tagFound);
|
||||
NfcManager.unregisterTagEvent();
|
||||
});
|
||||
|
||||
__Return value__
|
||||
- `blockIndex` - `number` - the block index of the sector
|
||||
NfcManager.setEventListener(NfcEvents.SessionClosed, () => {
|
||||
cleanUp();
|
||||
if (!tagFound) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
### 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);
|
||||
});
|
||||
NfcManager.registerTagEvent();
|
||||
});
|
||||
};
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
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).
|
||||
## Advanced Usage Concept
|
||||
|
||||
> 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.
|
||||
In higher level, there're 4 steps to use this library:
|
||||
|
||||
Notice you must be successfully authenticated with the `mifareClassicAuthenticateA` or `mifareClassicAuthenticateB` call before using this method.
|
||||
1. request your particular NFC technologies through `NfcManager.requestTechnology`, for example:
|
||||
|
||||
__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`
|
||||
- `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)
|
||||
|
||||
__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');
|
||||
});
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
## NfcTech.MifareUltralight API [Android only]
|
||||
## Advanced Usage Example: Mifare Ultralight
|
||||
|
||||
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:
|
||||
Here's another example to read a Mifare Ultralight tag:
|
||||
|
||||
### mifareUltralightReadPages(pageOffset) [Android only]
|
||||
Read 4 pages (16 bytes).
|
||||
```javascript
|
||||
async function readMifare() {
|
||||
let mifarePages = [];
|
||||
|
||||
> 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.
|
||||
try {
|
||||
// STEP 1
|
||||
let reqMifare = await NfcManager.requestTechnology(
|
||||
NfcTech.MifareUltralight,
|
||||
);
|
||||
|
||||
__Arguments__
|
||||
- `pageOffset` - `number` - index of first page to read, starting from 0
|
||||
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();
|
||||
}
|
||||
|
||||
### 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);
|
||||
return mifarePages;
|
||||
}
|
||||
```
|
||||
|
||||
### byteToString(bytes)
|
||||
Converts a byte array `byte[]` to a string (if the data represents an ASCII string).
|
||||
To see more examples, please see [React Native NFC ReWriter App](https://github.com/revtel/react-native-nfc-rewriter)
|
||||
|
||||
__Arguments__
|
||||
- `bytes` - `byte[]` - the result of a mifareClassicReadBlock call.
|
||||
## API
|
||||
|
||||
__Examples__
|
||||
```js
|
||||
let str = ByteParser.byteToString(result);
|
||||
console.log('string: ' + str);
|
||||
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"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## NFC Hardware requirement on Android
|
||||
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
|
||||
|
||||
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.
|
||||
> Notice: This Config Plugin will ensure the minimum Android SDK version is 31.
|
||||
|
||||
If you want to change this behavior to only have your app support NFC devices you have to override you app manifest manually.
|
||||
#### Props
|
||||
|
||||
Current setting is:
|
||||
```<uses-feature android:name="android.hardware.nfc" android:required="false" />```
|
||||
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.
|
||||
|
||||
If you want to only have your app support NFC devices then you have to change required to true.
|
||||
- `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
|
||||
|
||||
## 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);
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"plugins": [
|
||||
[
|
||||
"react-native-nfc-manager",
|
||||
{
|
||||
"nfcPermission": "Custom permission message",
|
||||
"selectIdentifiers": ["A0000002471001"],
|
||||
"systemCodes": ["8008"]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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>
|
||||
|
||||
|
||||
17
__mocks__/react-native.js
vendored
Normal file
17
__mocks__/react-native.js
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
const ReactNative = {};
|
||||
|
||||
let _os = 'ios';
|
||||
|
||||
const Platform = {
|
||||
get OS() {
|
||||
return _os;
|
||||
},
|
||||
|
||||
setOS: (os) => {
|
||||
_os = os;
|
||||
},
|
||||
};
|
||||
|
||||
ReactNative.Platform = Platform;
|
||||
|
||||
module.exports = ReactNative;
|
||||
151
__tests__/NfcManager.test.js
Normal file
151
__tests__/NfcManager.test.js
Normal file
@ -0,0 +1,151 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
73
__tests__/NfcManagerAndroid.test.js
Normal file
73
__tests__/NfcManagerAndroid.test.js
Normal file
@ -0,0 +1,73 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
169
__tests__/ndef.test.js
Normal file
169
__tests__/ndef.test.js
Normal file
@ -0,0 +1,169 @@
|
||||
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);
|
||||
});
|
||||
@ -4,7 +4,7 @@ def safeExtGet(prop, fallback) {
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -14,9 +14,7 @@ buildscript {
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet('compileSdkVersion', 27)
|
||||
buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3')
|
||||
|
||||
compileSdkVersion safeExtGet('compileSdkVersion', 31)
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet('minSdkVersion', 16)
|
||||
//noinspection OldTargetApi
|
||||
@ -38,5 +36,4 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation 'com.facebook.react:react-native:+'
|
||||
|
||||
}
|
||||
|
||||
1
android/gradle.properties
Normal file
1
android/gradle.properties
Normal file
@ -0,0 +1 @@
|
||||
android.useAndroidX=true
|
||||
@ -106,6 +106,8 @@ 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);
|
||||
}
|
||||
@ -128,6 +130,8 @@ 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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -7,7 +7,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.provider.Settings;
|
||||
import com.facebook.react.bridge.*;
|
||||
@ -49,9 +49,20 @@ 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;
|
||||
@ -104,7 +115,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
if (techRequest != null) {
|
||||
techRequest.close();
|
||||
try {
|
||||
techRequest.getPendingCallback().invoke("cancelled");
|
||||
techRequest.getPendingCallback().invoke(ERR_CANCEL);
|
||||
} catch (RuntimeException ex) {
|
||||
// the pending callback might already been invoked when there is an ongoing
|
||||
// connected tag, bypass this case explicitly
|
||||
@ -119,38 +130,21 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void requestTechnology(String tech, Callback callback) {
|
||||
public void requestTechnology(ReadableArray techs, Callback callback) {
|
||||
synchronized(this) {
|
||||
if (!isForegroundEnabled) {
|
||||
callback.invoke("you should requestTagEvent first");
|
||||
callback.invoke(ERR_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPendingRequest()) {
|
||||
callback.invoke("You can only issue one request at a time");
|
||||
} else {
|
||||
techRequest = new TagTechnologyRequest(tech, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void requestTechnologies(ReadableArray techs, Callback callback) {
|
||||
synchronized(this) {
|
||||
if (!isForegroundEnabled) {
|
||||
callback.invoke("you should requestTagEvent first");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPendingRequest()) {
|
||||
callback.invoke("You can only issue one request at a time");
|
||||
callback.invoke(ERR_MULTI_REQ);
|
||||
} else {
|
||||
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ReactMethod
|
||||
public void closeTechnology(Callback callback) {
|
||||
synchronized(this) {
|
||||
@ -167,19 +161,24 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
|
||||
@ReactMethod
|
||||
public void getTag(Callback callback) {
|
||||
synchronized(this) {
|
||||
synchronized (this) {
|
||||
if (techRequest != null) {
|
||||
try {
|
||||
TagTechnology tagTech = techRequest.getTechHandle();
|
||||
Tag tag = tagTech.getTag();
|
||||
Tag tag = techRequest.getTagHandle();
|
||||
if (tag != null) {
|
||||
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);
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "getTag fail");
|
||||
callback.invoke("getTag fail");
|
||||
} else {
|
||||
callback.invoke(ERR_NO_REFERENCE);
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,15 +188,15 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
synchronized(this) {
|
||||
if (techRequest != null) {
|
||||
try {
|
||||
Ndef ndef = Ndef.get(techRequest.getTechHandle().getTag());
|
||||
Ndef ndef = Ndef.get(techRequest.getTagHandle());
|
||||
WritableMap parsed = ndef2React(ndef, new NdefMessage[] { ndef.getCachedNdefMessage() });
|
||||
callback.invoke(null, parsed);
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "getCachedNdefMessage fail");
|
||||
callback.invoke("getCachedNdefMessage fail");
|
||||
Log.d(LOG_TAG, ex.toString());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,15 +206,39 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
synchronized(this) {
|
||||
if (techRequest != null) {
|
||||
try {
|
||||
Ndef ndef = Ndef.get(techRequest.getTechHandle().getTag());
|
||||
Ndef ndef = Ndef.get(techRequest.getTagHandle());
|
||||
WritableMap parsed = ndef2React(null, new NdefMessage[] { ndef.getNdefMessage() });
|
||||
callback.invoke(null, parsed);
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "getNdefMessage fail");
|
||||
callback.invoke("getNdefMessage fail");
|
||||
Log.d(LOG_TAG, ex.toString());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,15 +249,49 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
if (techRequest != null) {
|
||||
try {
|
||||
Ndef ndef = (Ndef)techRequest.getTechHandle();
|
||||
byte[] bytes = rnArrayToBytes(rnArray);
|
||||
ndef.writeNdefMessage(new NdefMessage(bytes));
|
||||
callback.invoke();
|
||||
if (ndef == null) {
|
||||
callback.invoke(ERR_API_NOT_SUPPORT);
|
||||
} else {
|
||||
byte[] bytes = rnArrayToBytes(rnArray);
|
||||
ndef.writeNdefMessage(new NdefMessage(bytes));
|
||||
callback.invoke();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "writeNdefMessage fail");
|
||||
callback.invoke("writeNdefMessage fail");
|
||||
Log.d(LOG_TAG, ex.toString());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -278,7 +335,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicAuthenticate fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,7 +375,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicGetBlockCountInSector fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -340,7 +397,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicGetSectorCount fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -367,7 +424,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicSectorToBlock fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,7 +457,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicReadBlock fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,7 +494,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicReadSector fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -474,7 +531,100 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareClassicWriteBlock fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -495,7 +645,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareUltralight fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -516,7 +666,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke("mifareUltralight fail: " + ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -530,11 +680,11 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
boolean result = ndef.makeReadOnly();
|
||||
callback.invoke(null, result);
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "makeReadOnly fail");
|
||||
callback.invoke("makeReadOnly fail");
|
||||
Log.d(LOG_TAG, ex.toString());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -575,41 +725,41 @@ 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, "setTimeout fail");
|
||||
Log.d(LOG_TAG, ex.toString());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
callback.invoke("setTimeout fail");
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,13 +818,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");
|
||||
Log.d(LOG_TAG, "transceive fail: " + ex.toString());
|
||||
callback.invoke(ERR_TRANSCEIVE_FAIL);
|
||||
}
|
||||
|
||||
callback.invoke("transceive fail");
|
||||
} else {
|
||||
callback.invoke("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -721,12 +871,13 @@ 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("no tech request available");
|
||||
callback.invoke(ERR_NO_TECH_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -735,11 +886,11 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
public void cancelNdefWrite(Callback callback) {
|
||||
synchronized(this) {
|
||||
if (writeNdefRequest != null) {
|
||||
writeNdefRequest.callback.invoke("cancelled");
|
||||
writeNdefRequest.callback.invoke(ERR_CANCEL);
|
||||
writeNdefRequest = null;
|
||||
callback.invoke();
|
||||
} else {
|
||||
callback.invoke("no writing request available");
|
||||
callback.invoke(ERR_NOT_REGISTERED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -748,12 +899,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
public void requestNdefWrite(ReadableArray rnArray, ReadableMap options, Callback callback) {
|
||||
synchronized(this) {
|
||||
if (!isForegroundEnabled) {
|
||||
callback.invoke("you should requestTagEvent first");
|
||||
callback.invoke(ERR_NOT_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPendingRequest()) {
|
||||
callback.invoke("You can only issue one request at a time");
|
||||
callback.invoke(ERR_MULTI_REQ);
|
||||
} else {
|
||||
boolean format = options.getBoolean("format");
|
||||
boolean formatReadOnly = options.getBoolean("formatReadOnly");
|
||||
@ -778,7 +929,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
formatReadOnly
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
callback.invoke("Incorrect ndef format");
|
||||
callback.invoke(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -803,11 +954,11 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
nfcAdapter.setNdefPushMessage(msgToPush, currentActivity);
|
||||
callback.invoke();
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "sendNdefPushMessage fail, " + ex.getMessage());
|
||||
callback.invoke("sendNdefPushMessage fail");
|
||||
Log.d(LOG_TAG, ex.toString());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
} else {
|
||||
callback.invoke("please first cancel existing tech or write request");
|
||||
callback.invoke(ERR_MULTI_REQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -821,15 +972,18 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity == null) {
|
||||
callback.invoke("fail to get current activity");
|
||||
callback.invoke(ERR_GET_ACTIVITY_FAIL);
|
||||
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("no nfc support");
|
||||
callback.invoke(ERR_NO_NFC_SUPPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -838,7 +992,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
Log.d(LOG_TAG, "isSupported");
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity == null) {
|
||||
callback.invoke("fail to get current activity");
|
||||
callback.invoke(ERR_GET_ACTIVITY_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -876,19 +1030,23 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
Log.d(LOG_TAG, "goToNfcSetting");
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity == null) {
|
||||
callback.invoke("fail to get current activity");
|
||||
callback.invoke(ERR_GET_ACTIVITY_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
|
||||
callback.invoke();
|
||||
try {
|
||||
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
|
||||
callback.invoke(null, true);
|
||||
} catch (Exception ex) {
|
||||
callback.invoke(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getLaunchTagEvent(Callback callback) {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity == null) {
|
||||
callback.invoke("fail to get current activity");
|
||||
callback.invoke(ERR_GET_ACTIVITY_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -898,11 +1056,23 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
private void registerTagEvent(String alertMessage, ReadableMap options, Callback callback) {
|
||||
this.isReaderModeEnabled = options.getBoolean("isReaderModeEnabled");
|
||||
this.readerModeFlags = options.getInt("readerModeFlags");
|
||||
public void getBackgroundTag(Callback callback) {
|
||||
callback.invoke(null, bgTag);
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "registerTag");
|
||||
@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");
|
||||
|
||||
Log.d(LOG_TAG, "registerTagEvent");
|
||||
isForegroundEnabled = true;
|
||||
|
||||
// capture all mime-based dispatch NDEF
|
||||
@ -929,15 +1099,36 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
|
||||
@ReactMethod
|
||||
private void unregisterTagEvent(Callback callback) {
|
||||
Log.d(LOG_TAG, "registerTag");
|
||||
isForegroundEnabled = false;
|
||||
intentFilters.clear();
|
||||
Log.d(LOG_TAG, "unregisterTagEvent");
|
||||
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);
|
||||
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");
|
||||
@ -972,25 +1163,36 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
Log.i(LOG_TAG, "enableReaderMode");
|
||||
Log.i(LOG_TAG, "enableReaderMode: " + readerModeFlags);
|
||||
Bundle readerModeExtras = new Bundle();
|
||||
readerModeExtras.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 10000);
|
||||
readerModeExtras.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, readerModeDelay * 1000);
|
||||
nfcAdapter.enableReaderMode(currentActivity, new NfcAdapter.ReaderCallback() {
|
||||
@Override
|
||||
public void onTagDiscovered(Tag 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (nfcTag != null) {
|
||||
sendEvent("NfcManagerDiscoverTag", nfcTag);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, readerModeFlags, readerModeExtras);
|
||||
@ -1015,7 +1217,12 @@ 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);
|
||||
return PendingIntent.getActivity(activity, 0, intent, 0);
|
||||
|
||||
int flag = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
flag = PendingIntent.FLAG_MUTABLE;
|
||||
}
|
||||
return PendingIntent.getActivity(activity, 0, intent, flag);
|
||||
}
|
||||
|
||||
private IntentFilter[] getIntentFilters() {
|
||||
@ -1089,7 +1296,16 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
Log.d(LOG_TAG, "onNewIntent " + intent);
|
||||
WritableMap nfcTag = parseNfcIntent(intent);
|
||||
if (nfcTag != null) {
|
||||
sendEvent("NfcManagerDiscoverTag", nfcTag);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1119,10 +1335,10 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
if (!techRequest.isConnected()) {
|
||||
boolean result = techRequest.connect(tag);
|
||||
if (result) {
|
||||
techRequest.getPendingCallback().invoke();
|
||||
techRequest.getPendingCallback().invoke(null, techRequest.getTechType());
|
||||
} else {
|
||||
techRequest.getPendingCallback().invoke("fail to connect tag");
|
||||
techRequest = null;
|
||||
// this indicates that we get a NFC tag, but none of the user required tech is matched
|
||||
techRequest.getPendingCallback().invoke(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1206,7 +1422,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
Log.d(LOG_TAG, "ready to writeNdef");
|
||||
NdefFormatable formatable = NdefFormatable.get(tag);
|
||||
if (formatable == null) {
|
||||
callback.invoke("fail to apply ndef formatable tech");
|
||||
callback.invoke(ERR_API_NOT_SUPPORT);
|
||||
} else {
|
||||
Log.d(LOG_TAG, "ready to format ndef, seriously");
|
||||
formatable.connect();
|
||||
@ -1218,14 +1434,14 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
callback.invoke("writeNdef fail: " + ex.getMessage());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Log.d(LOG_TAG, "ready to writeNdef");
|
||||
Ndef ndef = Ndef.get(tag);
|
||||
if (ndef == null) {
|
||||
callback.invoke("fail to apply ndef tech");
|
||||
callback.invoke(ERR_API_NOT_SUPPORT);
|
||||
} else if (!ndef.isWritable()) {
|
||||
callback.invoke("tag is not writeable");
|
||||
} else if (ndef.getMaxSize() < message.toByteArray().length) {
|
||||
@ -1237,7 +1453,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
|
||||
callback.invoke();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
callback.invoke("writeNdef fail: " + ex.getMessage());
|
||||
callback.invoke(ex.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ 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.*;
|
||||
@ -20,21 +21,15 @@ class TagTechnologyRequest {
|
||||
static String LOG_TAG = "NfcManager-tech";
|
||||
Tag mTag;
|
||||
TagTechnology mTech;
|
||||
String mTechType;
|
||||
ArrayList<Object> mTechTypes;
|
||||
|
||||
String mTechType; // the actual connected type
|
||||
ArrayList<Object> mTechTypes; // the desired types
|
||||
Callback mJsCallback;
|
||||
|
||||
TagTechnologyRequest(String techType, Callback cb) {
|
||||
mTechTypes = new ArrayList<Object>();
|
||||
mTechTypes.add(techType);
|
||||
mJsCallback = cb;
|
||||
}
|
||||
|
||||
TagTechnologyRequest(ArrayList<Object> techTypes, Callback cb) {
|
||||
mTechTypes = techTypes;
|
||||
mJsCallback = cb;
|
||||
}
|
||||
|
||||
String getTechType() {
|
||||
return mTechType;
|
||||
}
|
||||
@ -47,8 +42,12 @@ class TagTechnologyRequest {
|
||||
return mTech;
|
||||
}
|
||||
|
||||
Tag getTagHandle() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return mTag != null;
|
||||
return mTech != null;
|
||||
}
|
||||
|
||||
boolean connect(Tag tag) {
|
||||
@ -57,47 +56,51 @@ class TagTechnologyRequest {
|
||||
return false;
|
||||
}
|
||||
|
||||
mTech = null;
|
||||
mTag = tag;
|
||||
int i = 0;
|
||||
boolean connection = false;
|
||||
while(i < mTechTypes.size() && connection == false){
|
||||
mTechType = (String)mTechTypes.get(i);
|
||||
i++;
|
||||
if (mTechType.equals("Ndef")) {
|
||||
mTech = Ndef.get(tag);
|
||||
} else if (mTechType.equals("NfcA")) {
|
||||
mTech = NfcA.get(tag);
|
||||
} else if (mTechType.equals("NfcB")) {
|
||||
mTech = NfcB.get(tag);
|
||||
} else if (mTechType.equals("NfcF")) {
|
||||
mTech = NfcF.get(tag);
|
||||
} else if (mTechType.equals("NfcV")) {
|
||||
mTech = NfcV.get(tag);
|
||||
} else if (mTechType.equals("IsoDep")) {
|
||||
mTech = IsoDep.get(tag);
|
||||
} else if (mTechType.equals("MifareClassic")) {
|
||||
mTech = MifareClassic.get(tag);
|
||||
} else if (mTechType.equals("MifareUltralight")) {
|
||||
mTech = MifareUltralight.get(tag);
|
||||
}
|
||||
|
||||
if (mTech == null) {
|
||||
connection = false;
|
||||
}
|
||||
for (int i = 0; i < mTechTypes.size(); i++) {
|
||||
String techType = (String)mTechTypes.get(i);
|
||||
|
||||
try {
|
||||
Log.d(LOG_TAG, "connect to " + mTechType);
|
||||
mTech.connect();
|
||||
connection = true;
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "fail to connect tech");
|
||||
connection = false;
|
||||
}
|
||||
if (techType.equals("Ndef")) {
|
||||
mTech = Ndef.get(tag);
|
||||
} else if (techType.equals("NfcA")) {
|
||||
mTech = NfcA.get(tag);
|
||||
} else if (techType.equals("NfcB")) {
|
||||
mTech = NfcB.get(tag);
|
||||
} else if (techType.equals("NfcF")) {
|
||||
mTech = NfcF.get(tag);
|
||||
} else if (techType.equals("NfcV")) {
|
||||
mTech = NfcV.get(tag);
|
||||
} else if (techType.equals("IsoDep")) {
|
||||
mTech = IsoDep.get(tag);
|
||||
} else if (techType.equals("MifareClassic")) {
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Log.d(LOG_TAG, "connect to " + techType);
|
||||
mTech.connect();
|
||||
mTechType = techType;
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
Log.d(LOG_TAG, "fail to connect tech");
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
// not connected, restore to default
|
||||
mTech = null;
|
||||
mTechType = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void close() {
|
||||
try {
|
||||
|
||||
@ -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,22 +97,23 @@ public class Util {
|
||||
static JSONArray byteArrayToJSON(byte[] bytes) {
|
||||
JSONArray json = new JSONArray();
|
||||
for (byte aByte : bytes) {
|
||||
json.put(aByte);
|
||||
int v = aByte & 0xFF;
|
||||
json.put(v);
|
||||
}
|
||||
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 {
|
||||
|
||||
105
app.plugin.js
Normal file
105
app.plugin.js
Normal file
@ -0,0 +1,105 @@
|
||||
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;
|
||||
@ -1,322 +0,0 @@
|
||||
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;
|
||||
@ -1,160 +0,0 @@
|
||||
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
341
example/App.js
@ -1,341 +0,0 @@
|
||||
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;
|
||||
@ -1,64 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
Button,
|
||||
Platform,
|
||||
TouchableOpacity,
|
||||
Linking,
|
||||
TextInput,
|
||||
ScrollView,
|
||||
} from 'react-native';
|
||||
import NfcManager, {Ndef} from '../NfcManager';
|
||||
|
||||
class AppIOS13 extends React.Component {
|
||||
componentDidMount() {
|
||||
NfcManager.start();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._cleanUp();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{padding: 20}}>
|
||||
<Text>NFC Demo on iOS13</Text>
|
||||
<TouchableOpacity
|
||||
style={{padding: 10, width: 200, margin: 20, borderWidth: 1, borderColor: 'black'}}
|
||||
onPress={this._testMifare}
|
||||
>
|
||||
<Text>Test Mifare</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.unregisterTagEventEx().catch(() => 0);
|
||||
}
|
||||
|
||||
_testMifare = async () => {
|
||||
try {
|
||||
await NfcManager.registerTagEventEx()
|
||||
let resp = await NfcManager.requestTechnology('mifare');
|
||||
console.warn(resp);
|
||||
let tag = await NfcManager.getTag();
|
||||
console.warn(tag);
|
||||
this._cleanUp();
|
||||
} catch (ex) {
|
||||
this._cleanUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AppIOS13;
|
||||
@ -1,215 +0,0 @@
|
||||
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;
|
||||
BIN
images/Apple-App-Store-Icon.png
Normal file
BIN
images/Apple-App-Store-Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
BIN
images/edit-entitlement.png
Normal file
BIN
images/edit-entitlement.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
BIN
images/enable-capability.png
Normal file
BIN
images/enable-capability.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
images/google-play-icon.jpeg
Normal file
BIN
images/google-play-icon.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
BIN
images/xcode-capability.png
Normal file
BIN
images/xcode-capability.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
BIN
images/xcode-entitlement.png
Normal file
BIN
images/xcode-entitlement.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
536
index.d.ts
vendored
536
index.d.ts
vendored
@ -1,150 +1,444 @@
|
||||
import { EmitterSubscription } from "react-native";
|
||||
|
||||
// Type definitions for react-native-nfc-manager
|
||||
// Project: https://github.com/whitedogg13/react-native-nfc-manager
|
||||
// Definitions by: April Ayres <april.ayres@papercut.com> and Paul Huynh <paul.huynh@papercut.com>
|
||||
|
||||
declare module 'react-native-nfc-manager' {
|
||||
export interface NdefRecord {
|
||||
id?: number[];
|
||||
tnf: number;
|
||||
type: number[];
|
||||
payload: any[];
|
||||
}
|
||||
export enum NfcEvents {
|
||||
DiscoverTag = 'NfcManagerDiscoverTag',
|
||||
DiscoverBackgroundTag = 'NfcManagerDiscoverBackgroundTag',
|
||||
SessionClosed = 'NfcManagerSessionClosed',
|
||||
StateChanged = 'NfcManagerStateChanged',
|
||||
}
|
||||
|
||||
export interface ParseUriResult {
|
||||
uri: string;
|
||||
}
|
||||
export enum 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',
|
||||
}
|
||||
|
||||
export interface StartOptions {
|
||||
onSessionClosedIOS(): void;
|
||||
}
|
||||
export enum NdefStatus {
|
||||
NotSupported = 1,
|
||||
ReadWrite = 2,
|
||||
ReadOnly = 3,
|
||||
}
|
||||
|
||||
export interface TagEvent {
|
||||
ndefMessage: NdefRecord[];
|
||||
maxSize: number;
|
||||
type: string;
|
||||
techTypes: string[];
|
||||
id: number[];
|
||||
}
|
||||
export enum 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,
|
||||
}
|
||||
|
||||
interface RegisterTagEventOpts {
|
||||
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;
|
||||
payload: any[];
|
||||
}
|
||||
|
||||
export interface TagEvent {
|
||||
ndefMessage: NdefRecord[];
|
||||
maxSize?: number;
|
||||
type?: string;
|
||||
techTypes?: string[];
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface RegisterTagEventOpts {
|
||||
alertMessage?: string;
|
||||
invalidateAfterFirstRead?: boolean;
|
||||
isReaderModeEnabled?: boolean;
|
||||
readerModeFlags?: number;
|
||||
readerModeDelay?: number;
|
||||
}
|
||||
|
||||
interface NdefWriteOpts {
|
||||
format?: boolean
|
||||
formatReadOnly?: boolean
|
||||
}
|
||||
interface EventStateChange {
|
||||
state: string
|
||||
}
|
||||
interface NfcManager {
|
||||
export interface CancelTechReqOpts {
|
||||
throwOnError?: boolean = false;
|
||||
delayMsAndroid?: number = 1000;
|
||||
}
|
||||
|
||||
start(options?: StartOptions): Promise<any>;
|
||||
interface NdefHandler {
|
||||
writeNdefMessage: (bytes: number[]) => Promise<void>;
|
||||
getNdefMessage: () => Promise<TagEvent | null>;
|
||||
makeReadOnly: () => Promise<void>;
|
||||
getNdefStatus: () => Promise<{
|
||||
status: NdefStatus;
|
||||
capacity: number;
|
||||
}>;
|
||||
getCachedNdefMessageAndroid: () => Promise<TagEvent | null>;
|
||||
}
|
||||
|
||||
stop(): void;
|
||||
interface NfcAHandler {
|
||||
transceive: (bytes: number[]) => Promise<number[]>;
|
||||
}
|
||||
|
||||
isSupported(): Promise<boolean>;
|
||||
interface NfcVHandler {
|
||||
transceive: (bytes: number[]) => Promise<number[]>;
|
||||
}
|
||||
|
||||
/** [ANDROID ONLY] */
|
||||
isEnabled(): Promise<boolean>;
|
||||
interface IsoDepHandler {
|
||||
transceive: (bytes: number[]) => Promise<number[]>;
|
||||
}
|
||||
|
||||
/** [ANDROID ONLY] */
|
||||
goToNfcSetting(): Promise<any>;
|
||||
interface MifareClassicHandlerAndroid {
|
||||
mifareClassicSectorToBlock: (sector: number) => Promise<ArrayLike<number>>;
|
||||
mifareClassicReadBlock: (
|
||||
block: ArrayLike<number>,
|
||||
) => Promise<ArrayLike<number>>;
|
||||
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>;
|
||||
mifareClassicGetSectorCount: () => Promise<number>;
|
||||
mifareClassicAuthenticateA: (
|
||||
sector: number,
|
||||
keys: number[],
|
||||
) => Promise<void>;
|
||||
mifareClassicAuthenticateB: (
|
||||
sector: number,
|
||||
keys: number[],
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
/** [ANDROID ONLY] */
|
||||
getLaunchTagEvent(): Promise<TagEvent | null>;
|
||||
interface MifareUltralightHandlerAndroid {
|
||||
mifareUltralightReadPages: (offset: number) => Promise<ArrayLike<number>>;
|
||||
mifareUltralightWritePage: (
|
||||
offset: number,
|
||||
data: number[],
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to listen to ANY NFC Tags
|
||||
* @param listener The callback function when a tag is found.
|
||||
* @param alertMessage [iOS ONLY] Message displayed when NFC Scanning popup appears.
|
||||
* @param invalidateAfterFirstRead [iOS ONLY] When set to true, will auto-dismiss the NFC Scanning popup after scanning.
|
||||
*/
|
||||
registerTagEvent(
|
||||
listener: (tag: TagEvent) => void,
|
||||
alertMessage?: string,
|
||||
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<any>;
|
||||
): Promise<NfcTech | null>;
|
||||
cancelTechnologyRequest: (options?: CancelTechReqOpts) => Promise<void>;
|
||||
getTag: () => Promise<TagEvent | null>;
|
||||
getBackgroundTag: () => Promise<TagEvent | null>;
|
||||
clearBackgroundTag: () => Promise<void>;
|
||||
setAlertMessage: (alertMessage: string) => Promise<void>;
|
||||
|
||||
unregisterTagEvent(): Promise<any>;
|
||||
/* android only */
|
||||
cancelNdefWrite(): Promise<any>;
|
||||
requestNdefWrite(bytes: number[], opts?: NdefWriteOpts): Promise<any>;
|
||||
onStateChanged(listener: (event: EventStateChange) => void): Promise<EmitterSubscription>;
|
||||
|
||||
mifareClassicSectorToBlock: (sector: number) => ArrayLike<number>
|
||||
mifareClassicReadBlock: (block: ArrayLike<number>) => ArrayLike<number>
|
||||
mifareClassicWriteBlock: (block: ArrayLike<number>, simpliArr: any[]) => void
|
||||
cancelTechnologyRequest: () => Promise<EmitterSubscription>
|
||||
closeTechnology: () => void
|
||||
requestTechnology: (data: any) => void
|
||||
getTag: () => void
|
||||
mifareClassicGetSectorCount: () => number
|
||||
mifareClassicAuthenticateA: (sector: number, keys: number[]) => void
|
||||
}
|
||||
const nfcManager: NfcManager;
|
||||
export namespace NdefParser {
|
||||
function parseUri(ndef: NdefRecord): ParseUriResult;
|
||||
function parseText(ndef: NdefRecord): string | null;
|
||||
}
|
||||
/**
|
||||
* common tech handler getters for both iOS / Android
|
||||
*/
|
||||
ndefHandler: NdefHandler;
|
||||
nfcAHandler: NfcAHandler;
|
||||
nfcVHandler: NfcVHandler;
|
||||
isoDepHandler: IsoDepHandler;
|
||||
|
||||
type ISOLangCode = "en" | string;
|
||||
type URI = string;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
export enum 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,
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
export const Ndef: {
|
||||
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", // [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]
|
||||
|
||||
text: {
|
||||
encodePayload: (text: string, lang?: ISOLangCode, encoding?: any) => NdefRecord;
|
||||
decodePayload: (data: Uint8Array) => string;
|
||||
};
|
||||
uri: {
|
||||
encodePayload: (uri: URI) => NdefRecord;
|
||||
decodePayload: (data: Uint8Array) => string;
|
||||
};
|
||||
util: {
|
||||
stringToBytes: (string: string) => any[];
|
||||
bytesToString: (bytes: any) => string;
|
||||
bytesToHexString: (bytes: any) => string;
|
||||
toHex: (i: any) => any;
|
||||
toPrintable: (i: any) => string;
|
||||
};
|
||||
isType(record: NdefRecord, tnf: number, type: string): boolean;
|
||||
stringify(data: number[], separator: string): string;
|
||||
encodeMessage(records: NdefRecord[]): Uint8Array;
|
||||
decodeMessage(bytes: any[] | Buffer): NdefRecord[];
|
||||
textRecord(text: string, lang?: ISOLangCode, encoding?: any): NdefRecord;
|
||||
uriRecord(uri: URI, id?: any): NdefRecord;
|
||||
}
|
||||
const nfcManager: NfcManager;
|
||||
|
||||
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;
|
||||
TNF_MIME_MEDIA: 0x02;
|
||||
TNF_ABSOLUTE_URI: 0x03;
|
||||
TNF_EXTERNAL_TYPE: 0x04;
|
||||
TNF_UNKNOWN: 0x05;
|
||||
TNF_UNCHANGED: 0x06;
|
||||
TNF_RESERVED: 0x07;
|
||||
|
||||
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]
|
||||
|
||||
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,
|
||||
lang?: ISOLangCode,
|
||||
encoding?: any,
|
||||
) => NdefRecord;
|
||||
decodePayload: (data: Uint8Array) => string;
|
||||
};
|
||||
uri: {
|
||||
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;
|
||||
bytesToHexString: (bytes: any) => string;
|
||||
toHex: (i: any) => any;
|
||||
toPrintable: (i: any) => string;
|
||||
};
|
||||
isType(record: NdefRecord, tnf: TNF, type: string): boolean;
|
||||
stringify(data: number[], separator: string): string;
|
||||
encodeMessage(records: NdefRecord[]): number[];
|
||||
decodeMessage(bytes: number[]): 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;
|
||||
|
||||
@ -9,10 +9,29 @@
|
||||
#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 *sessionEx;
|
||||
@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
|
||||
|
||||
@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);
|
||||
|
||||
1215
ios/NfcManager.m
1215
ios/NfcManager.m
File diff suppressed because it is too large
Load Diff
55
ios/Util.m
Normal file
55
ios/Util.m
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
69
ndef-lib/constants.js
Normal file
69
ndef-lib/constants.js
Normal file
@ -0,0 +1,69 @@
|
||||
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;
|
||||
@ -1,570 +1,122 @@
|
||||
// ndef.js
|
||||
// Copyright 2013 Don Coleman
|
||||
//
|
||||
// This code is from phonegap-nfc.js https://github.com/don/phonegap-nfc
|
||||
// originally from phonegap-nfc.js by Don Coleman
|
||||
// adapted for react-native-nfc-manager by Richie Hsieh
|
||||
|
||||
var util = require('./ndef-util'),
|
||||
textHelper = require('./ndef-text'),
|
||||
uriHelper = require('./ndef-uri');
|
||||
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 ndef = {
|
||||
const PrimitiveRecord = {
|
||||
emptyRecord() {
|
||||
return createNdefRecord(constants.TNF_EMPTY, [], [], []);
|
||||
},
|
||||
|
||||
// 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,
|
||||
absoluteUriRecord(uri, payload = [], id = []) {
|
||||
return createNdefRecord(constants.TNF_ABSOLUTE_URI, uri, 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]
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// handle null values
|
||||
if (!tnf) { tnf = ndef.TNF_EMPTY; }
|
||||
if (!type) { type = []; }
|
||||
if (!id) { id = []; }
|
||||
if (!payload) { payload = []; }
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
mimeMediaRecord(mimeType, payload, id = []) {
|
||||
return createNdefRecord(constants.TNF_MIME_MEDIA, mimeType, id, payload);
|
||||
},
|
||||
|
||||
externalTypeRecord(externalType, payload, id = []) {
|
||||
return createNdefRecord(
|
||||
constants.TNF_EXTERNAL_TYPE,
|
||||
externalType,
|
||||
id,
|
||||
payload,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function tnfToString(tnf) {
|
||||
var value = tnf;
|
||||
const WellKnownRecord = {
|
||||
textRecord(text, languageCode, id = []) {
|
||||
return createNdefRecord(
|
||||
constants.TNF_WELL_KNOWN,
|
||||
constants.RTD_TEXT,
|
||||
id,
|
||||
textHelper.encodePayload(text, languageCode),
|
||||
);
|
||||
},
|
||||
|
||||
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;
|
||||
uriRecord(uri, id = []) {
|
||||
return createNdefRecord(
|
||||
constants.TNF_WELL_KNOWN,
|
||||
constants.RTD_URI,
|
||||
id,
|
||||
uriHelper.encodePayload(uri),
|
||||
);
|
||||
},
|
||||
|
||||
smartPoster(ndefRecords, id = []) {
|
||||
let payload = [];
|
||||
|
||||
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 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;
|
||||
}
|
||||
return createNdefRecord(
|
||||
constants.TNF_WELL_KNOWN,
|
||||
constants.RTD_SMART_POSTER,
|
||||
id,
|
||||
payload,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
// convert bytes to a String
|
||||
function s(bytes) {
|
||||
if (typeof(bytes) === 'string') {
|
||||
return bytes;
|
||||
}
|
||||
const ExtraTypeRecord = {
|
||||
androidApplicationRecord(packageName, id = []) {
|
||||
return PrimitiveRecord.externalTypeRecord(
|
||||
'android.com:pkg',
|
||||
packageName,
|
||||
id,
|
||||
);
|
||||
},
|
||||
|
||||
return bytes.reduce(function (acc, byte) {
|
||||
return acc + String.fromCharCode(byte);
|
||||
}, '')
|
||||
// return new Buffer(bytes).toString();
|
||||
}
|
||||
wifiSimpleRecord: function (credentials, id = []) {
|
||||
let payload = wifiSimpleHelper.encodePayload(credentials);
|
||||
return PrimitiveRecord.mimeMediaRecord(constants.MIME_WFA_WSC, payload, id);
|
||||
},
|
||||
};
|
||||
|
||||
// expose helper objects
|
||||
ndef.text = textHelper;
|
||||
ndef.uri = uriHelper;
|
||||
ndef.tnfToString = tnfToString;
|
||||
ndef.util = util;
|
||||
ndef.stringify = stringifier.stringify;
|
||||
const NDEF = {
|
||||
...constants,
|
||||
...PrimitiveRecord,
|
||||
...WellKnownRecord,
|
||||
...ExtraTypeRecord,
|
||||
|
||||
module.exports = ndef;
|
||||
record: createNdefRecord,
|
||||
encodeMessage: encodeNdefMessage,
|
||||
decodeMessage: decodeNdefMessage,
|
||||
isType: equalToRecordType,
|
||||
|
||||
// individual record type helpers
|
||||
text: textHelper,
|
||||
uri: uriHelper,
|
||||
wifiSimple: wifiSimpleHelper,
|
||||
|
||||
// other helpers
|
||||
util,
|
||||
stringify: stringifier.stringify,
|
||||
};
|
||||
|
||||
module.exports = NDEF;
|
||||
|
||||
@ -1,61 +1,33 @@
|
||||
var util = require('./ndef-util');
|
||||
var util = require('./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
|
||||
|
||||
var languageCodeLength = (data[0] & 0x3F), // 6 LSBs
|
||||
languageCode = data.slice(1, 1 + languageCodeLength),
|
||||
utf16 = (data[0] & 0x80) !== 0; // assuming UTF-16BE
|
||||
|
||||
if (utf16) {
|
||||
return util.bytesToString(data.slice(languageCodeLength + 1));
|
||||
}
|
||||
|
||||
var utf8 = "";
|
||||
var i, len, c;
|
||||
var char2, char3;
|
||||
// TODO need to deal with UTF in the future
|
||||
// console.log("lang " + languageCode + (utf16 ? " utf16" : " utf8"));
|
||||
|
||||
len = data.length;
|
||||
i = 0;
|
||||
while(i < len) {
|
||||
c = data[i++];
|
||||
switch(c >> 4)
|
||||
{
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||
utf8 += String.fromCharCode(c);
|
||||
break;
|
||||
case 12: case 13:
|
||||
char2 = data[i++];
|
||||
utf8 += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
|
||||
break;
|
||||
case 14:
|
||||
char2 = data[i++];
|
||||
char3 = data[i++];
|
||||
utf8 += String.fromCharCode(((c & 0x0F) << 12) |
|
||||
((char2 & 0x3F) << 6) |
|
||||
((char3 & 0x3F) << 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 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';
|
||||
}
|
||||
|
||||
// ISO/IANA language code, but we're not enforcing
|
||||
if (!lang) { lang = 'en'; }
|
||||
var encoded = util.stringToBytes(lang + text);
|
||||
encoded.unshift(lang.length);
|
||||
|
||||
var encoded = util.stringToBytes(lang + text);
|
||||
encoded.unshift(lang.length);
|
||||
|
||||
return encoded;
|
||||
return encoded;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encodePayload: encode,
|
||||
decodePayload: decode
|
||||
}
|
||||
encodePayload: encode,
|
||||
decodePayload: decode,
|
||||
};
|
||||
|
||||
@ -1,49 +1,44 @@
|
||||
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:" ]
|
||||
const util = require('./util');
|
||||
const protocols = require('./constants').RTD_URI_PROTOCOLS;
|
||||
|
||||
// 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;
|
||||
}
|
||||
});
|
||||
|
||||
if (!prefix) {
|
||||
prefix = "";
|
||||
// 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;
|
||||
}
|
||||
|
||||
encoded = util.stringToBytes(uri.slice(prefix.length));
|
||||
protocolCode = protocols.indexOf(prefix);
|
||||
// prepend protocol code
|
||||
encoded.unshift(protocolCode);
|
||||
|
||||
return encoded;
|
||||
});
|
||||
|
||||
if (!prefix) {
|
||||
prefix = '';
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
// ndef-util.js
|
||||
// Copyright 2013 Don Coleman
|
||||
//
|
||||
|
||||
// Thanks to
|
||||
// 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 utf8 = [];
|
||||
for (var i=0; i < str.length; i++) {
|
||||
var charcode = str.charCodeAt(i);
|
||||
if (charcode < 0x80) utf8.push(charcode);
|
||||
else if (charcode < 0x800) {
|
||||
utf8.push(0xc0 | (charcode >> 6),
|
||||
0x80 | (charcode & 0x3f));
|
||||
}
|
||||
else if (charcode < 0xd800 || charcode >= 0xe000) {
|
||||
utf8.push(0xe0 | (charcode >> 12),
|
||||
0x80 | ((charcode>>6) & 0x3f),
|
||||
0x80 | (charcode & 0x3f));
|
||||
}
|
||||
// surrogate pair
|
||||
else {
|
||||
i++;
|
||||
// UTF-16 encodes 0x10000-0x10FFFF by
|
||||
// subtracting 0x10000 and splitting the
|
||||
// 20 bits of 0x0-0xFFFFF into two halves
|
||||
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
|
||||
| (str.charCodeAt(i) & 0x3ff));
|
||||
utf8.push(0xf0 | (charcode >>18),
|
||||
0x80 | ((charcode>>12) & 0x3f),
|
||||
0x80 | ((charcode>>6) & 0x3f),
|
||||
0x80 | (charcode & 0x3f));
|
||||
}
|
||||
}
|
||||
return utf8;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
114
ndef-lib/ndef-wifi-simple.js
Normal file
114
ndef-lib/ndef-wifi-simple.js
Normal file
@ -0,0 +1,114 @@
|
||||
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,
|
||||
};
|
||||
197
ndef-lib/ndef.js
Normal file
197
ndef-lib/ndef.js
Normal file
@ -0,0 +1,197 @@
|
||||
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,
|
||||
};
|
||||
166
ndef-lib/stringifier.js
Normal file
166
ndef-lib/stringifier.js
Normal file
@ -0,0 +1,166 @@
|
||||
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;
|
||||
139
ndef-lib/util.js
Normal file
139
ndef-lib/util.js
Normal file
@ -0,0 +1,139 @@
|
||||
// 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
57
ndef.test.js
@ -1,57 +0,0 @@
|
||||
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
Normal file
34445
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
52
package.json
52
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "react-native-nfc-manager",
|
||||
"version": "1.2.4",
|
||||
"version": "3.13.5",
|
||||
"description": "A NFC module for react native.",
|
||||
"main": "NfcManager",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/whitedogg13/react-native-nfc-manager.git"
|
||||
@ -17,18 +17,15 @@
|
||||
"nfc"
|
||||
],
|
||||
"files": [
|
||||
"ByteParser.js",
|
||||
"NfcManager.js",
|
||||
"NativeNfcManager.js",
|
||||
"NdefParser.js",
|
||||
"src",
|
||||
"ndef-lib",
|
||||
"index.d.ts",
|
||||
"android",
|
||||
"ios",
|
||||
"react-native-nfc-manager.podspec",
|
||||
"example"
|
||||
"app.plugin.js"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "Richie",
|
||||
"url": "https://github.com/whitedogg13"
|
||||
@ -36,14 +33,41 @@
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-runtime"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"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"
|
||||
"@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"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"test": "jest -i",
|
||||
"release": "dotenv release-it --"
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,5 +14,9 @@ Pod::Spec.new do |s|
|
||||
s.source_files = "ios/**/*.{h,m}"
|
||||
s.platform = :ios, "8.0"
|
||||
|
||||
s.dependency "React"
|
||||
s.dependency "React-Core"
|
||||
|
||||
s.xcconfig = {
|
||||
'OTHER_LDFLAGS': '-weak_framework CoreNFC',
|
||||
}
|
||||
end
|
||||
49
setup.md
Normal file
49
setup.md
Normal file
@ -0,0 +1,49 @@
|
||||
## iOS
|
||||
|
||||
1. In [apple developer site](https://developer.apple.com/), enable capability for NFC
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
If this is the first time you toggle the capabilities, the Xcode will generate a `<your-project>.entitlement` file for you:
|
||||
|
||||

|
||||
|
||||
4. in Xcode, review the generated entitlement. It should look like this:
|
||||
|
||||

|
||||
|
||||
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" />
|
||||
```
|
||||
33
src/NativeNfcManager.js
Normal file
33
src/NativeNfcManager.js
Normal file
@ -0,0 +1,33 @@
|
||||
'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};
|
||||
166
src/NfcError.js
Normal file
166
src/NfcError.js
Normal file
@ -0,0 +1,166 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
227
src/NfcManager.js
Normal file
227
src/NfcManager.js
Normal file
@ -0,0 +1,227 @@
|
||||
'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,
|
||||
};
|
||||
135
src/NfcManagerAndroid.js
Normal file
135
src/NfcManagerAndroid.js
Normal file
@ -0,0 +1,135 @@
|
||||
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};
|
||||
151
src/NfcManagerIOS.js
Normal file
151
src/NfcManagerIOS.js
Normal file
@ -0,0 +1,151 @@
|
||||
'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};
|
||||
131
src/NfcTech/Iso15693HandlerIOS.js
Normal file
131
src/NfcTech/Iso15693HandlerIOS.js
Normal file
@ -0,0 +1,131 @@
|
||||
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};
|
||||
35
src/NfcTech/IsoDepHandler.js
Normal file
35
src/NfcTech/IsoDepHandler.js
Normal file
@ -0,0 +1,35 @@
|
||||
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};
|
||||
114
src/NfcTech/MifareClassicHandlerAndroid.js
Normal file
114
src/NfcTech/MifareClassicHandlerAndroid.js
Normal file
@ -0,0 +1,114 @@
|
||||
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};
|
||||
32
src/NfcTech/MifareUltralightHandlerAndroid.js
Normal file
32
src/NfcTech/MifareUltralightHandlerAndroid.js
Normal file
@ -0,0 +1,32 @@
|
||||
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};
|
||||
16
src/NfcTech/NdefFormatableHandlerAndroid.js
Normal file
16
src/NfcTech/NdefFormatableHandlerAndroid.js
Normal file
@ -0,0 +1,16 @@
|
||||
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};
|
||||
50
src/NfcTech/NdefHandler.js
Normal file
50
src/NfcTech/NdefHandler.js
Normal file
@ -0,0 +1,50 @@
|
||||
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};
|
||||
14
src/NfcTech/NfcAHandler.js
Normal file
14
src/NfcTech/NfcAHandler.js
Normal file
@ -0,0 +1,14 @@
|
||||
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};
|
||||
39
src/NfcTech/NfcVHandler.js
Normal file
39
src/NfcTech/NfcVHandler.js
Normal file
@ -0,0 +1,39 @@
|
||||
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};
|
||||
35
src/__mocks__/NativeNfcManager.js
Normal file
35
src/__mocks__/NativeNfcManager.js
Normal file
@ -0,0 +1,35 @@
|
||||
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};
|
||||
35
src/index.js
Normal file
35
src/index.js
Normal file
@ -0,0 +1,35 @@
|
||||
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,
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user