【问题标题】:How to get NFC working on Android using Qt 5.6如何使用 Qt 5.6 让 NFC 在 Android 上运行
【发布时间】:2016-01-25 14:08:56
【问题描述】:

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

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

第一步是让标签/卡片检测工作,我被困在那里。我的测试应用程序实例化了一个QNearFieldManager,检查NFC 是否可用并将插槽连接到信号targetDetectedtargetLostQNearFieldManager::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 文件似乎需要修改。我尝试了不同的方法,但似乎都没有产生预期的效果。当清单定义这样的意图过滤器时,我只能触发 targetDetectedtargetLost 事件:

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

【问题讨论】:

  • 您是否检查了android docs 以确保您设置了所有正确的权限?例如:&lt;uses-permission android:name="android.permission.NFC" /&gt;
  • 是的,权限设置正确。就像我上面描述的那样,目标的存在确实会触发 targetDetected 信号,我认为如果权限设置不正确,就不会发生这种情况。现在的问题是它还启动了该应用程序的另一个实例,这使其崩溃。我稍后会发布我的完整 AndroidManifest.xml 文件...
  • logcat 是否包含有趣的内容?
  • 软木板示例也适合您吗?
  • @FrankMeerkötter:logcat 是什么意思?黑板不起作用,但我认为我需要一个包含 Ndef 数据的标签。但我需要在较低级别访问我的标签(Mifare),即直接发送 APDU。

标签: android c++ qt nfc qt5.6


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我不认为硬件/固件是问题,因为我在 Playstore 上找到了一个使用相同标签可以正常工作的应用程序。遗憾的是我不记得应用程序的名称,手机前段时间被砸坏了,我切换到那个水果公司......
    【解决方案3】:

    我已经解决了这个问题。

    原因是在QtNfc.java 中,qt 处理 NFC 意图,它通过过滤 ACTION_NDEF_DISCOVERED 操作来处理 NDEF 标签(和 ACTION_TECH_DISCOVERED 用于将报告为技术的 NDEF 标签 em>) 没有简单的 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 creatorqtconnectivity.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" 在应用程序活动中(如here)。

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

    我添加suggestion to Qt Jira 并提交patch

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

    【讨论】:

    • 我的补丁被合并到 qt 5.9.0 Beta 1
    【解决方案4】:

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

    清单中的权限

    【讨论】:

    • 在我看来是实时的 java 代码,但我需要让它在 Qt (C++) 中工作
    猜你喜欢
    • 1970-01-01
    • 2015-05-12
    • 1970-01-01
    • 2021-12-22
    • 2012-05-19
    • 2016-02-11
    • 2016-02-29
    • 1970-01-01
    • 2013-01-27
    相关资源
    最近更新 更多