I am currently developing an Android NFC application. This application contains a NavigationDrawer
in which I can access 3 different fragments which each correspond to 3 different NFC features.
First, I want to verify that my application can scan an NFC tag (here I have an NXP NTAG 5 boost tag, which is an NFC Forum Type 5 tag).
My problem is that when my application is running, the onNewIntent
of my MainActivity
is never called.
I'm sure it's not due to the hardware as I can detect this tag with common applications, but it may be due to parameters I passed to a function in the NfcManager
file, like activity.applicationContext
but I'm not sure.
For now, I specify that I don't want a particular activity to launch when a tag is detected, I just want to detect an NFC tag when the application runs.
Can you help me?
MainActivity
:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private val TAG = MainActivity::class.java.simpleName
var tag: Tag? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkNFC(this)
setupNFC(this)
configureToolbar()
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun configureToolbar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == ENABLE_NFC_REQUEST_CODE) {
onActivityResultOutSourced(this)
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
override fun onResume() {
super.onResume()
onResumeOutSourced(this)
}
override fun onPause() {
super.onPause()
onPauseOutSourced(this)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
val techList = tag!!.techList
//Check that the discovered tag is a vicinity tag
if (techList[0] == "android.nfc.tech.NfcV") {
val tagUid = tag!!.id
nfcvTag = NfcV.get(tag)
//ISO/IEC 15693 tags can be operated in two modes:
// Select mode and Addressed mode.
//To work in the select mode it is needed to send a SELECT
// command at the beginning of communic.
//In the address mode, the tag UID is sent within each command.
//This application works in SELECT MODE.
val select_command: ByteArray = RFCommands.cmd_select
System.arraycopy(tagUid, 0, select_command, 2, 8)
if (nfcvTag != null) {
try {
nfcvTag!!.connect()
val select_respo = nfcvTag!!.transceive(select_command)
Log.d(TAG, "Select response: " +
Tools.byteArrayToHex(select_respo))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
companion object {
const val ENABLE_NFC_REQUEST_CODE = 0x11
private var nfcvTag: NfcV? = null
}
}
NfcManager:
class NfcManager {
companion object {
private var mNfcAdapter: NfcAdapter? = null
private var mPendingIntent: PendingIntent? = null
private lateinit var writeTagFilters: Array<IntentFilter>
private lateinit var mTechLists: Array<Array<String>>
/**
* Check the availability of NFC interface and let the user enable them
* if not active during the activity creation
*/
fun checkNFC(@NonNull activity: Activity) {
if (activity.packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(activity.applicationContext)
if (mNfcAdapter != null && !mNfcAdapter!!.isEnabled) {
AlertDialog.Builder(activity.applicationContext)
.setTitle(activity.resources.getString(R.string.dialog_nfc_not_enabled_title))
.setMessage(activity.resources.getString(R.string.dialog_nfc_not_enabled_msg))
.setPositiveButton(activity.resources.getString(R.string.dialog_nfc_not_enabled_positive_btn)
) { _, _ -> activity.startActivityForResult(Intent(Settings.ACTION_NFC_SETTINGS), MainActivity.ENABLE_NFC_REQUEST_CODE) }
.setNegativeButton(activity.resources.getString(R.string.dialog_nfc_not_enabled_negative_btn)
) { _, _ ->
Toast.makeText(activity.applicationContext, activity.resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
activity.finish()
}.show()
}
} else {
Toast.makeText(activity.applicationContext, activity.resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
activity.finish()
}
}
/**
* Create a generic PendingIntent that will be delivered to this activity.
* The NFC stack will fill in the intent with the details of the discovered
* tag before delivering it to this activity.
*/
fun setupNFC(@NonNull activity: Activity) {
mPendingIntent = PendingIntent.getActivity(activity, 0, Intent(
activity, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
writeTagFilters = arrayOf(tagDetected)
mTechLists = arrayOf(arrayOf(
NfcV::class.java.name
))
}
fun onActivityResultOutSourced(@NonNull activity: Activity) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(activity)
if (mNfcAdapter!!.isEnabled) {
Toast.makeText(activity.applicationContext, activity.resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
activity.finish()
}
}
fun onResumeOutSourced(@NonNull activity: Activity) {
if (mNfcAdapter != null) {
mNfcAdapter!!.enableForegroundDispatch(activity, mPendingIntent, writeTagFilters, mTechLists)
}
}
fun onPauseOutSourced(@NonNull activity: Activity) {
if (mNfcAdapter != null) mNfcAdapter!!.disableForegroundDispatch(activity)
}
}
}
EDIT:
The objective of the "NfcManager" class was to encapsulate as much as possible the NFC functions, in order to avoid having them in the "MainActivity".
The problem is that the previous code doesn't work, while the following code, in which the logic is contained in the "MainActivity" works well. I really don't understand what the problem is.
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private val TAG: String = this::class.java.simpleName
private val ENABLE_NFC_REQUEST_CODE = 0x11
private var mNfcAdapter: NfcAdapter? = null
private var nfcvTag: NfcV? = null
var tag: Tag? = null
private var mPendingIntent: PendingIntent? = null
private lateinit var writeTagFilters: Array<IntentFilter>
private lateinit var mTechLists: Array<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkNFC()
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
setNfcIntent()
configureToolbar()
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun configureToolbar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
}
private fun setNfcIntent() {
// Create a generic PendingIntent that will be delivered to this activity. The NFC stack will fill
// in the intent with the details of the discovered tag before delivering it to this activity.
mPendingIntent = PendingIntent.getActivity(this, 0, Intent(
applicationContext, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
writeTagFilters = arrayOf(tagDetected)
mTechLists = arrayOf(arrayOf(
NfcV::class.java.name
))
}
override fun onActivityResult(requestCode: Int,
resultCode: Int, data: Intent?) {
if (requestCode == ENABLE_NFC_REQUEST_CODE) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (mNfcAdapter!!.isEnabled) {
Toast.makeText(applicationContext, resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
finish()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
/**
* Check the availability of NFC and BLE interfaces and let the user enable them
* if not active during the activity creation
*/
private fun checkNFC() {
if (packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (mNfcAdapter != null && !mNfcAdapter!!.isEnabled) {
AlertDialog.Builder(this)
.setTitle(resources.getString(R.string.dialog_nfc_not_enabled_title))
.setMessage(resources.getString(R.string.dialog_nfc_not_enabled_msg))
.setPositiveButton(resources.getString(R.string.dialog_nfc_not_enabled_positive_btn)
) { dialog, which -> startActivityForResult(Intent(Settings.ACTION_NFC_SETTINGS), ENABLE_NFC_REQUEST_CODE) }
.setNegativeButton(resources.getString(R.string.dialog_nfc_not_enabled_negative_btn)
) { dialog, which ->
Toast.makeText(applicationContext, resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
finish()
}.show()
}
} else {
Toast.makeText(applicationContext, resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
finish()
}
}
override fun onResume() {
super.onResume()
if (mNfcAdapter != null) {
mNfcAdapter!!.enableForegroundDispatch(this, mPendingIntent, writeTagFilters, mTechLists)
}
}
override fun onPause() {
super.onPause()
if (mNfcAdapter != null) mNfcAdapter!!.disableForegroundDispatch(this)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
val techList = tag!!.techList
//Check that the discovered tag is a vicinity tag
if (techList[0] == "android.nfc.tech.NfcV") {
val tagUid = tag!!.id
nfcvTag = NfcV.get(tag)
//ISO/IEC 15693 tags can be operated in two modes:
// Select mode and Addressed mode.
//To work in the select mode it is needed to send a SELECT
// command at the beginning of communic.
//In the address mode, the tag UID is sent within each command.
//This application works in SELECT MODE.
val select_command: ByteArray = RFCommands.cmd_select
System.arraycopy(tagUid, 0, select_command, 2, 8)
if (nfcvTag != null) {
try {
nfcvTag!!.connect()
val select_respo: ByteArray = nfcvTag!!.transceive(select_command)
Log.d(TAG, "Select response: " +
Tools.byteArrayToHex(select_respo))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
}