如何使用Qt 5.6在Android上运行NFC

我试图使用Qt的NFC模块在我的Android手机上阅读NFC标签。

根据这个页面 ,Qt将从5.6版本开始支持Android。 这个版本还没有发布,所以我从源码开始构build,遵循这个页面上的说明,并安装在Qt创build器中。

第一步是让标签/卡检测工作,我卡在那里。 我的testing应用程序实例化一个QNearFieldManager ,检查NFC是否可用,并将插槽连接到信号targetLosttargetLostQNearFieldManager::isAvailable方法报告NFC是可用的(Qt 5.5没有),但是targetLost / targetLost信号永远不会被触发。

以下是我的testing应用程序的代码:

 #include <QLabel> #include <QVBoxLayout> #include <QNearFieldManager> #include <QNearFieldTarget> #include <QDebug> #include "window.h" Window::Window(QWidget *parent) : QWidget(parent) { nfcLabel_ = new QLabel(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(nfcLabel_, 1); setLayout(mainLayout); setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); setWindowTitle(tr("NFC Test")); nfc_ = new QNearFieldManager(this); if (nfc_->isAvailable()) { nfcLabel_->setText("NFC available"); } else { nfcLabel_->setText("NFC not available"); qWarning() << "NFC not available"; } nfc_->setTargetAccessModes(QNearFieldManager::NdefReadTargetAccess); // doesn't help nfc_->registerNdefMessageHandler(this, SLOT(handleNdefMessage(QNdefMessage,QNearFieldTarget*))); // doesn't help connect(nfc_, SIGNAL(targetDetected(QNearFieldTarget*)), this, SLOT(targetDetected(QNearFieldTarget*))); connect(nfc_, SIGNAL(targetLost(QNearFieldTarget*)), this, SLOT(targetLost(QNearFieldTarget*))); if (!nfc_->startTargetDetection()) { qWarning() << "NFC target detection could not be started"; } } Window::~Window() { nfc_->stopTargetDetection(); } void Window::targetDetected(QNearFieldTarget * /*target*/) { nfcLabel_->setText("Target detected"); } void Window::targetLost(QNearFieldTarget *target) { nfcLabel_->setText("Target lost"); target->deleteLater(); } void Window::handleNdefMessage(const QNdefMessage &/*message*/, QNearFieldTarget */*target*/) { qDebug() << "Ndef Message"; } 

我肯定错过了什么…

更新1

看起来AndroidManifest.xml文件需要修改。 我尝试了不同的东西,但似乎没有产生预期的效果。 我只能得到targetLosttargetLost事件,当清单定义一个intent-filter这样的filter时:

 <intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> 

但是,这也会导致应用程序在每次扫描目标时启动,即使应用程序已经在运行。 我需要的是启动应用程序,然后等待目标被扫描。 我怎样才能做到这一点?

更新2

下面是我试过的完整的AndroidManifest.xml文件。

 <?xml version="1.0"?> <manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto"> <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:theme="@android:style/Theme.Holo"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <!-- Without this, the targetDetected/targetLost signals aren't fired --> <intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.repository" android:value="default"/> <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/> <!-- Deploy Qt libs as part of package --> <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> <!-- Run with local libs --> <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/> <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> <!-- Messages maps --> <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/> <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/> <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/> <!-- Messages maps --> <!-- Splash screen --> <!-- <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/> --> <!-- Splash screen --> <!-- Background running --> <!-- Warning: changing this value to true may cause unexpected crashes if the application still try to draw after "applicationStateChanged(Qt::ApplicationSuspended)" signal is sent! --> <meta-data android:name="android.app.background_running" android:value="false"/> <!-- Background running --> </activity> </application> <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="14"/> <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> <uses-feature android:name="android.hardware.nfc" android:required="true"/> <uses-permission android:name="android.permission.NFC"/> </manifest> 

如果您使用的是某个制造商的NFC标签,则移动NFC中也应该存在相同的NFC标签,那么现在NFC将不会全球支持。 例如。 如果Sony设备内部的NFC存在将仅支持其制造,并且在大多数情况下不能连接到其他设备,如连接点。 所以试着find你的制造商并连接它。 希望它可以帮助你..

我不相信你需要清单中的那些意图filter。 添加这些,告诉操作系统在检测到标签时启动您的应用程序(这就是为什么它是这样做的)。 这看起来好像是在代码中正确注册NFC事件,所以也许问题在于手机中的NFC芯片的品牌,以及您正在使用的testing标签。 如果您的手机配备了Broadcom NFC芯片,而您尝试使用恩智浦的Mifare Classic标签,则会遇到问题。 使用Desfire或NTAG标签可能会有所帮助。

