@@ -20,6 +20,7 @@ import android.nfc.NdefMessage
2020import android.nfc.NfcAdapter
2121import android.nfc.Tag
2222import android.nfc.tech.IsoDep
23+ import android.nfc.tech.Ndef
2324import fr.acinq.lightning.utils.toByteVector
2425import fr.acinq.phoenix.android.components.nfc.NfcState
2526import fr.acinq.phoenix.android.components.nfc.NfcStateRepository
@@ -29,30 +30,47 @@ import org.slf4j.LoggerFactory
2930/* *
3031 * This class retrieves the first NDEF message in an NFC tag.
3132 *
32- * Sends a sequence of APDU commands to the tag, mirroring what's done in [HceService].
33+ * Two tag types are supported:
34+ * - ISO-DEP tags (NFC Forum Type 4, e.g. NTAG424-DNA, Bolt Cards): communicates via a sequence
35+ * of APDU commands, mirroring what's done in [HceService].
36+ * - Plain NDEF tags (NFC Forum Type 1/2, e.g. NTAG213/215/216): reads the NDEF message directly
37+ * using the Android [Ndef] API without APDU negotiation.
3338 */
3439class NfcReaderCallback (val onFoundData : (String ) -> Unit ) : NfcAdapter.ReaderCallback {
3540 private val log = LoggerFactory .getLogger(this ::class .java)
3641
3742 @OptIn(ExperimentalStdlibApi ::class )
3843 override fun onTagDiscovered (tag : Tag ? ) {
3944 log.info(" discovered tag=$tag " )
45+
46+ if (! NfcStateRepository .isReading()) {
47+ log.info(" aborting tag discovery: nfc_state=${NfcStateRepository .state.value} " )
48+ return
49+ }
50+
51+ // Prefer ISO-DEP (NFC Forum Type 4: NTAG424-DNA, Bolt Cards, …)
4052 val isoDep = IsoDep .get(tag)
41- if (isoDep = = null ) {
42- log.info( " aborting tag discovery: tag does not support iso-dep " )
53+ if (isoDep ! = null ) {
54+ readIsoDepTag(isoDep )
4355 return
4456 }
4557
46- if (! NfcStateRepository .isReading()) {
47- log.info(" aborting tag discovery: nfc_state=${NfcStateRepository .state.value} " )
48- isoDep.close()
58+ // Fallback: plain NDEF (NFC Forum Type 1/2: NTAG213, NTAG215, NTAG216, …)
59+ val ndef = Ndef .get(tag)
60+ if (ndef != null ) {
61+ readNdefTag(ndef)
4962 return
5063 }
5164
65+ log.info(" aborting tag discovery: tag does not support iso-dep or plain ndef" )
66+ }
67+
68+ @OptIn(ExperimentalStdlibApi ::class )
69+ private fun readIsoDepTag (isoDep : IsoDep ) {
5270 try {
53- log.debug(" connecting to tag={} " , tag)
71+ log.debug(" connecting to iso-dep tag" )
5472 isoDep.connect()
55- log.info(" nfc reader connected, starting communication..." )
73+ log.info(" iso-dep reader connected, starting communication..." )
5674
5775 val selectCommand = ApduCommands .SELECT_AID
5876 log.debug(" SEND select aid: ${selectCommand.toByteArray().toHexString()} " )
@@ -120,7 +138,7 @@ class NfcReaderCallback(val onFoundData: (String) -> Unit) : NfcAdapter.ReaderCa
120138 }
121139
122140 val m = NdefMessage (result)
123- log.info(" successfully read tag, found ndef_message=$m " )
141+ log.info(" successfully read iso-dep tag, found ndef_message=$m " )
124142 val d = m.records.mapNotNull {
125143 NdefParser .parseNdefRecord(it)
126144 }
@@ -129,13 +147,40 @@ class NfcReaderCallback(val onFoundData: (String) -> Unit) : NfcAdapter.ReaderCa
129147 }
130148
131149 } catch (e: Exception ) {
132- log.error(" failed to read tag: " , e)
150+ log.error(" failed to read iso-dep tag: " , e)
133151 } finally {
134152 isoDep.close()
135153 if (NfcStateRepository .isReading()) {
136154 NfcStateRepository .updateState(NfcState .Inactive )
137155 }
138- log.info(" terminated nfc reader callback" )
156+ log.info(" terminated iso-dep reader callback" )
157+ }
158+ }
159+
160+ private fun readNdefTag (ndef : Ndef ) {
161+ try {
162+ log.debug(" connecting to ndef tag type=${ndef.type} " )
163+ ndef.connect()
164+ log.info(" ndef reader connected, reading message..." )
165+
166+ val message = ndef.ndefMessage
167+ if (message == null ) {
168+ log.info(" ndef tag contains no message" )
169+ return
170+ }
171+
172+ log.info(" successfully read ndef tag, found message=$message " )
173+ val data = message.records.mapNotNull { NdefParser .parseNdefRecord(it) }
174+ data.firstOrNull()?.let { onFoundData(it) }
175+
176+ } catch (e: Exception ) {
177+ log.error(" failed to read ndef tag: " , e)
178+ } finally {
179+ ndef.close()
180+ if (NfcStateRepository .isReading()) {
181+ NfcStateRepository .updateState(NfcState .Inactive )
182+ }
183+ log.info(" terminated ndef reader callback" )
139184 }
140185 }
141186}
0 commit comments