我目前正在开发一个 Android NFC 应用程序。这个应用程序包含一个NavigationDrawer
我可以访问 3 个不同的片段,每个片段对应于 3 个不同的 NFC 功能。
首先,我想验证我的应用程序是否可以扫描 NFC 标签(这里我有一个 NXP NTAG 5 boost 标签,它是一个 NFC Forum Type 5 标签)。
我的问题是,当我的应用程序运行时,onNewIntent
myMainActivity
永远不会被调用。
我确定这不是由于硬件,因为我可以使用常见应用程序检测到这个标签,但这可能是由于我传递给NfcManager
文件中的函数的参数,比如activity.applicationContext
但我不确定。
现在,我指定我不希望在检测到标签时启动特定活动,我只想在应用程序运行时检测 NFC 标签。
你能帮助我吗?
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)
}
}
}
编辑:
“NfcManager”类的目标是尽可能多地封装 NFC 功能,以避免将它们放在“MainActivity”中。
问题是前面的代码不起作用,而下面的代码,其中包含在“MainActivity”中的逻辑运行良好。我真的不明白问题是什么。
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()
}
}
}
}
}