Compare commits

...

277 Commits

Author SHA1 Message Date
Richie Hsieh
ee0dbed93c chore: update license to MIT 2022-07-23 14:22:32 +08:00
Richie
648aeb95ca chore: release 3.13.5 2022-06-25 20:12:45 +08:00
Richie
0ea6a38721
Merge pull request #536 from revtel/fix/android-nfc-setting-ex
fix(android): avoid native exception while jumping to system NFC settings
2022-06-25 18:10:46 +08:00
Richie
9607b509b5 fix(android): avoid native exception while jumping to system NFC settings 2022-06-25 18:08:36 +08:00
Richie
899856dc4c chore: release 3.13.4 2022-06-25 17:38:40 +08:00
Richie
eaa7f7ad22
Merge pull request #534 from revtel/dependabot/npm_and_yarn/jsdom-16.7.0
build(deps): bump jsdom from 16.4.0 to 16.7.0
2022-06-25 17:37:21 +08:00
Richie
2572d6271a
Merge pull request #527 from revtel/dependabot/npm_and_yarn/simple-plist-1.3.1
build(deps): bump simple-plist from 1.1.1 to 1.3.1
2022-06-25 17:37:13 +08:00
Richie
43a89b88a6
Merge pull request #517 from revtel/dependabot/npm_and_yarn/ansi-regex-3.0.1
build(deps): bump ansi-regex from 3.0.0 to 3.0.1
2022-06-25 17:37:00 +08:00
Richie
0b7a9622b6
Merge pull request #516 from revtel/dependabot/npm_and_yarn/plist-3.0.5
build(deps): bump plist from 3.0.1 to 3.0.5
2022-06-25 17:36:50 +08:00
Richie
8184c65e6f
Merge pull request #514 from revtel/dependabot/npm_and_yarn/async-2.6.4
build(deps): bump async from 2.6.3 to 2.6.4
2022-06-25 17:36:29 +08:00
Richie
02b7a9e157
Merge pull request #505 from revtel/dependabot/npm_and_yarn/minimist-1.2.6
build(deps): bump minimist from 1.2.5 to 1.2.6
2022-06-25 17:36:19 +08:00
Richie
6dadd629f9
Merge pull request #533 from ericksprengel/fix/ios-12.5.5
fix: add CoreNFC as a waek framework to avoid crashes on devices without CoreNFC
2022-06-25 17:35:03 +08:00
Richie
e829e3693a
Merge pull request #535 from revtel/fix/ios-felica-fix
Fix/ios felica fix
2022-06-25 17:25:06 +08:00
Richie
c9b9013c6d fix(ios): only append ISO18092 polling flag when felica is added into
scanning techs

- so we don't need to force the user to setup
  "com.apple.developer.nfc.readersession.felica.systemcodes"
