Disclaimer: I'm pretty much new in NFC stuff. The official documentation is confusing for me. This is a test app to learn about NFC
So, I have this activity code in an Android app. I waant the activity to read an NFC tag (SDK 32). I don't have the oportunity to test it because I don't know what is a NFC_READ_COMMAND, and therefore, I cannot compile it. I am also unsure on how to properly get the data, I want to show the data in the "text_view_nfc" TextView.
I cannot compile because I don't know what NFC_READ_COMMAND is.
import com.miguel_rp.nfc_gestor_test.ActividadGestoraDeNFCsBase
import android.app.Activity
import android.nfc.NfcAdapter
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.nfc.Tag
import android.nfc.tech.NfcA
import android.widget.TextView
import kotlin.jvm.internal.Intrinsics
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.miguel_rp.nfc_gestor_test.R
class creador_de_cartas : ActividadGestoraDeNFCsBase() {
override var activity = this as Activity
var text_view_nfc: TextView? = null
private var nfcAdapter: NfcAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setContentView(R.layout.activity_creador_de_cartas)
text_view_nfc = findViewById(R.id.textViewContenido)
this.nfcAdapter = NfcAdapter.getDefaultAdapter(this)
}
private fun enableForegroundDispatch(activity: AppCompatActivity, adapter: NfcAdapter?) {
val intent = Intent(activity.applicationContext, activity.javaClass)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
val pendingIntent = PendingIntent.getActivity(activity.applicationContext, 0, intent, 0)
val filters = arrayOfNulls<IntentFilter>(1)
val techList = arrayOf<Array<String>>()
filters[0] = IntentFilter()
with(filters[0]) {
this?.addAction(NfcAdapter.ACTION_NDEF_DISCOVERED)
this?.addCategory(Intent.CATEGORY_DEFAULT)
try {
this?.addDataType("text/plain")
} catch (ex: IntentFilter.MalformedMimeTypeException) {
throw RuntimeException("e")
}
}
adapter?.enableForegroundDispatch(activity, pendingIntent, filters, techList)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
var tagFromIntent: Tag? = intent?.getParcelableExtra(NfcAdapter.EXTRA_TAG)
val nfc = NfcA.get(tagFromIntent)
val atqa: ByteArray = nfc.getAtqa()
val sak: Short = nfc.getSak()
nfc.connect()
val isConnected= nfc.isConnected()
if(isConnected)
{
val receivedData:ByteArray= nfc.transceive(NFC_READ_COMMAND)
//code to handle the received data
}else{
Log.e("ans", "Not connected")
}
}
override fun onResume() {
super.onResume()
enableForegroundDispatch(this, nfcAdapter)
}
}
In case you are wondering, "ActividadGestoraDeNFCsBase" is basically a parent class that makes sure that NFC is always activated while using the app.
package com.miguel_rp.nfc_gestor_test import android.app.Activity import android.content.Context import android.content.Intent import android.nfc.NfcAdapter import android.provider.Settings import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import java.util.* open class ActividadGestoraDeNFCsBase() : AppCompatActivity() { open lateinit var activity: Activity companion object { lateinit var nfcAdapter: NfcAdapter; var timer = Timer() fun isNFCEnabled(context: Context, actividad: Activity){ if (!nfcAdapter.isEnabled()) { actividad.runOnUiThread{ Toast.makeText(context, "Esta aplicación necesita tener el NFC activado", Toast.LENGTH_SHORT).show() } val intent = Intent(Settings.ACTION_NFC_SETTINGS) actividad.startActivity(intent) } } } override fun onResume() { super.onResume() val task = object : TimerTask() { override fun run() { isNFCEnabled(applicationContext, activity) } } timer.scheduleAtFixedRate(task, 0, 1000) } override fun onPause() { super.onPause() timer.cancel() timer = Timer() } }
Just in case, I'm using this NFC cards: Spanish Amazon. Sorry for that
CodePudding user response:
So a lot of NFC stuff is custom to the make and model of the NFC Tag, luckily your Amazon links say your using an NTAG215 which is a NFC Type2 Standard Tag.
The datasheet for this Tag tells you all you need to know about what "NFC_READ_COMMAND" needs to be to read this card.
The Tag can be read at the low level using the NfcA
standard.
The Wikipedia image gives a good overview of all the standards used in NFC.
So from the datasheet Section "8.5 Memory organization" you can see that this tag stores data in a number of 4 byte pages with page addresses starting at zero 0x00h
to 0x86h
for the Ntag 215.
Again from the datasheet some page addresses at the start and end of the range have specific functions and some are read only.
As you can see that a lot in NFC is about creating and decoding arrays of bytes.
Moving on in the datasheet to Section "10. NTAG commands" you can see the various commands that this Tag supports and their byte values.
One of these is the Read Command of 0x30h
, the read command takes a second arg that is the value of which page to read. (Note that the Java NfcA class handles adding the CRC value for you.
The Read Command returns an array of bytes which will the bytes read of 4 pages (16 bytes) from the start address given or the NAK error code.
So in your case a "NFC_READ_COMMAND" can be something like:-
byteArrayOf(0x30.toByte(), 0x00.toByte())
Which is to read (4 bytes) starting at page zero (these address contain data of the UID, Capability Container and other things)
Also because it is NFC Type 2 compliant you can also use the higher level Ndef
NFC data format standard to store and access data IF the data was stored using this format.
Details of how Ndef Data would be stored on a Type 2 Tag is at http://apps4android.org/nfc-specifications/NFCForum-TS-Type-2-Tag_1.1.pdf
UPDATE
I have just reread the code example, and the guide you are using is weird (not totally wrong but a complicated and weird way to do things)
It use the older and less functional/reliable enableForegroundDispatch
to get notified when a Tag containing Ndef data is presented.
But then assumes it's an NfcA
tag that is storing the Ndef data (which in your case with an NTAG215 it is)
IF a Tag was presented that also stored Ndef Data BUT was a NfcB
Tag, your code would get a Null pointer exception.
It would be much more logical instead of
val nfc = NfcA.get(tagFromIntent)
to do:-
val ndef = Ndef.get(tagFromIntent)
The you could use ndef.getNdefMessage()
to get the data on the Tag without having to use low level commands.
or as you are using enableForegroundDispatch
get the Ndef data direct from the Intent.
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
...
if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
// Process the messages array.
...
}
}
}
(Taken from https://developer.android.com/guide/topics/connectivity/nfc/nfc#obtain-info )