56

我正在尝试使用 Qt 的 NFC 模块读取我的 Android 手机上的 NFC 标签。

根据此页面,Qt 将从 5.6 版开始在 Android 上支持 NFC。这个版本还没有发布,所以我按照这个页面上的说明从源代码构建了它,并将它安装在 Qt creator 中。

第一步是让标签/卡检测工作,我被困在那里。我的测试应用程序实例化一个QNearFieldManager,检查 NFC 是否可用并将插槽连接到信号targetDetectedtargetLost. 该QNearFieldManager::isAvailable方法报告 NFC 可用(Qt 5.5 不可用),但信号targetDetected/targetLost从未被触发。

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

#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 文件。我尝试了不同的方法,但似乎都没有产生预期的效果。我只能在清单定义这样的意图过滤器时触发targetDetectedand事件:targetLost

<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>
4

4 回答 4

1

我已经解决了这个问题。

原因是在QtNfc.java中,qt 处理 NFC 意图,它通过过滤 ACTION_NDEF_DISCOVERED 动作(以及将报告为技术的 NDEF 标签的 ACTION_TECH_DISCOVERED只处理 NDEF标签,而没有简单的 ACTION_TAG_DISCOVERED (尽管它在 getStartIntent 函数中处理它)。

但我想你只想扫描一个简单的标签来读取 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 中也将过滤器修改为 ACTION_TAG_DISCOVERED 会更正确。最快的方法是在 qt creator qtconnectivity .pro 中打开相应的分支,更正 QtNfc.java,构建它并替换 android_armv7\lib qt 文件夹中的 libQt5Nfc.so(android_armv7\jar 文件夹中的 QtNfc.jar 和 QtNfc-bundled.jar 将在构建期间更新)。

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

顺便说一句:

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

当您将模块 nfc 添加到 .pro 时,qt 会自动添加

这个

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

我想没有必要。没有它也可以工作。

但是,如果您想告诉 android 在检测到标签时启动您的应用程序,则可以添加此意图过滤器,如 Anansi 上面提到的。但我真的建议在应用程序活动中添加 android:alwaysRetainTaskState="true" android:launchMode="singleInstance" (如这里)。

我使用 android 4.4.4 平板电脑和 ndefeditor 示例测试所有这些。它完美地触发了 targetDetected/targetLost。系统中可能有另一个默认的标签应用程序(例如NFC 阅读器),它会在每个标签检测时打开,但不是 ndefeditor 等待标签的时间(按钮检索)。当然,qt 示例对非 NDEF 标记说“NDEF 读取错误”,但它会检测到它们并读取 uid。正是我需要的。

我将建议添加到 Qt Jira并提交补丁

我唯一不明白的是——为什么 ndefeditor 在另一台装有 android 4.2 的平板电脑上工作。也许这是一个硬件方面,另一台平板电脑上的 android 总是意图 ACTION_NDEF_DISCOVERED?

于 2017-03-13T19:00:57.803 回答
1

如果您使用的是某个制造商的 NFC 标签,那么移动 NFC 中也应该存在相同的标签,那么只有它才能正确配对,因为目前 NFC 不支持全球。例如。如果索尼设备内的 NFC 存在将最大程度地支持其制造,并且在大多数情况下它无法连接到其他设备,例如 nexus。所以试着找到你的制造商并连接它。希望对你有帮助。。

于 2016-11-08T05:02:47.687 回答
1

我不相信你想要清单中的那些意图过滤器。添加这些,告诉操作系统在检测到标签时启动您的应用程序(这就是它这样做的原因)。看起来您在代码中正确注册了 NFC 事件,因此问题可能出在手机中 NFC 芯片的品牌以及您用于测试的标签上。如果您的手机配备了 Broadcom NFC 芯片,并且您尝试使用 NXP 的 Mifare Classic 标签,您会遇到问题。使用 Desfire 或 NTAG 标签可能会有所帮助。

于 2016-12-07T23:57:04.443 回答
-3

您好,下面是答案,如果您只是在寻找这个,请告诉我。:) 首先在 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());
            }

        }
    }

您将在 strTagData 变量中获得 NFC 数据

清单中的权限

于 2016-01-20T07:09:56.403 回答