2022-06-25 17:21:39 +08:00
dependabot[bot]
e8f8863436
build(deps): bump jsdom from 16.4.0 to 16.7.0
Bumps [jsdom](https://github.com/jsdom/jsdom) from 16.4.0 to 16.7.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/16.4.0...16.7.0)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-25 02:53:57 +00:00
Erick Massa Sprengel
527bebd15f fix: add CoreNFC as a waek framework to avoid crashes on devices without
CoreNFC
2022-06-20 20:56:31 -03:00
kikuchy
5aef17bcd6 Add NFCPollingISO18092 for polling on iOS 2022-06-18 18:14:48 +09:00
dependabot[bot]
eff40abc01
build(deps): bump simple-plist from 1.1.1 to 1.3.1
Bumps [simple-plist](https://github.com/wollardj/simple-plist) from 1.1.1 to 1.3.1.
- [Release notes](https://github.com/wollardj/simple-plist/releases)
- [Commits](https://github.com/wollardj/simple-plist/compare/v1.1.1...v1.3.1)

---
updated-dependencies:
- dependency-name: simple-plist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-02 02:44:04 +00:00
dependabot[bot]
5c02feea32
build(deps): bump ansi-regex from 3.0.0 to 3.0.1
Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-30 14:49:26 +00:00
dependabot[bot]
aebfa93318
build(deps): bump plist from 3.0.1 to 3.0.5
Bumps [plist](https://github.com/TooTallNate/node-plist) from 3.0.1 to 3.0.5.
- [Release notes](https://github.com/TooTallNate/node-plist/releases)
- [Changelog](https://github.com/TooTallNate/plist.js/blob/master/History.md)
- [Commits](https://github.com/TooTallNate/node-plist/commits)

---
updated-dependencies:
- dependency-name: plist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-30 14:49:25 +00:00
Richie
884120826c chore: release 3.13.3 2022-04-30 22:48:42 +08:00
Richie
f4a05a08d3
Merge pull request #515 from ShiriNmi1520/fix/xcode_build
XCode 13 build failed fix
2022-04-30 22:21:10 +08:00
Y.C.Huang
90394fd4a0
fix: Change dependency name
Fix Xcode build failed issue
2022-04-30 20:24:55 +08:00
dependabot[bot]
a190d06482
build(deps): bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-29 16:43:12 +00:00
Richie
f4e0615d35
doc: add NFC course info 2022-04-03 21:16:10 +08:00
dependabot[bot]
543b733e54
build(deps): bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-31 17:23:06 +00:00
Richie
228d1fbb44
Merge pull request #497 from javix64/feature/mifareClassicNewMethods
[ENH] Add increment, decrement, transfer and authenticateKeyB
2022-02-20 10:20:08 +08:00
javix64
f4a31269e9 [ENH] Add increment, decrement, transfer and authenticateKeyB 2022-02-19 11:26:55 +01:00
Richie
abd84bb113 chore: release 3.13.2 2022-02-19 12:09:31 +08:00
Richie
83eeda33be
Merge pull request #496 from revtel/android-fix-reader-mode
fix(android): handle enableReaderMode related issues
2022-02-19 12:07:25 +08:00
Richie
38d1f4e8ad fix(android): handle extra enableReaderMode related issues 2022-02-19 11:55:57 +08:00
Richie
c396c553ff chore: release 3.13.1 2022-02-06 22:12:01 +08:00
Richie
2f7c87182a
Merge pull request #490 from JdaieLin/main
fix: correct order of externalTypeRecord params
2022-02-06 09:25:59 +08:00
JdaieLin
cd0ea79f70 fix: correct order of externalTypeRecord params 2022-02-05 22:11:29 +08:00
Richie
7003eebbff refactor(ios): extract Util.m 2022-01-28 16:06:20 +08:00
Richie
da661500e6 chore: format android files 2022-01-27 10:17:47 +08:00
Richie
391f229a40 chore: release 3.13.0 2022-01-26 15:17:29 +08:00
Richie
f6fe75306e
Merge pull request #486 from revtel/iso15693-send-request
feat(ios): support iso15693 sendRequest
2022-01-26 15:16:12 +08:00
Richie
14d49fc28b feat: provide unified NfcV handler 2022-01-25 15:59:32 +08:00
Richie
ba676d9ea5 feat(ios): support iso15693 sendRequest 2022-01-20 10:50:15 +08:00
Richie
198c03e238 chore: release 3.12.0 2022-01-19 13:23:51 +08:00
Richie
d1c79c79ad feat: add NdefFormatableHandlerAndroid 2022-01-18 15:30:54 +08:00
Richie
d385f16067 chore: release 3.11.5 2022-01-13 10:48:53 +08:00
Richie
8687bc49f9 chore: update lock file 2022-01-13 10:48:06 +08:00
Richie
8b90348f33
Merge pull request #478 from EvanBacon/@evanbacon/config-plugin/bump-to-sdk-31
[config plugin] bump to minimum Android SDK version to 31
2022-01-13 10:46:34 +08:00
dependabot[bot]
d35545ccd4
build(deps): bump ua-parser-js from 0.7.23 to 0.7.31 (#474)
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.23 to 0.7.31.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.23...0.7.31)

---
updated-dependencies:
- dependency-name: ua-parser-js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-13 10:46:10 +08:00
Evan Bacon
99c195847c Update README.md 2022-01-12 11:35:51 -07:00
Evan Bacon
90fa88623c Make Expo Config Plugin bump compileSdkVersion to at least 31 2022-01-12 11:34:28 -07:00
Richie
e1fabc6f3d
doc: fix incorrect link to index.d.ts 2022-01-09 16:16:30 +08:00
Richie
1d1eecef19 chore: release 3.11.4 2022-01-09 16:12:09 +08:00
Richie
b3a6fc2780 doc: provide cleaner explaination for NFC beginners 2022-01-09 16:10:31 +08:00
likrot
9cd1b7e50e
fix(android): Fix for 'new NativeEventEmitter()' warning which appear with using RN versions higher than 0.65.0 (#471) 2022-01-09 15:14:27 +08:00
Richie
6361cc630a chore: release 3.11.3 2022-01-09 11:20:09 +08:00
Richie
94874b3216 fix(typing): TagEvent.id should be string 2022-01-09 11:19:37 +08:00
Dan
ce60aa983f
ISO setup.md could be more enlightening. (#475)
* Added instructions for working with ISO7816 tags.

To work with ISO7816 tags developers must add om.apple.developer.nfc.readersession.iso7816.select-identifiers key in info.plist with values.

* Spelling and formating setup.md
2021-12-31 07:56:19 +08:00
Richie
8c077e492b
Merge pull request #467 from revtel/dependabot/npm_and_yarn/hosted-git-info-2.8.9
build(deps): bump hosted-git-info from 2.8.8 to 2.8.9
2021-12-28 10:03:28 +08:00
Richie
bb6c5767aa
Merge pull request #468 from revtel/dependabot/npm_and_yarn/browserslist-4.18.1
build(deps): bump browserslist from 4.16.1 to 4.18.1
2021-12-28 10:03:01 +08:00
Richie
7f6627d452
Merge pull request #466 from revtel/dependabot/npm_and_yarn/path-parse-1.0.7
build(deps): bump path-parse from 1.0.6 to 1.0.7
2021-12-28 10:01:20 +08:00
Richie
fa55d62958
Merge pull request #465 from revtel/dependabot/npm_and_yarn/glob-parent-5.1.2
build(deps): bump glob-parent from 5.1.1 to 5.1.2
2021-12-28 10:01:02 +08:00
Richie
37d02c03d1
Merge pull request #464 from revtel/dependabot/npm_and_yarn/tmpl-1.0.5
build(deps): bump tmpl from 1.0.4 to 1.0.5
2021-12-28 10:00:40 +08:00
Richie
42baa33bef
chore: update issue stale days from 60 to 90 2021-12-16 16:17:13 +08:00
Richie
67b4cde9c1
chore: update issue stale day from 30 to 60 2021-12-16 16:15:51 +08:00
richie
3c2cb1e758 chore: release 3.11.2 2021-12-14 11:17:33 +08:00
richie
18a74dbe8c fix(android): update default compileSdkVersion to 31 2021-12-14 11:16:35 +08:00
Richie
35b904c62a
doc: update README about android 12 2021-12-14 11:14:06 +08:00
dependabot[bot]
9dba218f09
build(deps): bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

---
updated-dependencies:
- dependency-name: hosted-git-info
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 02:48:04 +00:00
dependabot[bot]
d5e00c6dca
build(deps): bump browserslist from 4.16.1 to 4.18.1
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.1 to 4.18.1.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.1...4.18.1)

---
updated-dependencies:
- dependency-name: browserslist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 02:48:04 +00:00
dependabot[bot]
14251db428
build(deps): bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 02:48:04 +00:00
dependabot[bot]
ca01f31a84
build(deps): bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 02:48:03 +00:00
dependabot[bot]
adb65e895f
build(deps): bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 02:48:01 +00:00
richie
22402a2eb0 chore: release 3.11.1 2021-12-09 10:47:10 +08:00
Richie
59bc7f58e3
Merge pull request #463 from jack828/main
fix(android): explicitly set intent FLAG_MUTABLE for android 12+ support
2021-12-09 10:45:27 +08:00
Jack Burgess
a8760cfb39
fix(android): explicitly set intent FLAG_MUTABLE for android 12+ support 2021-12-06 17:20:48 +00:00
richie
280f3a2310 doc: include ReWriter android app link 2021-10-19 11:22:05 +08:00
richie
1072faab45 chore: release 3.11.0 2021-10-08 15:44:31 +08:00
Richie
863ac928f4
Merge pull request #443 from jineshfrancs/main
Update NfcManager.java
2021-10-08 15:11:45 +08:00
Jinesh Francis
5e0978fd46
Update NfcManager.java
Added broadcast event
2021-10-04 19:51:35 +05:30
Richie
2f027ef344
doc: add mifare read example to readme (#441)
feat: add mifare read example to readme
2021-10-04 10:10:29 +08:00
Vincenzo Domina
fd3a25c342 feat: add mifare read example to readme 2021-10-03 13:40:59 +02:00
richie
dc7e0deda8 chore: release 3.10.1 2021-09-21 22:17:12 +08:00
richie
eb74466bb1 fix: fix release script 2021-09-21 22:16:34 +08:00
Ognjen Marčeta
374b8bef95
Fixed issue with lost reference to a Tag when application is in background (#438) 2021-09-21 21:53:53 +08:00
MUHAMMED SHAHEEN TK
484314e4fc
doc: update setup.md (#418) 2021-08-10 14:00:18 +08:00
Richie
8b751ad5c2 chore: release 3.10.0 2021-08-03 08:15:36 +08:00
Evan Bacon
adc847b1f6
feat: [ECP] Fixed select identifiers and added system codes support (#414) 2021-08-03 08:13:32 +08:00
Anurag Parmar
7ab86f4481
fix: typo on line 11 sucm => such (#412) 2021-07-27 15:23:15 +08:00
Richie
c272b0fb21 chore: release 3.9.0 2021-07-10 11:02:46 +08:00
Richie
e369429326 feat(DiscoverBackgroundTag): support DiscoverBackgroundTag in JS 2021-07-10 10:59:31 +08:00
Richie
4cce6275c3 feat(DiscoverBackgroundTag): support DiscoverBackgroundTag event in native layer 2021-07-10 10:58:13 +08:00
Richie
2274106ea3 feat: getBackgroundTag for Android, and wrapper layer for iOS 2021-07-08 22:27:12 +08:00
Richie
650d1186d5 chore: release 3.8.0 2021-07-05 08:01:29 +08:00
Richie
245c1c4f63 feat: add support for iOS background tag reading 2021-07-05 07:51:13 +08:00
Richie
588df7b9f1
doc: update readme 2021-06-15 16:13:43 +08:00
Richie
a06b96325d
doc: update readme 2021-06-15 08:49:20 +08:00
yanwzhang
9c89f7fdf8
fix: recover stringify function to NDEF which has before (#403)
Co-authored-by: zhangyanwei <zhangyanwei@zbj.com>
2021-06-04 17:50:08 +08:00
Richie
0d9be43ea4 chore: release 3.7.0 2021-05-28 21:19:32 +08:00
Richie
de0c2c0684 doc: add more description about iOS setup and remove legacy v1/v2 docs 2021-05-28 21:17:57 +08:00
Dulmandakh
1aae79383d
fix: remove build tools version (#401) 2021-05-28 20:56:07 +08:00
Evan Bacon
d7e1047b8d
feat: added support for selectIdentifiers (#400) 2021-05-28 20:54:42 +08:00
Richie
02c942ad08 chore: release 3.6.0 2021-05-27 20:27:47 +08:00
Evan Bacon
00629a6f5a
Created Expo config plugin (#399)
* Created Expo config plugin

* Added docs

* Update package.json

* Update README.md
2021-05-27 19:50:25 +08:00
Richie
0259f4b009 doc: add NfcReWriter app link into README 2021-05-16 18:22:54 +08:00
Richie
b2cfe2efe5 chore: release 3.5.0 2021-05-15 10:33:48 +08:00
Richie
3c44eabd60 fix: TagResponseNfcErrorBase -> TagResopnseError 2021-05-15 10:30:16 +08:00
Richie
e190066be6 fix: adjust test cases 2021-05-15 10:26:46 +08:00
Richie
b6c3b0d319
feat: better error handling for registerTagEvent path (#396) 2021-05-15 10:04:21 +08:00
Richie
c7eeb9934d
chore: add github actions for stale issues 2021-05-10 23:05:24 +08:00
Richie
6ae8d02112 chore: release 3.4.0 2021-05-10 22:50:05 +08:00
Richie
43fd209335
feat: enhance error checking (#394)
* feat: add NfcError module

* chore: update index.d.ts for NfcError

* draft: enhance NfcError

* fix: export correct NfcErrorIOS, and fix bug in cancelTechnology

* feat: wrap all callNative with handleNativeException

* fix(android): use buildNfcExceptionAndroid rather than
handleNativeException for requestTechnology and cancelTechnologyRequest

* feat: enhance Android's native error message

* feat: enhance index.d.ts

* fix: export NfcError namespace
2021-05-10 22:47:19 +08:00
Richie
4f3427b84a
Merge pull request #388 from maheshwarimrinal/patch-1
Adding mavenCentral() as jcenter() is shutting
2021-05-08 08:08:17 +08:00
Richie
160dfd70c4
chore: update slack invitation link 2021-05-07 17:44:56 +08:00
Richie
d039d0df59 chore: release 3.3.0 2021-04-21 15:27:46 +08:00
Richie
b8f3345906 feat: add setAlertMessage, enhance cancelTechnologyRequest
* setAlertMessage is no-op for Android, and the same as setAlertMessageIOS
for iOS.
* cancelTechnology won't throw any exceptions by default.
2021-04-21 15:25:00 +08:00
Mrinal Maheshwari
d5397d55af
Adding mavenCentral() as jcenter() is shutting
Adding mavenCentral() as jcenter() is shutting down
2021-04-19 11:01:18 +05:30
Richie
18f3f4427d chore: release 3.2.2 2021-04-08 07:47:25 +08:00
Richie
91899feb1b fix: correct handler and constant for
mifareUltralightHandlerAndroid
2021-04-08 07:46:05 +08:00
Richie
0c35b51525 chore: release 3.2.1 2021-04-05 21:39:14 +08:00
Richie
0f7b9856d1 fix: index.d.ts for NfcErrorIOS 2021-04-05 21:37:50 +08:00
Richie
1cf21205a1 chore: release 3.2.0 2021-04-05 21:17:30 +08:00
Richie
44c4a20c5f fix: NfcErrorIOS.parse should only handle string as input 2021-04-05 21:10:00 +08:00
Richie
3a78967673 chore: code formating 2021-04-05 20:59:29 +08:00
Richie
2a8291e392 feat: add NfcErrorIOS, which exports errCodes and parse function 2021-04-05 20:50:54 +08:00
Richie
09e792f615 Merge branch 'main' of https://github.com/whitedogg13/react-native-nfc-manager into main 2021-04-05 19:36:32 +08:00
Richie
f33bc989fd feat: export RTD_BYTES_xxx 2021-04-05 19:35:56 +08:00
Richie
ec0d0fb2b5
doc: update slack link 2021-03-30 07:17:10 +08:00
Richie
58c551be3d chore: release 3.1.1 2021-03-25 07:50:20 +08:00
Richie
fd38edde10 fix: (android) add setTimeout into index.d.ts 2021-03-25 07:49:11 +08:00
Richie
d1eea850a7 chore: release 3.1.0 2021-03-23 10:23:57 +08:00
Richie
ad1e45d2bc feat: ios isEnabled always return true 2021-03-23 10:21:52 +08:00
Richie
354e77d290 Merge branch 'main' of https://github.com/whitedogg13/react-native-nfc-manager into main 2021-03-23 10:14:21 +08:00
Richie
993013ab31 fix: comply isoDep.transceive interface for iOS 2021-03-23 10:13:55 +08:00
Richie
5c8cfd4820 chore: release 3.0.3 2021-03-12 21:40:47 +08:00
Richie
a0bb82cd67 fix: type def for transceive 2021-03-12 21:33:16 +08:00
Richie
c410ff438e doc: remove legacy v1 / v2 examples, point doc to v2 branch 2021-03-10 07:49:39 +08:00
Richie
5834344efc doc: add "latest changes" section 2021-03-04 22:58:03 +08:00
Richie
363dc8fc30 chore: generate CHANGELOG.md for the first time 2021-02-26 08:21:00 +08:00
Richie
d700071ba8 build: add changelog for release 2021-02-26 08:10:39 +08:00
Richie
bca427fc80 Release 3.0.2 2021-02-22 21:57:01 +08:00
Richie
8b7f8629a8 feat(android): allow requestTechnology resolves to tags even when tech
doesn't match
2021-02-22 21:53:08 +08:00
Richie
6c640721c8 Release 3.0.1 2021-02-20 21:08:12 +08:00
Richie
0a28937ec5 Merge branch 'main' of https://github.com/whitedogg13/react-native-nfc-manager into main 2021-02-20 21:07:36 +08:00
Richie
b1364197c8 fix: #371 missing MIFARE_BLOCK_SIZE for mifareClassicHandlerAndroid 2021-02-20 21:06:09 +08:00
Richie
084d511f0f
doc: add NfcManager.start() into examples 2021-02-18 21:28:51 +08:00
Richie
5f72f0cf04 Release 3.0.0 2021-02-18 20:24:22 +08:00
Richie
6d8ae7ed81 chore: code formatting 2021-02-18 20:22:50 +08:00
Richie
26516a7ce8 feat: expose RTD_URI_PROTOCOLS 2021-02-18 20:15:12 +08:00
Richie
3b197f3eec test: don't use direct path to access ndef text helper 2021-02-18 20:13:39 +08:00
Richie
9068abf4f2 doc: enhance readme 2021-02-18 16:52:05 +08:00
Richie
8b45c911ef Release 3.0.0-0 2021-02-10 11:12:50 +08:00
Richie
f019aa6234 build: add release-it 2021-02-10 11:10:37 +08:00
Richie
b3e42b1506 Merge branch 'master' of https://github.com/whitedogg13/react-native-nfc-manager into v3 2021-02-10 11:03:43 +08:00
Richie
824854837d
doc: update README about NDEF formatting 2021-02-10 10:01:27 +08:00
Richie
934d983a77
doc: create FAQ.md 2021-02-09 22:35:01 +08:00
Richie
2ccc038f85 refactor: extract externalTypeRecord, and use it for AAR 2021-02-07 14:15:42 +08:00
Richie
32c6d2965d chore: remove ByteParse.test.js 2021-02-07 13:52:02 +08:00
Richie
5a83f66e09 refactor: update index.d.ts 2021-02-07 12:47:52 +08:00
Richie
bbe12ef834 refactor: minor changes 2021-02-07 11:47:24 +08:00
Richie
4dd0b4b771 refactor: move isType into ndef.js 2021-02-07 11:46:30 +08:00
Richie
5f3607ee07 refactor: remove NdefParser for v3 2021-02-07 11:33:35 +08:00
Richie
54d90d2e9f refactor: remove ByteParser 2021-02-07 11:19:17 +08:00
Richie
545c3a3899 refactor: more on ndef-lib 2021-02-07 11:07:39 +08:00
Richie
104292bd69 refactor: ndef-lib file structure 2021-02-06 21:34:28 +08:00
Richie
4c94b1554b chore: fix warnings 2021-02-06 20:48:13 +08:00
Richie
a4b4a0dd46 chore: linter set up 2021-02-06 20:36:11 +08:00
Richie
0032408af2 feat: extract more NfcTech handlers 2021-02-02 09:45:24 +08:00
Richie
4cc68ce233 feat(android): getNdefStatus 2021-01-30 22:42:48 +08:00
Richie
32dd3be296 feat(ios): enhance didDetectTags and add queryNDEFStatus / makeReadOnly 2021-01-30 22:08:46 +08:00
Richie
64115f80f8 fix: disable 18092 polling (cause iOS freeze) 2021-01-28 22:27:17 +08:00
Richie
b7984a59ea fix(ios): add polling for iso18092 2021-01-28 20:50:37 +08:00
Richie
9af9ed31b5 Merge branch 'refactor' of https://github.com/whitedogg13/react-native-nfc-manager into refactor 2021-01-26 20:18:08 +08:00
Richie
312aed2659 refactor: extract NdefHandler 2021-01-26 20:17:39 +08:00
Richie
c95cc76a0a fix: import NfcManager from new location 2021-01-25 22:30:20 +08:00
Richie
2d6150d652 refactor: split NfcManager{IOS,Android} 2021-01-25 21:56:56 +08:00
Richie
14851a5865 refactor(ios): remove codes 'connectedNdefTag' 2021-01-25 09:31:26 +08:00
Richie
0ae08ae1c3 test: more on test 2021-01-25 09:17:06 +08:00
Richie
ad29dfe0cc refactor: new skeleton 2021-01-25 00:07:29 +08:00
Richie
5b19ff03f8 chore: bump version 2.2.1 2021-01-07 22:53:23 +08:00
Richie
ba08fe673d
Merge pull request #355 from ajacquierbret/allow-uint16-blockNumber-for-extended-commands
Set blockNumber param as uint16_t
2021-01-07 09:45:02 +08:00
Adrien Jacquier Bret
49ef6815ae Set blockNumber param as uint16_t 2021-01-06 19:28:35 +01:00
Richie
92f2d12c8f remove .Rhistory file 2020-10-05 21:42:46 +08:00
Richie
f5dc6f8b70 Merge branch 'master' of https://github.com/whitedogg13/react-native-nfc-manager 2020-10-05 21:42:29 +08:00
Richie
1281d0b5f7 update index.d.ts for AAR 2020-10-05 21:42:17 +08:00
Richie
7f44e067b8
Merge pull request #332 from AlexandraOlegovna/master
add readerModeDelay param
2020-10-05 21:40:02 +08:00
AlexandraOlegovna
9d17ff41bb add readerModeDelay param 2020-10-03 17:30:52 +03:00
Richie
86200ff53e update README about iOS 14 simulator issue 2020-09-20 22:24:31 +08:00
Richie
587efbf723 v2.2.0 2020-09-20 22:15:35 +08:00
Richie
c83750e686
Merge pull request #316 from monder/rmb
Add readMultipleBlocks method for iso15693 on iOS
2020-09-20 21:46:35 +08:00
Aleksejs Sinicins
94eae4c591 Add readMultipleBlocks method for iso15693 on iOS 2020-08-05 22:03:54 +03:00
Richie
70f8ead083
Merge pull request #312 from TobiasMelen/patch-1
Type definition NfcManager.requestTechnology
2020-07-13 09:21:52 +08:00
TobiasMelen
f86e63ed1a
Type definition NfcManager.requestTechnology
A small update to requestTechnology, previous declaration only accepted a single NfcTech and no options param.
The update makes tech argument accept single value or array of NfcTech and adds options parameter as optional RegisterTagEventOpts (I believe that's the correct type).
Thanks for creating, sharing and maintaining this library!
2020-07-11 12:58:05 +02:00
Richie
f9b9f41488
Merge pull request #308 from pedroyan/type-definitions-improvement
Added types definitions for record function
2020-07-01 09:33:43 +08:00
Pedro Yan Ornelas
8b7b7f620f Removed unreferenced buffer type 2020-06-30 21:34:30 -03:00
Pedro Yan Ornelas
f5f9741685 Fixed typo on return type 2020-06-30 19:23:54 -03:00
Pedro Yan Ornelas
2468eada87 Fixed type incompatibility between the encodeMessage method and the writeNDef method 2020-06-30 19:17:07 -03:00
Pedro Yan Ornelas
d46de6f96d Added types definitions for record function 2020-06-30 18:51:23 -03:00
Richie
aecd7fd689 v2.1.9 2020-05-24 22:29:43 +08:00
Richie
5e1586d1e0 disable iso18092 for ios 2020-05-24 22:26:41 +08:00
Richie
82c9b519cf v2.1.8 2020-05-12 09:29:48 +08:00
Richie
889c33c1d7
Merge pull request #288 from clrsustainas/return_isWriteable_canMakeReadonly
Fix jsonToReact(JSONArray jsonArray) when parsing boolean
2020-05-12 09:29:01 +08:00
Christian Rasmussen
b944436e8c Fix variable name in jsonToReact() 2020-05-11 19:04:12 +02:00
Richie
d0e163ed3a v2.1.7 2020-05-11 22:21:50 +08:00
Richie
f8fac99c0d
Merge pull request #287 from clrsustainas/return_isWriteable_canMakeReadonly
Make boolean values accessible in react, e.g. "isWriteable" and "canMakeReadonly"
2020-05-08 17:06:48 +08:00
Christian Rasmussen
2d60e596d3 Returns boolean values to react too, e.g. isWriteable and canMakeReadonly 2020-05-04 11:38:42 +02:00
Richie
d8150c2d64
Merge pull request #282 from hataguchi/feature/felica
Added sendFelicaCommandIOS
2020-04-24 13:21:50 +08:00
Akito Hataguchi
560abfcc85 ### Added
- sendFelicaCommandIOS method
2020-04-21 11:15:36 +09:00
hataguchi
5080b1ba70 ### Added
- sendFelicaCommandIOS method
2020-04-20 20:54:44 +09:00
Richie
324c95a0d3 v2.1.6 2020-04-07 00:26:28 +08:00
Richie
300b8fc1b1 (iOS) remove readerSession:didDetectTags call, and let Ndef tech can be
handled by tagReaderSession
2020-04-07 00:22:57 +08:00
Richie
995fb88f92 2.1.5 2020-04-06 22:25:43 +08:00
Richie
b805f3a6e3 fix #271, signed issue in Android ndef message payload 2020-04-06 22:24:03 +08:00
Richie
c4594e61ee 2.1.4 2020-03-13 21:56:02 +08:00
Richie
8572cabfca better ios error message 2020-03-13 21:54:05 +08:00
Richie
27ab072aa6 v2.1.3 2020-03-11 11:34:55 +08:00
Richie
65273ce0ac add type definition for wifi simple ndef 2020-03-11 11:33:00 +08:00
Richie
5935bd67f2 Merge branch 'master' into ndef-wifi-simple 2020-03-11 10:35:46 +08:00
Richie
67b3d5b9a2 ios: enhance getTag(), so it can be called with Ndef tech registration as
well
2020-03-09 23:47:02 +08:00
Richie
a6371d1693 v2.1.2 2020-03-09 22:53:27 +08:00
Richie
7e0b478b4c Merge branch 'master' of https://github.com/whitedogg13/react-native-nfc-manager 2020-03-09 22:51:08 +08:00
Richie
aaf385fb5c ios: allow specific tech to perform writeNdefMessage as well 2020-03-09 22:50:25 +08:00
Richie
ec9bc133f7 support ndef wifi simple record 2020-02-29 09:07:04 +08:00
Richie
053e4de272
Merge pull request #261 from levepic/master
Update README.md Launch app on nfc event
2020-02-27 20:43:30 +08:00
Richie
a10ddb7fe2
Merge pull request #260 from HannesOberreiter/master
added more details to getLaunchTag()
2020-02-27 20:43:05 +08:00
levepic
2cd94cecaf
Update README.md Launch app on nfc event 2020-02-27 11:37:42 +01:00
HannesOberreiter
ce61b3f265
Update APIv1.md
added more details for getLaunchTag()
2020-02-26 17:01:10 +01:00
Richie
ad32b07218
Update README.md 2020-02-12 00:55:44 +08:00
Richie
54d55d18c4 v2.1.1 2020-01-31 16:34:13 +08:00
Richie
3331bf9e4c (ios) add invalidateSession 2020-01-31 16:33:39 +08:00
Richie
5c249a7f20
Merge pull request #244 from JimTeva/patch-1
Update AppV2Iso15693.js
2020-01-28 18:03:07 +08:00
JimTeva
7f5af65631
Update AppV2Iso15693.js
As describe in issue https://github.com/whitedogg13/react-native-nfc-manager/issues/230, the interface for Iso15693 is 
interface Iso15693HandlerIOS {
    getSystemInfo: (
      requestFloags: number,
    ) => Promise<{ dsfid: number, afi: number, blockSize: number, blockCount: number, icReference: number}>;
    readSingleBlock: (params: {flags: number, blockNumber: 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[]>;
    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>; 
  }
It doesn't contain the 'IOS' at the end of the different methods like 'getSystemInfoIOS' which is 'getSystemInfo'
2020-01-27 22:25:01 -10:00
Richie
c6035b3703 v2.1.0 2020-01-28 07:56:59 +08:00
Richie
7915b98132 fix: reject tech request promise when native iOS cancel button is pressed 2020-01-28 07:54:43 +08:00
Richie
126e66f7b4 v2.1.0-beta.3 2020-01-18 21:37:36 +08:00
Richie
64c9cbd05e fix: promise won't be rejected when calling cancelTechnologyRequest in
iOS
2020-01-18 21:35:43 +08:00
Richie
6ddbb0814f v2.1.0-beta.2 2020-01-18 08:13:20 +08:00
Richie
73859fbac8 fix: getNdefMessage should use the same structure for platforms 2020-01-18 08:12:23 +08:00
Richie
53ff1787e6 v2.1.0-beta.1 2020-01-18 08:06:48 +08:00
Richie
581e7c4da7 enhance getTag for ios to return NdefMessage as well 2020-01-18 08:05:03 +08:00
Richie
995e2f0315 v2.1.0-beta.0 2020-01-08 09:49:02 +08:00
Richie
1c086af0cb More for iso15693 iOS feature 2020-01-07 16:59:37 +08:00
Richie
f32f92f16a Merge branch 'master' into ios-15693 2020-01-07 15:12:14 +08:00
Richie
65a3306a4e
Merge pull request #233 from Frans-L/fix-ts-type
Fix typescript declaration errors
2020-01-05 17:33:51 +08:00
Frans L
13715334ea fix: add callback type null possible 2020-01-04 21:32:54 +02:00
Frans L
6fc257e61e fix: remove getNdefMessage's parameter declaration 2020-01-04 21:29:23 +02:00
Richie
550c24a2a1 Merge branch 'master' into ios-15693 2020-01-04 09:44:45 +08:00
Richie
03b29872af v2.0.3 2019-12-31 14:20:31 +08:00
Richie
741a37a447 bug fix: no-op for setAlertMessageIOS when platform is Android 2019-12-31 14:04:50 +08:00
Richie
99995b7a1e move callNative to NativeNfcManager 2019-12-30 21:24:56 +08:00
Richie
968e8a8cfe bug fix, wrong native call 2019-12-30 21:23:57 +08:00
Richie
e274a60213 partial work 2019-12-30 15:04:30 +08:00
Richie
78b6c69906 bridge more iso15693 methods for ios 2019-12-30 10:34:28 +08:00
Richie
1c0ae74786 Merge branch 'master' into ios-15693 2019-12-30 09:16:55 +08:00
Richie
198e1de6eb v2.0.2 2019-12-07 21:11:39 +08:00
Richie
3779ca21cd update example to show more info about iOS apdu usage 2019-12-07 20:54:42 +08:00
Richie
adcc966c8e fix: ios isSupported 2019-12-07 20:49:38 +08:00
Richie
e04d8da0f1 handle null case when transforming NSData 2019-12-07 20:14:58 +08:00
Richie
f26806e451 enable two APDU forms for iOS, either raw bytes or structured APDU 2019-12-07 20:11:36 +08:00
Richie
77746767aa
Merge pull request #217 from acasademont/patch-1
Mention the new entitlements for iOS 13
2019-11-08 20:51:42 +08:00
Albert Casademont
31ab99e062
Fix typo 2019-11-08 12:51:31 +01:00
Albert Casademont
f9cff64855
Mention the new entitlements for iOS 13 2019-11-08 12:49:02 +01:00
Richie
b15488d9d6 ios15693: getSystemInfoWithRequestFlag 2019-11-05 21:51:24 +08:00
Richie
0e1bcca9c1 add Nfc15693RequestFlagIOS 2019-11-05 21:24:33 +08:00
Richie
210be517aa ios15693: expose tag info in getRNTag 2019-11-05 21:23:35 +08:00
Richie
e51b429f5f [ios] getRNTag() output historicalBytes and applicationData as byte
array for iso7816 tag
2019-11-05 21:12:59 +08:00
Richie
7be1cbdc36 add more examples 2019-11-05 20:58:46 +08:00
Richie
a3f5b08e46 2.0.1 2019-10-29 13:16:14 +08:00
Richie
c8bc318dcc
Merge pull request #214 from NicholasBertazzon/patch-1
ndef-util: handle UTF-8 encoded special characters
2019-10-29 13:13:59 +08:00
Nicholas
fc725ebf87
ndef-util: handle UTF-8 encoded special characters
Before, characters like "à" couldn't be parsed and they were falling back to "a" (another example: §)
2019-10-28 20:13:15 +01:00
Richie
92cbf7b941 2.0.0 2019-10-28 21:16:42 +08:00
Richie
f9ca22de32 android: .getTag() should contain NDEF message if possible 2019-10-28 21:13:38 +08:00
Richie
03700daa71 v2.0.0-beta.5 2019-10-24 23:06:58 +08:00
Richie
f71860dcab update type def for invalidateSessionWithErrorIOS 2019-10-24 23:05:20 +08:00
Richie
91a9db350a Merge branch 'v2-type-define' 2019-10-24 23:04:34 +08:00
Richie
942ff6aaf9 (ios) enhance getNdefMessage, allow non NFCNDEFTag to call
readNdefMessage
2019-10-24 23:03:33 +08:00
Richie
bf37d83a93 remove _config.yml 2019-10-24 22:37:39 +08:00
Richie
dac6ee1871 Merge branch 'master' of https://github.com/whitedogg13/react-native-nfc-manager 2019-10-24 22:36:04 +08:00
Richie
c631110b64 feat: invalidateSessionWithErrorIOS 2019-10-24 22:35:51 +08:00
Richie
69b5edf663 Set theme jekyll-theme-leap-day 2019-10-23 15:09:01 +08:00
77 changed files with 39290 additions and 4593 deletions

View File

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

6
.eslintignore Normal file
View File

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

6
.eslintrc.js Normal file
View File

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

20
.github/workflows/stale.yml vendored Normal file
View 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
View File

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

6
.prettierrc.js Normal file
View File

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

17
.release-it.json Normal file
View 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"
}
}
}

625
APIv1.md
View File

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

159
APIv2.md
View File

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

View File

@ -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,
}

View File

@ -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
View 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
View 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
View File

@ -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.

View File

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

View File

@ -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,
}

View File

@ -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);
});

View File

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

324
README.md
View File

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

17
__mocks__/react-native.js vendored Normal file
View 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;

View 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);
}
}
});
});

View 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
View 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);
});

View File

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

View File

@ -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();
}

View File

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

View File

@ -49,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
@ -122,12 +133,12 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
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");
callback.invoke(ERR_MULTI_REQ);
} else {
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
}
@ -150,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);
}
}
}
@ -172,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);
}
}
}
@ -190,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);
}
}
}
@ -209,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);
}
}
}
@ -261,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);
}
}
@ -301,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);
}
}
}
@ -323,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);
}
}
}
@ -350,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);
}
}
}
@ -383,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);
}
}
}
@ -420,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);
}
}
}
@ -457,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);
}
}
}
@ -478,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);
}
}
}
@ -499,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);
}
}
}
@ -513,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);
}
}
}
@ -558,12 +725,13 @@ 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);
}
}
}
@ -571,27 +739,27 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
@ReactMethod
public void connect(ReadableArray techs, Callback callback){
synchronized(this) {
try {
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
techRequest.connect(this.tag);
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
try {
techRequest = new TagTechnologyRequest(techs.toArrayList(), callback);
techRequest.connect(this.tag);
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
}
}
@ReactMethod
public void close(Callback callback){
synchronized(this) {
try {
techRequest.close();
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
try {
techRequest.close();
callback.invoke(null, null);
return;
} catch (Exception ex) {
callback.invoke(ex.toString());
}
}
}
@ -650,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);
}
}
}
@ -703,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);
}
}
}
@ -717,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);
}
}
}
@ -730,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");
@ -760,7 +929,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
formatReadOnly
);
} catch (FormatException e) {
callback.invoke("Incorrect ndef format");
callback.invoke(e.toString());
}
}
}
@ -785,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);
}
}
}
@ -803,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);
}
}
@ -820,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;
}
@ -858,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;
}
@ -880,11 +1056,23 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
}
@ReactMethod
private void registerTagEvent(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
@ -911,21 +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");
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");
@ -960,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);
@ -1003,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() {
@ -1077,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;
}
}
}
@ -1109,8 +1337,8 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
if (result) {
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);
}
}
@ -1194,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();
@ -1206,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) {
@ -1225,7 +1453,7 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
callback.invoke();
}
} catch (Exception ex) {
callback.invoke("writeNdef fail: " + ex.getMessage());
callback.invoke(ex.toString());
}
}
}