我已经解决了这个问题。

原因在于,在QtNfc.java中 ,qt处理NFC意图的时候,通过过滤ACTION_NDEF_DISCOVERED动作( 和ACTION_TECH_DISCOVERED作为NDEF标签,将报告为技术 ), 而不用简单的ACTION_TAG_DISCOVERED( 尽pipe它在getStartIntentfunction中处理它),它只处理 NDEF标签

但是我想你只是想扫描一个简单的标签来阅读uid,就像我一样。 所以你需要在QtNfc.java start()函数中添加ACTION_TAG_DISCOVERED来过滤列表:

 IntentFilter[] filters = new IntentFilter[3]; filters[0] = new IntentFilter(); filters[0].addAction(NfcAdapter.ACTION_TAG_DISCOVERED); filters[0].addCategory(Intent.CATEGORY_DEFAULT); ... 

我认为在setContext中将filter修改为ACTION_TAG_DISCOVERED会更正确。 最快的方法是打开qt creator qtconnectivity .pro为相应的分支,正确的QtNfc.java,build立它,并replaceandroid_armv7 \ lib中的libQt5Nfc.so qt文件夹(QtNfc.jar和QtNfc-bundled.jar在android_armv7 \ jar文件夹将在构build期间更新)。

那是。 无需在工作应用程序中修改清单。

顺便说一下这个:

 <uses-permission android:name="android.permission.NFC"/> 

qt添加到.pro模块nfc时自动添加

这个

 <uses-feature android:name="android.hardware.nfc" android:required="true"/> 

没有必要我想。 它没有它的工作。

但是如果你想告诉android在Anansi提到的标签被检测到的时候启动你的应用,你可以添加这个intent-filter。 但我真的推荐在应用程序活动(如这里 )中添加android:alwaysRetainTaskState =“true”android:launchMode =“singleInstance”。

我用android 4.4.4 tablet和ndefeditor的例子testing了这一切。 它发射targetDetected / targetLost完美。 系统中可以有另一个默认的应用程序(例如NFC阅读器 ),并在每个标签检测时打开,但不是ndefeditor正在等待标签(button检索)的时间。 当然对于非NDEF标签,qt示例显示为“NDEF读取错误”,但它检测到它们并读取uid。 正是我所需要的。

我将这个build议添加到Qt Jira并提交补丁 。

唯一我不明白的 – 为什么ndefeditor曾经在android 4.2的平板电脑上工作过。 也许这是一个硬件方面和另一台平板电脑上的android总是intent ACTION_NDEF_DISCOVERED?

你好,下面是答案,让我知道,如果你只是在寻找这个。 :)首先写在onCreate()

 //Code in onCreate mNfcAdapter = NfcAdapter.getDefaultAdapter(this); mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); // set an intent filter for all MIME data IntentFilter ndefIntent = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); try { ndefIntent.addDataType("*/*"); mIntentFilters = new IntentFilter[] { ndefIntent }; } catch (Exception e) { Log.fnLogToFile(strFunctionName + "-" + e.getMessage(), ErrorType.ERROR); Log.createCrashReport(); } mNFCTechLists = new String[][] { new String[] { NfcF.class.getName() } }; 

在onCreate()外面写这个onNewIntent

 @Override public void onNewIntent(Intent intent) { StackTraceElement[] arrFunctionName = Thread.currentThread().getStackTrace() ; String strFunctionName = arrFunctionName[arrFunctionName.length-1].getMethodName(); Log.fnLogToFile(strFunctionName + "Entered", ErrorType.INFO); tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); String strTagData = ""; // parse through all NDEF messages and their records and pick text type only Parcelable[] data = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (data != null) { try { for (int i = 0; i < data.length; i++) { NdefRecord [] recs = ((NdefMessage)data[i]).getRecords(); for (int j = 0; j < recs.length; j++) { if (recs[j].getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(recs[j].getType(), NdefRecord.RTD_TEXT)) { byte[] payload = recs[j].getPayload(); String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16"; int langCodeLen = payload[0] & 0077; //tag data is saved in strTagData strTagData += ("\n" + new String(payload, langCodeLen + 1, payload.length - langCodeLen - 1, textEncoding)); } } } } catch (Exception e) { Log.fnLogToFile(strFunctionName + "-" + e.getMessage(), ErrorType.ERROR); Log.createCrashReport(); Log.e("TagDispatch", e.toString()); } } } 

您将在strTagDatavariables中获得NFC数据

清单中的许可