your programing

Qt 5.6을 사용하여 Android에서 NFC를 작동시키는 방법

lovepro 2020. 12. 31. 23:08
반응형

Qt 5.6을 사용하여 Android에서 NFC를 작동시키는 방법


Qt의 NFC 모듈을 사용하여 내 Android 휴대폰에서 NFC 태그를 읽으려고합니다.

페이지 에 따르면 Qt는 버전 5.6부터 Android에서 NFC를 지원합니다. 이 버전은 아직 출시되지 않았기 때문에이 페이지 의 지침에 따라 소스에서 빌드하고 Qt 작성자에 설치했습니다.

첫 번째 단계는 태그 / 카드 감지가 작동하도록하는 것입니다. 내 테스트 응용 프로그램은를 인스턴스화하고 QNearFieldManagerNFC를 사용할 수 있는지 확인하고 슬롯을 신호 targetDetectedtargetLost. QNearFieldManager::isAvailableNFC는 (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>

특정 제조업체의 NFC 태그를 사용하는 경우 모바일 NFC에도 동일한 태그가 있어야하며 현재 NFC는 전역 적으로 지원하지 않는 현재 만 올바르게 페어링됩니다. 예를 들어. Sony 장치 내부의 NFC Present가 최대 제조업체 만 지원하고 대부분의 경우 nexus와 같은 다른 장치에 연결하지 못하는 경우. 따라서 제조업체를 찾아 연결해보십시오. 도움이 되었기를 바랍니다 ..


매니페스트에서 이러한 인 텐트 필터를 원하지 않는다고 생각합니다. 이를 추가하면 태그가 감지 될 때 앱을 시작하도록 운영 체제에 지시합니다 (이것이 그렇게하는 이유입니다). NFC 이벤트에 대한 코드에 올바르게 등록한 것처럼 보이므로 테스트에 사용하는 태그와 함께 휴대 전화의 NFC 칩 브랜드가 문제 일 수 있습니다. 휴대 전화에 Broadcom NFC 칩이 장착되어 있고 NXP의 Mifare Classic 태그를 사용하려고하면 문제가 발생합니다. Desfire 또는 NTAG 태그를 사용하면 도움이 될 수 있습니다.


이 문제를 해결했습니다.

The reason is that in QtNfc.java where qt handles NFC intents it handles only NDEF tags by filtering ACTION_NDEF_DISCOVERED actions (and ACTION_TECH_DISCOVERED for NDEF tags that will report as tech) without simple ACTION_TAG_DISCOVERED (despite the fact it handles it in getStartIntent fuction).

But I supposed you just want to scan a simple tag to read uid, as I do. So you need to add ACTION_TAG_DISCOVERED to filter list in QtNfc.java start() function:

IntentFilter[] filters = new IntentFilter[3];
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_TAG_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
...

I think it would be more correct to modify filter to ACTION_TAG_DISCOVERED in setContext too. The fastest way is to open in qt creator qtconnectivity .pro for corresponding branch, correct QtNfc.java, build it and replace libQt5Nfc.so in android_armv7\lib qt folder (QtNfc.jar and QtNfc-bundled.jar in android_armv7\jar folder will be updated during build).

That is. No need to modify manifest in working application.

By the way this one:

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

qt add automatically when you add module nfc to .pro

This one

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

is not necessary I suppose. It works without it.

But you can add this intent-filter if you want to tell android to start your app when a tag is detected as Anansi mentioned upper. But I really recommend to add android:alwaysRetainTaskState="true" android:launchMode="singleInstance" in application activity (like here).

I test all this with android 4.4.4 tablet and ndefeditor example. It fires targetDetected/targetLost perfectly. There can be another default application for tags in system (for example NFC Reader) and it opens up on every tag detecting, but not the time ndefeditor is waiting tag (button retrieve). And of course qt example says "NDEF read error" for non-NDEF tags, but it detects them and reads uid. Precisely what I needed.

I add the suggestion to Qt Jira and submit the patch.

The only thing I didn't understand - why ndefeditor had worked on another tablet with android 4.2. Maybe it is a hardware aspect and android on another tablet was always intent ACTION_NDEF_DISCOVERED?


Hello below is the answer, let me know if you are looking for this only. :) Firstly write this in 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() } };

Write this onNewIntent outside 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());
            }

        }
    }

You will get NFC data in strTagData variable

Permission in Manifest

ReferenceURL : https://stackoverflow.com/questions/33346378/how-to-get-nfc-working-on-android-using-qt-5-6

반응형