View File

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

View File

@ -39,9 +39,9 @@ public class Util {
// mTag.getTagService(); of the Ndef object sometimes returns null
// see http://issues.mroland.at/index.php?do=details&task_id=47
try {
json.put("canMakeReadOnly", ndef.canMakeReadOnly());
json.put("canMakeReadOnly", ndef.canMakeReadOnly());
} catch (NullPointerException e) {
json.put("canMakeReadOnly", JSONObject.NULL);
json.put("canMakeReadOnly", JSONObject.NULL);
}
} catch (JSONException e) {
Log.e(TAG, "Failed to convert ndef into json: " + ndef.toString(), e);
@ -97,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
View 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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

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

View File

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

View File

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

View File

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

View File

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
images/edit-entitlement.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
images/xcode-capability.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

397
index.d.ts vendored
View File

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

View File

@ -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);

File diff suppressed because it is too large Load Diff

55
ios/Util.m Normal file
View 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
View 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;

View File

@ -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;

View File

@ -1,33 +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
// TODO need to deal with UTF in the future
// console.log("lang " + languageCode + (utf16 ? " utf16" : " utf8"));
// TODO need to deal with UTF in the future
// console.log("lang " + languageCode + (utf16 ? " utf16" : " utf8"));
return util.bytesToString(data.slice(languageCodeLength + 1));
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,
};

View File

@ -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,
};

View File

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

View File

@ -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
View 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
View 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
View 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,
};

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{
"name": "react-native-nfc-manager",
"version": "2.0.0-beta.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 --"
}
}

View File

@ -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
View File

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

33
src/NativeNfcManager.js Normal file
View 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
View 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
View 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
View 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
View 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};

View 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};

View 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};

View 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};

View 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};

View 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};

View 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};

View 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};

View 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};

View 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
View 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,
};