I'm trying to read NFC tags on my Android phone using the NFC module of Qt.
According to this page, Qt will support NFC on Android starting from version 5.6. This version hasn't been released yet, so I built it from source, following the instructions on this page, and installed it in Qt creator.
First step is to get the tag/card detection working and I'm stuck there. My test application instantiates a QNearFieldManager
, checks if NFC is available and connects slots to the signals targetDetected
and targetLost
.
The QNearFieldManager::isAvailable
method reports that NFC is available (with Qt 5.5 it did not), but the signals targetDetected
/targetLost
are never fired.
Below is the code of my test application:
#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";
}
I must be missing something...
UPDATE 1
It appears that the AndroidManifest.xml file needs to be modified. I tried different things, but none seem to produce the desired effect. I can only get the targetDetected
and targetLost
events to fire when the manifest defines an intent-filter like this:
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
However, this also causes the app to be started each time a target is scanned, even if the app is already running. What I need is to start the app and then wait for a target to be scanned. How can I accomplish this?
UPDATE 2
Below is the full AndroidManifest.xml file that I tried.
<?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>