Подтвердить что ты не робот

Как заставить NFC работать на Android с помощью Qt 5.6

Я пытаюсь читать теги NFC на своем телефоне Android, используя модуль NFC Qt.

В соответствии с этой страницей Qt будет поддерживать NFC на Android начиная с версии 5.6. Эта версия еще не выпущена, поэтому я построил ее из источника, следуя инструкциям на этой странице и установил ее в создателе Qt.

Первый шаг - получить работу с тегом/картой, и я застрял там. Мое тестовое приложение создает экземпляр QNearFieldManager, проверяет, доступен ли NFC, и соединяет слоты с сигналами targetDetected и targetLost. Метод 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 необходимо изменить. Я пробовал разные вещи, но никто, кажется, не производит желаемого эффекта. Я могу получить только события targetDetected и 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>
4b9b3361

Ответ 1

Если вы используете тег NFC для определенного производителя, то то же самое должно присутствовать и в мобильном NFC, тогда только он будет правильно парно, поскольку NFC не поддерживает глобально. Напр. если NFC присутствует в устройстве Sony, будет максимально поддерживать его производство, и в большинстве случаев он не сможет подключиться к другим устройствам, таким как нексус. Поэтому попробуйте найти своего производителя и подключить его. Надеюсь, это поможет вам.

Ответ 2

Я не верю, что вы хотите, чтобы в вашем манифесте были те фильтры намерений. Добавляя эти файлы, она сообщает операционной системе, чтобы она запускала ваше приложение, когда обнаружен тег (именно поэтому он это делает). Похоже, вы правильно регистрируетесь в своем коде для событий NFC, поэтому, возможно, проблема заключается в марке NFC-чипа в вашем телефоне в сочетании с тегом, который вы используете для тестирования. Если ваш телефон оснащен чипом Broadcom NFC, и вы пытаетесь использовать тег NXP Mifare Classic, вы столкнетесь с проблемами. Использование дескриптора или тега NTAG может помочь.

Ответ 3

Я решил этот.

Причина в том, что в QtNfc.java, где qt обрабатывает NFC, он обрабатывает только NDEF путем фильтрации действий ACTION_NDEF_DISCOVERED (и ACTION_TECH_DISCOVERED для тегов NDEF, которые будут сообщаться как технические) без простого ACTION_TAG_DISCOVERED (несмотря на то, что он обрабатывает его в getStartIntent fuction).

Но я предположил, что вы просто хотите отсканировать простой тег, чтобы читать uid, как и я. Поэтому вам необходимо добавить ACTION_TAG_DISCOVERED для списка фильтров в функции 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);
...

Я думаю, было бы правильнее модифицировать фильтр в ACTION_TAG_DISCOVERED в setContext. Самый быстрый способ - открыть в qt creator qtconnectivity.pro для соответствующей ветки, исправить QtNfc.java, построить его и заменить libQt5Nfc.so в папке android_armv7\lib qt (QtNfc.jar и QtNfc-bundled.jar в папке android_armv7\jar будут обновляться во время сборки).

То есть. Нет необходимости изменять манифест в рабочем приложении.

Кстати, этот:

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

qt автоматически добавляется при добавлении модуля nfc в .pro

Этот

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

не нужно. Он работает без него.

Но вы можете добавить этот фильтр намерений, если хотите сообщить андроиду, чтобы начать свое приложение, когда тег обнаружен как Anansi, упомянутый выше. Но я действительно рекомендую добавить   android: alwaysRetainTaskState = "true" android: launchMode = "singleInstance" в приложении (например, здесь).

Я тестирую все это с помощью примера Android android 4.4.4 и ndefeditor. Он отлично справляется с targetDetected/targetLost. В системе может быть другое приложение по умолчанию для тегов (например NFC Reader), и он открывается на каждом обнаружении тегов, но не на время ndefeditor является ожидающим тегом (кнопка retrieve). И, конечно, пример qt говорит "Ошибка чтения NDEF" для тегов, отличных от NDEF, но обнаруживает их и читает uid. Именно то, что мне нужно.

Я добавляю предложение в Qt Jira и отправлю патч .

Единственное, что я не понял - почему ndefeditor работал над другим планшетом с Android 4.0. Может быть, это аппаратный аспект, и андроид на другом планшете всегда был нацелен на ACTION_NDEF_DISCOVERED?

Ответ 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() } };

Записать это onNewIntent вне 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());
            }

        }
    }

Вы получите данные NFC в переменной strTagData​​p >

Разрешение в манифесте