Cómo hacer que NFC funcione en Android usando Qt 5.6
c++ qt5.6 (4)
Estoy tratando de leer etiquetas NFC en mi teléfono Android usando el módulo NFC de Qt.
Según esta page , Qt admitirá NFC en Android a partir de la versión 5.6. Esta versión aún no se ha lanzado, así que la construí desde el código fuente, siguiendo las instrucciones de esta page , y la instalé en Qt Creator.
El primer paso es hacer que funcione la detección de etiqueta / tarjeta y estoy atascado allí.
Mi aplicación de prueba
QNearFieldManager
una instancia de
QNearFieldManager
, comprueba si NFC está disponible y conecta las ranuras a las señales
targetDetected
y
targetLost
.
El método
QNearFieldManager::isAvailable
informa que NFC está disponible (con Qt 5.5 no estaba disponible), pero las señales
targetDetected
/
targetLost
nunca se
targetLost
.
A continuación se muestra el código de mi aplicación de prueba:
#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";
}
Debo estar perdiendo algo...
ACTUALIZACIÓN 1
Parece que el archivo AndroidManifest.xml necesita ser modificado.
Intenté cosas diferentes, pero ninguna parece producir el efecto deseado.
Solo puedo hacer que se
targetLost
eventos
targetDetected
y
targetLost
cuando el manifiesto define un filtro de intención como este:
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
Sin embargo, esto también hace que la aplicación se inicie cada vez que se analiza un objetivo, incluso si la aplicación ya se está ejecutando. Lo que necesito es iniciar la aplicación y luego esperar a que se escanee un objetivo. ¿Cómo puedo lograr esto?
ACTUALIZACIÓN 2
A continuación se muestra el archivo completo de AndroidManifest.xml que probé.
<?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>
He resuelto este.
La razón es que en QtNfc.java donde qt maneja las intenciones de NFC, maneja solo NDEF etiquetas NDEF al filtrar las acciones ACTION_NDEF_DISCOVERED ( y ACTION_TECH_DISCOVERED para las etiquetas NDEF que informarán como tecnología ) sin ACTION_TAG_DISCOVERED simple (a pesar de que lo maneja en getStartIntent fu ).
Pero supongo que solo quieres escanear una etiqueta simple para leer uid, como yo. Por lo tanto, debe agregar ACTION_TAG_DISCOVERED para filtrar la lista en la función QtNfc.java start ():
IntentFilter[] filters = new IntentFilter[3];
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_TAG_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
...
Creo que sería más correcto modificar el filtro a ACTION_TAG_DISCOVERED en setContext también. La forma más rápida es abrir en qt creator qtconnectivity .pro para la rama correspondiente, corregir QtNfc.java, compilarlo y reemplazar libQt5Nfc.so en la carpeta android_armv7 / lib qt (QtNfc.jar y QtNfc-bundled.jar en la carpeta android_armv7 / jar ser actualizado durante la compilación).
Es decir. No es necesario modificar el manifiesto en la aplicación de trabajo.
Por cierto este:
<uses-permission android:name="android.permission.NFC"/>
qt se agrega automáticamente cuando agrega el módulo nfc a .pro
Éste
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
No es necesario, supongo. Funciona sin eso.
Pero puede agregar este filtro de intención si desea decirle a Android que inicie su aplicación cuando se detecta una etiqueta como Anansi mencionó en la parte superior. Pero realmente recomiendo agregar android: alwaysRetainTaskState = "true" android: launchMode = "singleInstance" en la actividad de la aplicación (como here ).
Pruebo todo esto con la tableta Android 4.4.4 y el ejemplo ndefeditor. Dispara targetDetected / targetLost perfectamente. Puede haber otra aplicación predeterminada para etiquetas en el sistema (por ejemplo, NFC Reader ) y se abre en cada detección de etiquetas, pero no en el momento en que ndefeditor está esperando la etiqueta (botón de recuperación). Y, por supuesto, el ejemplo qt dice "Error de lectura NDEF" para etiquetas que no son NDEF, pero las detecta y lee uid. Precisamente lo que necesitaba.
Agrego la sugerencia a Qt Jira y envío el patch .
Lo único que no entendí: por qué ndefeditor había trabajado en otra tableta con Android 4.2. ¿Quizás es un aspecto de hardware y Android en otra tableta siempre tuvo la intención ACTION_NDEF_DISCOVERED?
Hola a continuación es la respuesta, avíseme si solo está buscando esto. :) Primero escribe esto en 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() } };
Escriba esto en NewIntent fuera de onCreate ()
@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());
}
}
}
Obtendrá datos NFC en la variable strTagData
Permiso en manifiesto
No creo que quieras esos filtros de intención en tu manifiesto. Al agregarlos, le dice al sistema operativo que inicie su aplicación cuando se detecta una etiqueta (es por eso que lo está haciendo). Parece que se está registrando correctamente en su código para eventos NFC, por lo que tal vez el problema sea la marca del chip NFC en su teléfono, junto con la etiqueta que está utilizando para probar. Si su teléfono está equipado con un chip Broadcom NFC, y está tratando de usar la etiqueta Mifare Classic de NXP, se encontrará con problemas. Usar una etiqueta Desfire o NTAG podría ayudar.
Si está utilizando la etiqueta NFC de cierto fabricante, el mismo debería estar presente en el NFC móvil también, entonces solo se emparejará correctamente a partir de ahora, NFC no es compatible a nivel mundial. Por ej. si el NFC presente dentro del dispositivo Sony solo admitirá su fabricación y, en la mayoría de los casos, no se conecta a otros dispositivos como nexus. Intenta encontrar tu fabricante y conéctalo. Espero que te ayude ...