diff options
author | Alex Blasche <alexander.blasche@theqtcompany.com> | 2015-06-09 09:17:51 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@theqtcompany.com> | 2015-06-09 09:17:51 +0200 |
commit | 782c57a29fa03d795d96854daf51b8b9e523c933 (patch) | |
tree | 358520baf63a4bd8e7be441269c2fa4ead44dc65 /src | |
parent | 720118ea73431a0808f0c60dfec10f16fd85af7f (diff) | |
parent | 3f3d1289cc2f30b8fbc0837bf8fb7c767492e4e2 (diff) |
Merge QtNfc on Android into dev/Qt 5.6
Change-Id: I18f2d2d75064dffc4f21c2a7034bb63fcc5008e6
Diffstat (limited to 'src')
27 files changed, 2362 insertions, 32 deletions
diff --git a/src/android/android.pro b/src/android/android.pro index 07feb78f..f8f5c05e 100644 --- a/src/android/android.pro +++ b/src/android/android.pro @@ -1,2 +1,3 @@ TEMPLATE = subdirs qtHaveModule(bluetooth): SUBDIRS += bluetooth +qtHaveModule(nfc): SUBDIRS += nfc diff --git a/src/android/nfc/AndroidManifest.xml b/src/android/nfc/AndroidManifest.xml new file mode 100644 index 00000000..2ba062ae --- /dev/null +++ b/src/android/nfc/AndroidManifest.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.qtproject.qt5.android.nfc" + android:versionCode="1" + android:versionName="1.0"> + <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> +</manifest> diff --git a/src/android/nfc/bundledjar.pro b/src/android/nfc/bundledjar.pro new file mode 100644 index 00000000..37f7fc82 --- /dev/null +++ b/src/android/nfc/bundledjar.pro @@ -0,0 +1,3 @@ +TARGET = QtNfc-bundled +CONFIG += bundled_jar_file +include(nfc.pri) diff --git a/src/android/nfc/distributedjar.pro b/src/android/nfc/distributedjar.pro new file mode 100644 index 00000000..ed65c50a --- /dev/null +++ b/src/android/nfc/distributedjar.pro @@ -0,0 +1,2 @@ +TARGET = QtNfc +include(nfc.pri) diff --git a/src/android/nfc/nfc.pri b/src/android/nfc/nfc.pri new file mode 100644 index 00000000..65f197ca --- /dev/null +++ b/src/android/nfc/nfc.pri @@ -0,0 +1,13 @@ +CONFIG += java +DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar +API_VERSION = android-10 + +PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/nfc + +JAVACLASSPATH += $$PWD/src/ +JAVASOURCES += \ + $$PWD/src/org/qtproject/qt5/android/nfc/QtNfc.java + +# install +target.path = $$[QT_INSTALL_PREFIX]/jar +INSTALLS += target diff --git a/src/android/nfc/nfc.pro b/src/android/nfc/nfc.pro new file mode 100644 index 00000000..70373fe1 --- /dev/null +++ b/src/android/nfc/nfc.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS += bundledjar.pro distributedjar.pro + diff --git a/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java new file mode 100644 index 00000000..566489c8 --- /dev/null +++ b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android.nfc; + +import java.lang.Thread; +import java.lang.Runnable; + +import android.os.Parcelable; +import android.os.Looper; +import android.content.Context; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.content.IntentFilter.MalformedMimeTypeException; +import android.os.Bundle; +import android.util.Log; +import android.content.BroadcastReceiver; + +public class QtNfc +{ + /* static final QtNfc m_nfc = new QtNfc(); */ + static private final String TAG = "QtNfc"; + static public NfcAdapter m_adapter = null; + static public PendingIntent m_pendingIntent = null; + static public IntentFilter[] m_filters; + static public Activity m_activity; + + static public void setActivity(Activity activity, Object activityDelegate) + { + //Log.d(TAG, "setActivity " + activity); + m_activity = activity; + m_adapter = NfcAdapter.getDefaultAdapter(m_activity); + if (m_adapter == null) { + //Log.e(TAG, "No NFC available"); + return; + } + m_pendingIntent = PendingIntent.getActivity( + m_activity, + 0, + new Intent(m_activity, m_activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), + 0); + + //Log.d(TAG, "Pending intent:" + m_pendingIntent); + + IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); + + m_filters = new IntentFilter[]{ + filter + }; + + try { + filter.addDataType("*/*"); + } catch(MalformedMimeTypeException e) { + throw new RuntimeException("Fail", e); + } + + //Log.d(TAG, "Thread:" + Thread.currentThread().getId()); + } + + static public boolean start() + { + if (m_adapter == null) return false; + m_activity.runOnUiThread(new Runnable() { + public void run() { + //Log.d(TAG, "Enabling NFC"); + IntentFilter[] filters = new IntentFilter[2]; + filters[0] = new IntentFilter(); + filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED); + filters[0].addCategory(Intent.CATEGORY_DEFAULT); + try { + filters[0].addDataType("*/*"); + } catch (MalformedMimeTypeException e) { + throw new RuntimeException("Check your mime type."); + } + // some tags will report as tech, even if they are ndef formated/formatable. + filters[1] = new IntentFilter(); + filters[1].addAction(NfcAdapter.ACTION_TECH_DISCOVERED); + String[][] techList = new String[][]{ + {"android.nfc.tech.Ndef"}, + {"android.nfc.tech.NdefFormatable"} + }; + try { + m_adapter.enableForegroundDispatch(m_activity, m_pendingIntent, filters, techList); + } catch(IllegalStateException e) { + // On Android we must call enableForegroundDispatch when the activity is in foreground, only. + Log.d(TAG, "enableForegroundDispatch failed: " + e.toString()); + } + } + }); + return true; + } + + static public boolean stop() + { + if (m_adapter == null) return false; + m_activity.runOnUiThread(new Runnable() { + public void run() { + //Log.d(TAG, "Disabling NFC"); + try { + m_adapter.disableForegroundDispatch(m_activity); + } catch(IllegalStateException e) { + // On Android we must call disableForegroundDispatch when the activity is in foreground, only. + Log.d(TAG, "disableForegroundDispatch failed: " + e.toString()); + } + } + }); + return true; + } + + static public boolean isAvailable() + { + m_adapter = NfcAdapter.getDefaultAdapter(m_activity); + if (m_adapter == null) { + //Log.e(TAG, "No NFC available (Adapter is null)"); + return false; + } + return m_adapter.isEnabled(); + } + + static public Intent getStartIntent() + { + Log.d(TAG, "getStartIntent"); + if (m_activity == null) return null; + Intent intent = m_activity.getIntent(); + if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) || + NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || + NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { + return intent; + } else { + return null; + } + } +} diff --git a/src/imports/nfc/qdeclarativendeffilter.cpp b/src/imports/nfc/qdeclarativendeffilter.cpp index a3fb1581..508b9d2d 100644 --- a/src/imports/nfc/qdeclarativendeffilter.cpp +++ b/src/imports/nfc/qdeclarativendeffilter.cpp @@ -70,12 +70,18 @@ */ /*! + \qmlproperty QQmlNdefRecord::TypeNameFormat NdefFilter::typeNameFormat + + This property holds the NDEF record name format type \enum QQmlNdefRecord::TypeNameFormat. +*/ + +/*! \qmlproperty int NdefFilter::minimum This property holds the minimum number of records of the given type that must be in the NDEF message for it match. - To match any number of records set both the minimum and maximum properties to -1. + The default minimum is 1. \sa maximum */ @@ -86,13 +92,13 @@ This property holds the maximum number of records of the given type that must be in the NDEF message for it match. - To match any number of records set both the minimum and maximum properties to -1. + The default maximum is UINT_MAX. \sa minimum */ QDeclarativeNdefFilter::QDeclarativeNdefFilter(QObject *parent) -: QObject(parent), m_minimum(-1), m_maximum(-1) +: QObject(parent), m_minimum(1), m_maximum(UINT_MAX) { } diff --git a/src/imports/nfc/qdeclarativenearfield.cpp b/src/imports/nfc/qdeclarativenearfield.cpp index 3e45047b..17e6b177 100644 --- a/src/imports/nfc/qdeclarativenearfield.cpp +++ b/src/imports/nfc/qdeclarativenearfield.cpp @@ -92,6 +92,8 @@ This property indicates whether the order of records should be taken into account when matching messages. This is not supported when using neard. + + The default of orderMatch is false. */ /*! diff --git a/src/nfc/android/androidjninfc.cpp b/src/nfc/android/androidjninfc.cpp new file mode 100644 index 00000000..a95e2f98 --- /dev/null +++ b/src/nfc/android/androidjninfc.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidjninfc_p.h" + +#include <android/log.h> + +#include "androidmainnewintentlistener_p.h" + +#include "qglobal.h" +#include "qbytearray.h" +#include "qdebug.h" + +static const char *nfcClassName = "org/qtproject/qt5/android/nfc/QtNfc"; + +static AndroidNfc::MainNfcNewIntentListener mainListener; + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +bool startDiscovery() +{ + return QAndroidJniObject::callStaticMethod<jboolean>(nfcClassName,"start"); +} + +bool isAvailable() +{ + return QAndroidJniObject::callStaticMethod<jboolean>(nfcClassName,"isAvailable"); +} + +bool stopDiscovery() +{ + return QAndroidJniObject::callStaticMethod<jboolean>(nfcClassName,"stop"); +} + +QAndroidJniObject getStartIntent() +{ + QAndroidJniObject ret = QAndroidJniObject::callStaticObjectMethod(nfcClassName, "getStartIntent", "()Landroid/content/Intent;"); + return ret; +} + +bool registerListener(AndroidNfcListenerInterface *listener) +{ + return mainListener.registerListener(listener); +} + +bool unregisterListener(AndroidNfcListenerInterface *listener) +{ + return mainListener.unregisterListener(listener); +} + +QAndroidJniObject getTag(const QAndroidJniObject &intent) +{ + QAndroidJniObject extraTag = QAndroidJniObject::getStaticObjectField("android/nfc/NfcAdapter", "EXTRA_TAG", "Ljava/lang/String;"); + QAndroidJniObject tag = intent.callObjectMethod("getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;", extraTag.object<jstring>()); + Q_ASSERT_X(tag.isValid(), "getTag", "could not get Tag object"); + return tag; +} + +QT_END_ANDROIDNFC_NAMESPACE + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) +{ + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + + return JNI_VERSION_1_6; +} diff --git a/src/nfc/android/androidjninfc_p.h b/src/nfc/android/androidjninfc_p.h new file mode 100644 index 00000000..fd69412a --- /dev/null +++ b/src/nfc/android/androidjninfc_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ANDROIDJNINFC_H +#define ANDROIDJNINFC_H + +#include "qglobal.h" + +#include <QtAndroidExtras/QAndroidJniObject> + +#define QT_USE_ANDROIDNFC_NAMESPACE using namespace ::AndroidNfc; +#define QT_BEGIN_ANDROIDNFC_NAMESPACE namespace AndroidNfc { +#define QT_END_ANDROIDNFC_NAMESPACE } + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +class AndroidNfcListenerInterface +{ +public: + virtual ~AndroidNfcListenerInterface(){} + virtual void newIntent(QAndroidJniObject intent) = 0; +}; + +bool startDiscovery(); +bool stopDiscovery(); +QAndroidJniObject getStartIntent(); +bool isAvailable(); +bool registerListener(AndroidNfcListenerInterface *listener); +bool unregisterListener(AndroidNfcListenerInterface *listener); +QAndroidJniObject getTag(const QAndroidJniObject &intent); + +QT_END_ANDROIDNFC_NAMESPACE + +#endif diff --git a/src/nfc/android/androidmainnewintentlistener.cpp b/src/nfc/android/androidmainnewintentlistener.cpp new file mode 100644 index 00000000..f5b06cbc --- /dev/null +++ b/src/nfc/android/androidmainnewintentlistener.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2015 BasysKom GmbH +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "androidmainnewintentlistener_p.h" +#include "qdebug.h" +#include <QtGui/QGuiApplication> +#include <QtAndroidExtras/QAndroidJniObject> + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +MainNfcNewIntentListener::MainNfcNewIntentListener() + : listeners(), listenersLock(), paused(true), receiving(false) +{ + QtAndroidPrivate::registerNewIntentListener(this); + QtAndroidPrivate::registerResumePauseListener(this); +} + +MainNfcNewIntentListener::~MainNfcNewIntentListener() +{ + QtAndroidPrivate::unregisterNewIntentListener(this); + QtAndroidPrivate::unregisterResumePauseListener(this); +} + +bool MainNfcNewIntentListener::handleNewIntent(JNIEnv */*env*/, jobject intent) +{ + listenersLock.lockForRead(); + foreach (AndroidNfc::AndroidNfcListenerInterface *listener, listeners) { + listener->newIntent(QAndroidJniObject(intent)); + } + listenersLock.unlock(); + return true; +} + +bool MainNfcNewIntentListener::registerListener(AndroidNfcListenerInterface *listener) +{ + static bool firstListener = true; + if (firstListener) { + QAndroidJniObject intent = AndroidNfc::getStartIntent(); + if (intent.isValid()) { + listener->newIntent(intent); + } + paused = static_cast<QGuiApplication*>(QGuiApplication::instance())->applicationState() != Qt::ApplicationActive; + } + firstListener = false; + listenersLock.lockForWrite(); + if (!listeners.contains(listener)) + listeners.push_back(listener); + listenersLock.unlock(); + updateReceiveState(); + return true; +} + +bool MainNfcNewIntentListener::unregisterListener(AndroidNfcListenerInterface *listener) +{ + listenersLock.lockForWrite(); + listeners.removeOne(listener); + listenersLock.unlock(); + updateReceiveState(); + return true; +} + +void MainNfcNewIntentListener::handleResume() +{ + paused = false; + updateReceiveState(); +} + +void MainNfcNewIntentListener::handlePause() +{ + paused = true; + updateReceiveState(); +} + +void MainNfcNewIntentListener::updateReceiveState() +{ + if (paused) { + // We were paused while receiving, so we stop receiving. + if (receiving) { + AndroidNfc::stopDiscovery(); + receiving = false; + } + return; + } + + // We reach here, so we are not paused. + listenersLock.lockForRead(); + // We have nfc listeners and do not receive. Switch on. + if (listeners.count() && !receiving) + receiving = AndroidNfc::startDiscovery(); + + // we have no nfc listeners and do receive. Switch off. + if (!listeners.count() && receiving) { + AndroidNfc::stopDiscovery(); + receiving = false; + } + listenersLock.unlock(); +} + +QT_END_ANDROIDNFC_NAMESPACE diff --git a/src/nfc/android/androidmainnewintentlistener_p.h b/src/nfc/android/androidmainnewintentlistener_p.h new file mode 100644 index 00000000..6a016339 --- /dev/null +++ b/src/nfc/android/androidmainnewintentlistener_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 BasysKom GmbH +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDMAINNEWINTENTLISTENER_P_H_ +#define ANDROIDMAINNEWINTENTLISTENER_P_H_ + +#include <QtCore/private/qjnihelpers_p.h> + +#include "qlist.h" +#include "qreadwritelock.h" +#include "androidjninfc_p.h" + +QT_BEGIN_ANDROIDNFC_NAMESPACE + +class MainNfcNewIntentListener : public QtAndroidPrivate::NewIntentListener, QtAndroidPrivate::ResumePauseListener +{ +public: + MainNfcNewIntentListener(); + ~MainNfcNewIntentListener(); + + //QtAndroidPrivate::NewIntentListener + bool handleNewIntent(JNIEnv *env, jobject intent); + + bool registerListener(AndroidNfcListenerInterface *listener); + bool unregisterListener(AndroidNfcListenerInterface *listener); + + //QtAndroidPrivate::ResumePauseListener + void handleResume(); + void handlePause(); + +private: + void updateReceiveState(); +protected: + QList<AndroidNfc::AndroidNfcListenerInterface*> listeners; + QReadWriteLock listenersLock; + bool paused; + bool receiving; +}; + +QT_END_ANDROIDNFC_NAMESPACE + +#endif /* ANDROIDMAINNEWINTENTLISTENER_P_H_ */ diff --git a/src/nfc/doc/src/nfc-android.qdoc b/src/nfc/doc/src/nfc-android.qdoc new file mode 100644 index 00000000..e7c43423 --- /dev/null +++ b/src/nfc/doc/src/nfc-android.qdoc @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 BasysKom GmbH +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\ingroup connectivity-nfc +\inmodule QtNfc +\since 5.6 +\page nfc-android.html +\title Qt NFC on Android +\brief Notes on Nfc for Android. + +\section1 Automatically launching NDEF message handlers on Android +Android, the registration of NDEF message handlers is done via the +\l{http://developer.android.com/guide/topics/manifest/manifest-intro.html}{Android manifest file}. +This means the application has to provide an AndroidManifest.xml file with proper +\l{http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#manifest}{NFC intent-filter.} + +\code +<intent-filter> + <action android:name="android.nfc.action.NDEF_DISCOVERED"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> +</intent-filter> +\endcode + +When the application is started it has to register an message handler with +\l registerNdefMessageHandler(). +The first NDEF message arriving in the handler is the message that started the application. +See the \l {corkboard}{CorkBoard} application for an example. + +\section2 Note: +Supported tag types in Android are +\l{http://developer.android.com/reference/android/nfc/NfcAdapter.html#ACTION_NDEF_DISCOVERED}{NDEF_DISCOVERED} +and +\l{http://developer.android.com/reference/android/nfc/NfcAdapter.html#ACTION_TECH_DISCOVERED}{ACTION_TAG_DISCOVERED} +with +\l{http://developer.android.com/reference/android/nfc/tech/TagTechnology.html}{TagTechnology} +NdefFormatable or Ndef. +If the application register other types in the +\l{http://developer.android.com/guide/topics/manifest/manifest-intro.html}{Android manifest file} +the application will be started, but the tag will never get delivered to the handler. +The handler should be registered as early as possible. If the application has not registered a handler, the application +will be started every time a new tag is in range and the Android device end up running multiple instances of the application. +*/ + diff --git a/src/nfc/doc/src/nfc-blackberry.qdoc b/src/nfc/doc/src/nfc-blackberry.qdoc new file mode 100644 index 00000000..feebceb8 --- /dev/null +++ b/src/nfc/doc/src/nfc-blackberry.qdoc @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\ingroup connectivity-nfc +\inmodule QtNfc +\since 5.6 +\page nfc-blackberry.html +\title Qt NFC on Blackberry +\brief Notes on Nfc for Blackberry. + +\section1 Automatically launching NDEF message handlers on Blackberry +On BlackBerry the registration for NDEF message handlers is done over the +\l{https://developer.blackberry.com/native/documentation/core/invocation_framework.html}{Invocation Framework}. +This means that the application has to set an invoke target in the bar descriptor xml file when using +\l registerNdefMessageHandler(). + +\code +<invoke-target id="com.myapp.id"> + <type>APPLICATION</type> + <filter> + <action>bb.action.OPEN</action> + <mime-type>application/vnd.rim.nfc.ndef</mime-type> + </filter> +</invoke-target> +\endcode +*/ + diff --git a/src/nfc/nfc.pro b/src/nfc/nfc.pro index 7c6b6ff7..83b8e236 100644 --- a/src/nfc/nfc.pro +++ b/src/nfc/nfc.pro @@ -98,9 +98,8 @@ CONFIG(blackberry) { qnearfieldsharemanagerimpl_p.h \ qnearfieldsharetargetimpl_p.h } -} -linux:qtHaveModule(dbus) { +} else:linux:!android:qtHaveModule(dbus) { NFC_BACKEND_AVAILABLE = yes QT += dbus @@ -123,9 +122,8 @@ linux:qtHaveModule(dbus) { qnearfieldmanager_neard.cpp include(neard/neard.pri) -} -simulator { +} else:simulator { NFC_BACKEND_AVAILABLE = yes QT *= gui @@ -146,6 +144,37 @@ simulator { qllcpserver_simulator_p.cpp \ qnearfieldsharemanagerimpl_p.cpp \ qnearfieldsharetargetimpl_p.cpp +} else:android:!android-no-sdk { + NFC_BACKEND_AVAILABLE = yes + ANDROID_PERMISSIONS = \ + android.permission.NFC + ANDROID_BUNDLED_JAR_DEPENDENCIES = \ + jar/QtNfc-bundled.jar:org.qtproject.qt5.android.nfc.QtNfc + ANDROID_JAR_DEPENDENCIES = \ + jar/QtNfc.jar:org.qtproject.qt5.android.nfc.QtNfc + DEFINES += ANDROID_NFC + QT += core-private gui androidextras + + PRIVATE_HEADERS += \ + qllcpserver_android_p.h \ + qllcpsocket_android_p.h \ + android/androidjninfc_p.h \ + qnearfieldmanager_android_p.h \ + qnearfieldtarget_android_p.h \ + qnearfieldsharemanagerimpl_p.h \ + qnearfieldsharetargetimpl_p.h \ + android/androidmainnewintentlistener_p.h + + + SOURCES += \ + qllcpserver_android_p.cpp \ + qllcpsocket_android_p.cpp \ + android/androidjninfc.cpp \ + qnearfieldmanager_android.cpp \ + qnearfieldtarget_android.cpp \ + qnearfieldsharemanagerimpl_p.cpp \ + qnearfieldsharetargetimpl_p.cpp \ + android/androidmainnewintentlistener.cpp } isEmpty(NFC_BACKEND_AVAILABLE) { diff --git a/src/nfc/qllcpserver_android_p.cpp b/src/nfc/qllcpserver_android_p.cpp new file mode 100644 index 00000000..ba97bcee --- /dev/null +++ b/src/nfc/qllcpserver_android_p.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qllcpserver_android_p.h" +//#include "qnx/qnxnfcmanager_p.h" + +QT_BEGIN_NAMESPACE + +QLlcpServerPrivate::QLlcpServerPrivate(QLlcpServer *q) + : q_ptr(q), m_llcpSocket(0), m_connected(false) +{ +} + +bool QLlcpServerPrivate::listen(const QString &/*serviceUri*/) +{ + /*//The server is already listening + if (isListening()) + return false; + + nfc_result_t result = nfc_llcp_register_connection_listener(NFC_LLCP_SERVER, 0, serviceUri.toStdString().c_str(), &m_conListener); + m_connected = true; + if (result == NFC_RESULT_SUCCESS) { + m_serviceUri = serviceUri; + qQNXNFCDebug() << "LLCP server registered" << serviceUri; + } else { + qWarning() << Q_FUNC_INFO << "Could not register for llcp connection listener"; + return false; + } + QNXNFCManager::instance()->registerLLCPConnection(m_conListener, this);*/ + return true; +} + +bool QLlcpServerPrivate::isListening() const +{ + return m_connected; +} + +void QLlcpServerPrivate::close() +{ + /*nfc_llcp_unregister_connection_listener(m_conListener); + QNXNFCManager::instance()->unregisterLLCPConnection(m_conListener); + m_serviceUri = QString(); + m_connected = false;*/ +} + +QString QLlcpServerPrivate::serviceUri() const +{ + return m_serviceUri; +} + +quint8 QLlcpServerPrivate::serverPort() const +{ + /*unsigned int sap; + if (nfc_llcp_get_local_sap(m_target, &sap) == NFC_RESULT_SUCCESS) { + return sap; + }*/ + return -1; +} + +bool QLlcpServerPrivate::hasPendingConnections() const +{ + return m_llcpSocket != 0; +} + +QLlcpSocket *QLlcpServerPrivate::nextPendingConnection() +{ + /*QLlcpSocket *socket = m_llcpSocket; + m_llcpSocket = 0; + return socket;*/ + return 0; +} + +QLlcpSocket::SocketError QLlcpServerPrivate::serverError() const +{ + return QLlcpSocket::UnknownSocketError; +} + +/*void QLlcpServerPrivate::connected(nfc_target_t *target) +{ + m_target = target; + if (m_llcpSocket != 0) { + qWarning() << Q_FUNC_INFO << "LLCP socket not cloesed properly"; + return; + } + m_llcpSocket = new QLlcpSocket(); + m_llcpSocket->bind(serverPort()); +}*/ + +QT_END_NAMESPACE + + diff --git a/src/nfc/qllcpserver_android_p.h b/src/nfc/qllcpserver_android_p.h new file mode 100644 index 00000000..be725719 --- /dev/null +++ b/src/nfc/qllcpserver_android_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLLCPSERVER_ANDROID_P_H +#define QLLCPSERVER_ANDROID_P_H + +#include "qllcpserver_p.h" +//#include "nfc/nfc.h" + +QT_BEGIN_NAMESPACE + +class QLlcpServerPrivate : public QObject +{ + Q_OBJECT +public: + QLlcpServerPrivate(QLlcpServer *q); + + bool listen(const QString &serviceUri); + bool isListening() const; + + void close(); + + QString serviceUri() const; + quint8 serverPort() const; + + bool hasPendingConnections() const; + QLlcpSocket *nextPendingConnection(); + + QLlcpSocket::SocketError serverError() const; + + //Q_INVOKABLE void connected(nfc_target_t *); + +private: + QLlcpServer *q_ptr; + QLlcpSocket *m_llcpSocket; + //We can not use m_conListener for the connection state + bool m_connected; + //nfc_llcp_connection_listener_t m_conListener; + QString m_serviceUri; + //nfc_target_t *m_target; +}; + +QT_END_NAMESPACE + +#endif // QLLCPSERVER_ANDROID_P_H diff --git a/src/nfc/qllcpsocket_android_p.cpp b/src/nfc/qllcpsocket_android_p.cpp new file mode 100644 index 00000000..0dc425f0 --- /dev/null +++ b/src/nfc/qllcpsocket_android_p.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qllcpsocket_android_p.h" +#include <unistd.h> + +QT_BEGIN_NAMESPACE + +QLlcpSocketPrivate::QLlcpSocketPrivate(QLlcpSocket *q) + : q_ptr(q), m_state(QLlcpSocket::UnconnectedState), m_server(false) +{ +} + +QLlcpSocketPrivate::~QLlcpSocketPrivate() +{ + disconnectFromService(); +} + +void QLlcpSocketPrivate::connectToService(QNearFieldTarget *target, const QString &serviceUri) +{ + Q_UNUSED(target) + Q_UNUSED(serviceUri) + /*if (m_state != QLlcpSocket::UnconnectedState) { + qWarning() << Q_FUNC_INFO << "socket is already connected"; + return; + } + + m_state = QLlcpSocket::ConnectingState; + if (nfc_llcp_register_connection_listener(NFC_LLCP_CLIENT, 0, serviceUri.toLocal8Bit().constData(), + &m_conListener) != NFC_RESULT_SUCCESS) { + qWarning() << Q_FUNC_INFO << "could not register for connection listener"; + return; + } + + QNXNFCManager::instance()->registerLLCPConnection(m_conListener, this); + + qQNXNFCDebug() << "Connecting client socket" << serviceUri << m_conListener; + connect(QNXNFCManager::instance(), SIGNAL(llcpDisconnected()), this, SLOT(disconnectFromService()));*/ +} + +void QLlcpSocketPrivate::disconnectFromService() +{ + /*Q_Q(QLlcpSocket); + QNXNFCManager::instance()->unregisterTargetLost(this); + qQNXNFCDebug() << "Shutting down LLCP socket"; + if (!m_server && nfc_llcp_unregister_connection_listener(m_conListener) != NFC_RESULT_SUCCESS) { + qWarning() << Q_FUNC_INFO << "Error when trying to close LLCP socket"; + } + QNXNFCManager::instance()->unregisterLLCPConnection(m_conListener); + disconnect(QNXNFCManager::instance(), SIGNAL(llcpDisconnected()), this, SLOT(disconnectFromService())); + + q->disconnected(); + m_conListener = 0; + m_state = QLlcpSocket::UnconnectedState;*/ +} + +bool QLlcpSocketPrivate::bind(quint8 port) +{ + Q_UNUSED(port); + + /*m_state = QLlcpSocket::ConnectedState; + m_server = true; + connect(QNXNFCManager::instance(), SIGNAL(llcpDisconnected()), this, SLOT(disconnectFromService())); + connected(QNXNFCManager::instance()->getLastTarget());*/ + + return true; +} + +bool QLlcpSocketPrivate::hasPendingDatagrams() const +{ + return !m_receivedDatagrams.isEmpty(); +} + +qint64 QLlcpSocketPrivate::pendingDatagramSize() const +{ + if (m_receivedDatagrams.isEmpty()) + return -1; + + return m_receivedDatagrams.first().length(); +} + +qint64 QLlcpSocketPrivate::writeDatagram(const char *data, qint64 size) +{ + if (m_state == QLlcpSocket::ConnectedState) + return writeData(data, size); + + return -1; +} + +qint64 QLlcpSocketPrivate::writeDatagram(const QByteArray &datagram) +{ + return writeDatagram(datagram.constData(), datagram.size()); +} + +qint64 QLlcpSocketPrivate::readDatagram(char *data, qint64 maxSize, + QNearFieldTarget **target, quint8 *port) +{ + Q_UNUSED(target); + Q_UNUSED(port); + + if (m_state == QLlcpSocket::ConnectedState) + return readData(data, maxSize); + + return -1; +} + +qint64 QLlcpSocketPrivate::writeDatagram(const char *data, qint64 size, + QNearFieldTarget *target, quint8 port) +{ + Q_UNUSED(target); + Q_UNUSED(port); + + return writeDatagram(data, size); +} + +qint64 QLlcpSocketPrivate::writeDatagram(const QByteArray &datagram, + QNearFieldTarget *target, quint8 port) +{ + Q_UNUSED(datagram); + Q_UNUSED(target); + Q_UNUSED(port); + + return writeDatagram(datagram.constData(), datagram.size()-1); +} + +QLlcpSocket::SocketError QLlcpSocketPrivate::error() const +{ + return QLlcpSocket::UnknownSocketError; +} + +QLlcpSocket::SocketState QLlcpSocketPrivate::state() const +{ + return m_state; +} + +qint64 QLlcpSocketPrivate::readData(char *data, qint64 maxlen) +{ + Q_UNUSED(data); + Q_UNUSED(maxlen); + if (m_receivedDatagrams.isEmpty()) + return 0; + + /*const QByteArray datagram = m_receivedDatagrams.takeFirst(); + qint64 size = qMin(maxlen, qint64(datagram.length())); + memcpy(data, datagram.constData(), size); + return size;*/ + return 0; +} + +qint64 QLlcpSocketPrivate::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + /*if (socketState != Idle) { + m_writeQueue.append(QByteArray(data, len)); + return len; + } else { + socketState = Writing; + qQNXNFCDebug() << "LLCP write"; + nfc_result_t res = nfc_llcp_write(m_target, (uchar_t*)data, (size_t)len); + if (res == NFC_RESULT_SUCCESS) { + return len; + } else { + qWarning() << Q_FUNC_INFO << "Error writing to LLCP socket. Error" << res; + enteringIdle(); + return -1; + } + }*/ + return -1; +} + +qint64 QLlcpSocketPrivate::bytesAvailable() const +{ + /*qint64 available = 0; + foreach (const QByteArray &datagram, m_receivedDatagrams) + available += datagram.length(); + + return available;*/ + return 0; +} + +bool QLlcpSocketPrivate::canReadLine() const +{ + /*foreach (const QByteArray &datagram, m_receivedDatagrams) { + if (datagram.contains('\n')) + return true; + }*/ + + return false; +} + +bool QLlcpSocketPrivate::waitForReadyRead(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +bool QLlcpSocketPrivate::waitForBytesWritten(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +bool QLlcpSocketPrivate::waitForConnected(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +bool QLlcpSocketPrivate::waitForDisconnected(int msecs) +{ + Q_UNUSED(msecs); + + return false; +} + +/*void QLlcpSocketPrivate::connected(nfc_target_t *target) +{ + Q_Q(QLlcpSocket); + m_target = target; + + m_state = QLlcpSocket::ConnectedState; + emit q->connected(); + qQNXNFCDebug() << "Socket connected"; + + unsigned int targetId; + nfc_get_target_connection_id(target, &targetId); + QNXNFCManager::instance()->requestTargetLost(this, targetId); + enteringIdle(); +}*/ + +void QLlcpSocketPrivate::targetLost() +{ + disconnectFromService(); + //qQNXNFCDebug() << "LLCP target lost...socket disconnected"; +} + +void QLlcpSocketPrivate::dataRead(QByteArray& data) +{ + Q_UNUSED(data); + /*Q_Q(QLlcpSocket); + if (!data.isEmpty()) { + m_receivedDatagrams.append(data); + emit q->readyRead(); + } + socketState = Idle; + enteringIdle();*/ +} + +void QLlcpSocketPrivate::dataWritten() +{ + //enteringIdle(); +} + +void QLlcpSocketPrivate::read() +{ + /*if (socketState != Idle) { + qQNXNFCDebug() << "Trying to read but socket state not in idle..abort"; + return; + } + socketState = Reading; + qQNXNFCDebug() << "LLCP read"; + if (nfc_llcp_read(m_target, 128) != NFC_RESULT_SUCCESS) { + qWarning() << Q_FUNC_INFO << "Could not register for reading"; + socketState = Idle; + }*/ +} + +void QLlcpSocketPrivate::enteringIdle() +{ + /*qQNXNFCDebug() << "entering idle; Socket state:" << socketState; + socketState = Idle; + if (m_state == QLlcpSocket::ConnectedState) { + if (m_writeQueue.isEmpty()) { + qQNXNFCDebug() << "Write queue empty, reading in 50ms"; + QTimer::singleShot(50, this, SLOT(read())); + } else { + qQNXNFCDebug() << "Write first package in queue"; + writeDatagram(m_writeQueue.takeFirst()); + } + }*/ +} + +QT_END_NAMESPACE + diff --git a/src/nfc/qllcpsocket_android_p.h b/src/nfc/qllcpsocket_android_p.h new file mode 100644 index 00000000..02865823 --- /dev/null +++ b/src/nfc/qllcpsocket_android_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLLCPSOCKET_ANDROID_P_H +#define QLLCPSOCKET_ANDROID_P_H + +#include "qllcpsocket_p.h" + +//#include "qnearfieldtarget_ANDROID_p.h" + +QT_BEGIN_NAMESPACE + +class QLlcpSocketPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(QLlcpSocket) + +public: + QLlcpSocketPrivate(QLlcpSocket *q); + + ~QLlcpSocketPrivate(); + + void connectToService(QNearFieldTarget *target, const QString &serviceUri); + + bool bind(quint8 port); + + bool hasPendingDatagrams() const; + qint64 pendingDatagramSize() const; + + qint64 writeDatagram(const char *data, qint64 size); + qint64 writeDatagram(const QByteArray &datagram); + + qint64 readDatagram(char *data, qint64 maxSize, + QNearFieldTarget **target = 0, quint8 *port = 0); + qint64 writeDatagram(const char *data, qint64 size, + QNearFieldTarget *target, quint8 port); + qint64 writeDatagram(const QByteArray &datagram, QNearFieldTarget *target, quint8 port); + + QLlcpSocket::SocketError error() const; + QLlcpSocket::SocketState state() const; + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + + qint64 bytesAvailable() const; + bool canReadLine() const; + + bool waitForReadyRead(int msecs); + bool waitForBytesWritten(int msecs); + bool waitForConnected(int msecs); + bool waitForDisconnected(int msecs); + + //Q_INVOKABLE void connected(nfc_target_t *); + Q_INVOKABLE void targetLost(); + + void dataRead(QByteArray&); + void dataWritten(); + +public Q_SLOTS: + void disconnectFromService(); + +private: + QLlcpSocket *q_ptr; + unsigned int m_sap; + //nfc_llcp_connection_listener_t m_conListener; + //NearFieldTarget *m_target; + //nfc_target_t *m_target; + + QLlcpSocket::SocketState m_state; + + QList<QByteArray> m_receivedDatagrams; + QList<QByteArray> m_writeQueue; + + bool m_server; + + enum llcpState { + Idle, Reading, Writing + } socketState; + +private Q_SLOTS: + void read(); + void enteringIdle(); +}; + +QT_END_NAMESPACE + +#endif // QLLCPSOCKET_ANDROID_P_H diff --git a/src/nfc/qnearfieldmanager.cpp b/src/nfc/qnearfieldmanager.cpp index dce1a9f3..84f00d91 100644 --- a/src/nfc/qnearfieldmanager.cpp +++ b/src/nfc/qnearfieldmanager.cpp @@ -40,6 +40,8 @@ #include "qnearfieldmanager_qnx_p.h" #elif defined(NEARD_NFC) #include "qnearfieldmanager_neard_p.h" +#elif defined(ANDROID_NFC) +#include "qnearfieldmanager_android_p.h" #else #include "qnearfieldmanagerimpl_p.h" #endif @@ -100,20 +102,10 @@ QT_BEGIN_NAMESPACE \snippet doc_src_qtnfc.cpp handleNdefMessage - On BlackBerry the registration for NDEF message handlers is done over the - \l{https://developer.blackberry.com/native/documentation/core/invocation_framework.html}{Invocation Framework}. - This means that the application has to set an invoke target in the bar descriptor xml file when using - \l registerNdefMessageHandler(). - - \code - <invoke-target id="com.myapp.id"> - <type>APPLICATION</type> - <filter> - <action>bb.action.OPEN</action> - <mime-type>application/vnd.rim.nfc.ndef</mime-type> - </filter> - </invoke-target> - \endcode + Automatically launching NDEF message handlers is supported on + \l{nfc-blackberry.html}{Blackberry} + and + \l{nfc-android.html}{Android}. \section3 NFC on Linux The \l{https://01.org/linux-nfc}{Linux NFC project} provides software to support NFC on Linux platforms. diff --git a/src/nfc/qnearfieldmanager_android.cpp b/src/nfc/qnearfieldmanager_android.cpp new file mode 100644 index 00000000..d1434840 --- /dev/null +++ b/src/nfc/qnearfieldmanager_android.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnearfieldmanager_android_p.h" +#include "qnearfieldtarget_android_p.h" + +#include "qndeffilter.h" +#include "qndefmessage.h" +#include "qndefrecord.h" +#include "qbytearray.h" +#include "qcoreapplication.h" +#include "qdebug.h" +#include "qlist.h" + +#include <QtCore/QMetaType> +#include <QtCore/QMetaMethod> + +QT_BEGIN_NAMESPACE + +QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl() : + m_detecting(false), m_handlerID(0) +{ + qRegisterMetaType<QAndroidJniObject>("QAndroidJniObject"); + qRegisterMetaType<QNdefMessage>("QNdefMessage"); + connect(this, SIGNAL(targetDetected(QNearFieldTarget*)), this, SLOT(handlerTargetDetected(QNearFieldTarget*))); + connect(this, SIGNAL(targetLost(QNearFieldTarget*)), this, SLOT(handlerTargetLost(QNearFieldTarget*))); +} + +QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl() +{ +} + +void QNearFieldManagerPrivateImpl::handlerTargetDetected(QNearFieldTarget *target) +{ + if (ndefMessageHandlers.count() == 0 && ndefFilterHandlers.count() == 0) // if no handler is registered + return; + if (target->hasNdefMessage()) { + connect(target, SIGNAL(ndefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerNdefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &))); + connect(target, SIGNAL(requestCompleted(const QNearFieldTarget::RequestId &)), + this, SLOT(handlerRequestCompleted(const QNearFieldTarget::RequestId &))); + connect(target, SIGNAL(error(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerError(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &))); + + QNearFieldTarget::RequestId id = target->readNdefMessages(); + m_idToTarget.insert(id, target); + } +} + +void QNearFieldManagerPrivateImpl::handlerTargetLost(QNearFieldTarget *target) +{ + disconnect(target, SIGNAL(ndefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerNdefMessageRead(const QNdefMessage &, const QNearFieldTarget::RequestId &))); + disconnect(target, SIGNAL(requestCompleted(const QNearFieldTarget::RequestId &)), + this, SLOT(handlerRequestCompleted(const QNearFieldTarget::RequestId &))); + disconnect(target, SIGNAL(error(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &)), + this, SLOT(handlerError(QNearFieldTarget::Error, const QNearFieldTarget::RequestId &))); + m_idToTarget.remove(m_idToTarget.key(target)); +} + +struct VerifyRecord +{ + QNdefFilter::Record filterRecord; + unsigned int count; +}; + +void QNearFieldManagerPrivateImpl::handlerNdefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id) +{ + QNearFieldTarget *target = m_idToTarget.value(id); + //For message handlers without filters + for (int i = 0; i < ndefMessageHandlers.count(); i++) { + ndefMessageHandlers.at(i).second.invoke(ndefMessageHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target)); + } + + //For message handlers that specified a filter + for (int i = 0; i < ndefFilterHandlers.count(); ++i) { + bool matched = true; + + QNdefFilter filter = ndefFilterHandlers.at(i).second.first; + + QList<VerifyRecord> filterRecords; + for (int j = 0; j < filter.recordCount(); ++j) { + VerifyRecord vr; + vr.count = 0; + vr.filterRecord = filter.recordAt(j); + + filterRecords.append(vr); + } + + foreach (const QNdefRecord &record, message) { + for (int j = 0; matched && (j < filterRecords.count()); ++j) { + VerifyRecord &vr = filterRecords[j]; + + if (vr.filterRecord.typeNameFormat == record.typeNameFormat() && + ( vr.filterRecord.type == record.type() || + vr.filterRecord.type.isEmpty()) ) { + ++vr.count; + break; + } else { + if (filter.orderMatch()) { + if (vr.filterRecord.minimum <= vr.count && + vr.count <= vr.filterRecord.maximum) { + continue; + } else { + matched = false; + } + } + } + } + } + + for (int j = 0; matched && (j < filterRecords.count()); ++j) { + const VerifyRecord &vr = filterRecords.at(j); + + if (vr.filterRecord.minimum <= vr.count && vr.count <= vr.filterRecord.maximum) + continue; + else + matched = false; + } + + if (matched) { + ndefFilterHandlers.at(i).second.second.invoke(ndefFilterHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target)); + } + } +} + +void QNearFieldManagerPrivateImpl::handlerRequestCompleted(const QNearFieldTarget::RequestId &id) +{ + m_idToTarget.remove(id); +} + +void QNearFieldManagerPrivateImpl::handlerError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id) +{ + Q_UNUSED(error); + m_idToTarget.remove(id); +} + +bool QNearFieldManagerPrivateImpl::isAvailable() const +{ + return AndroidNfc::isAvailable(); +} + +bool QNearFieldManagerPrivateImpl::startTargetDetection() +{ + if (m_detecting) + return false; // Already detecting targets + + m_detecting = true; + updateReceiveState(); + return true; +} + +void QNearFieldManagerPrivateImpl::stopTargetDetection() +{ + m_detecting = false; + updateReceiveState(); +} + +int QNearFieldManagerPrivateImpl::registerNdefMessageHandler(QObject *object, const QMetaMethod &method) +{ + ndefMessageHandlers.append(QPair<QPair<int, QObject *>, QMetaMethod>(QPair<int, QObject *>(m_handlerID, object), method)); + updateReceiveState(); + //Returns the handler ID and increments it afterwards + return m_handlerID++; +} + +int QNearFieldManagerPrivateImpl::registerNdefMessageHandler(const QNdefFilter &filter, + QObject *object, const QMetaMethod &method) +{ + //If no record is set in the filter, we ignore the filter + if (filter.recordCount()==0) + return registerNdefMessageHandler(object, method); + + ndefFilterHandlers.append(QPair<QPair<int, QObject*>, QPair<QNdefFilter, QMetaMethod> > + (QPair<int, QObject*>(m_handlerID, object), QPair<QNdefFilter, QMetaMethod>(filter, method))); + + updateReceiveState(); + + return m_handlerID++; +} + +bool QNearFieldManagerPrivateImpl::unregisterNdefMessageHandler(int handlerId) +{ + for (int i=0; i<ndefMessageHandlers.count(); ++i) { + if (ndefMessageHandlers.at(i).first.first == handlerId) { + ndefMessageHandlers.removeAt(i); + updateReceiveState(); + return true; + } + } + for (int i=0; i<ndefFilterHandlers.count(); ++i) { + if (ndefFilterHandlers.at(i).first.first == handlerId) { + ndefFilterHandlers.removeAt(i); + updateReceiveState(); + return true; + } + } + return false; +} + +void QNearFieldManagerPrivateImpl::requestAccess(QNearFieldManager::TargetAccessModes accessModes) +{ + Q_UNUSED(accessModes); + //Do nothing, because we dont have access modes for the target +} + +void QNearFieldManagerPrivateImpl::releaseAccess(QNearFieldManager::TargetAccessModes accessModes) +{ + Q_UNUSED(accessModes); + //Do nothing, because we dont have access modes for the target +} + +void QNearFieldManagerPrivateImpl::newIntent(QAndroidJniObject intent) +{ + // This function is called from different thread and is used to move intent to main thread. + QMetaObject::invokeMethod(this, "onTargetDiscovered", Qt::QueuedConnection, Q_ARG(QAndroidJniObject, intent)); +} + +QByteArray QNearFieldManagerPrivateImpl::getUid(const QAndroidJniObject &intent) +{ + if (!intent.isValid()) + return QByteArray(); + + QAndroidJniEnvironment env; + QAndroidJniObject tag = AndroidNfc::getTag(intent); + return getUidforTag(tag); +} + +void QNearFieldManagerPrivateImpl::onTargetDiscovered(QAndroidJniObject intent) +{ + // Getting UID + QByteArray uid = getUid(intent); + + // Accepting all targets but only sending signal of requested types. + NearFieldTarget *&target = m_detectedTargets[uid]; + if (target) { + target->setIntent(intent); // Updating existing target + } else { + target = new NearFieldTarget(intent, uid, this); + connect(target, SIGNAL(targetDestroyed(QByteArray)), this, SLOT(onTargetDestroyed(QByteArray))); + connect(target, SIGNAL(targetLost(QNearFieldTarget*)), this, SIGNAL(targetLost(QNearFieldTarget*))); + } + emit targetDetected(target); +} + +void QNearFieldManagerPrivateImpl::onTargetDestroyed(const QByteArray &uid) +{ + m_detectedTargets.remove(uid); +} + +QByteArray QNearFieldManagerPrivateImpl::getUidforTag(const QAndroidJniObject &tag) +{ + if (!tag.isValid()) + return QByteArray(); + + QAndroidJniEnvironment env; + QAndroidJniObject tagId = tag.callObjectMethod("getId", "()[B"); + QByteArray uid; + jsize len = env->GetArrayLength(tagId.object<jbyteArray>()); + uid.resize(len); + env->GetByteArrayRegion(tagId.object<jbyteArray>(), 0, len, reinterpret_cast<jbyte*>(uid.data())); + return uid; +} + +void QNearFieldManagerPrivateImpl::updateReceiveState() +{ + if (m_detecting) { + AndroidNfc::registerListener(this); + } else { + if (ndefMessageHandlers.count() || ndefFilterHandlers.count()) { + AndroidNfc::registerListener(this); + } else { + AndroidNfc::unregisterListener(this); + } + } +} + +QT_END_NAMESPACE diff --git a/src/nfc/qnearfieldmanager_android_p.h b/src/nfc/qnearfieldmanager_android_p.h new file mode 100644 index 00000000..3d45b700 --- /dev/null +++ b/src/nfc/qnearfieldmanager_android_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNEARFIELDMANAGER_ANDROID_P_H +#define QNEARFIELDMANAGER_ANDROID_P_H + +#include "qnearfieldmanager_p.h" +#include "qnearfieldmanager.h" +#include "qnearfieldtarget.h" +#include "android/androidjninfc_p.h" + +#include <QHash> +#include <QMap> +#include <QtAndroidExtras/QAndroidJniObject> +#include <QtAndroidExtras/QAndroidJniEnvironment> + +QT_BEGIN_NAMESPACE + +typedef QList<QNdefMessage> QNdefMessageList; + +class NearFieldTarget; +class QByteArray; +class QNearFieldManagerPrivateImpl : public QNearFieldManagerPrivate, public AndroidNfc::AndroidNfcListenerInterface +{ + Q_OBJECT + +public: + QNearFieldManagerPrivateImpl(); + ~QNearFieldManagerPrivateImpl(); + + virtual bool isAvailable() const; + virtual bool startTargetDetection(); + virtual void stopTargetDetection(); + virtual int registerNdefMessageHandler(QObject *object, const QMetaMethod &method); + virtual int registerNdefMessageHandler(const QNdefFilter &filter, QObject *object, const QMetaMethod &method); + virtual bool unregisterNdefMessageHandler(int handlerId); + virtual void requestAccess(QNearFieldManager::TargetAccessModes accessModes); + virtual void releaseAccess(QNearFieldManager::TargetAccessModes accessModes); + virtual void newIntent(QAndroidJniObject intent); + QByteArray getUid(const QAndroidJniObject &intent); + +public slots: + void onTargetDiscovered(QAndroidJniObject intent); + void onTargetDestroyed(const QByteArray &uid); + void handlerTargetDetected(QNearFieldTarget *target); + void handlerTargetLost(QNearFieldTarget *target); + void handlerNdefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id); + void handlerRequestCompleted(const QNearFieldTarget::RequestId &id); + void handlerError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id); + +protected: + static QByteArray getUidforTag(const QAndroidJniObject &tag); + void updateReceiveState(); + +private: + bool m_detecting; + QHash<QByteArray, NearFieldTarget*> m_detectedTargets; + QMap<QNearFieldTarget::RequestId, QNearFieldTarget*> m_idToTarget; + + int m_handlerID; + QList< QPair<QPair<int, QObject *>, QMetaMethod> > ndefMessageHandlers; + QList< QPair<QPair<int, QObject *>, QPair<QNdefFilter, QMetaMethod> > > ndefFilterHandlers; +}; + +QT_END_NAMESPACE + +#endif // QNEARFIELDMANAGER_ANDROID_P_H diff --git a/src/nfc/qnearfieldmanager_qnx.cpp b/src/nfc/qnearfieldmanager_qnx.cpp index 2c3c7077..f4508bd8 100644 --- a/src/nfc/qnearfieldmanager_qnx.cpp +++ b/src/nfc/qnearfieldmanager_qnx.cpp @@ -140,6 +140,12 @@ void QNearFieldManagerPrivateImpl::releaseAccess(QNearFieldManager::TargetAccess //Do nothing, because we don't have access modes for the target } +struct VerifyRecord +{ + QNdefFilter::Record filterRecord; + unsigned int count; +}; + void QNearFieldManagerPrivateImpl::handleMessage(const QNdefMessage &message, QNearFieldTarget *target) { qQNXNFCDebug() << "Handling message in near field manager. Filtercount:" @@ -151,20 +157,53 @@ void QNearFieldManagerPrivateImpl::handleMessage(const QNdefMessage &message, QN //For message handlers that specified a filter for (int i = 0; i < ndefFilterHandlers.count(); i++) { + bool matched = true; + QNdefFilter filter = ndefFilterHandlers.at(i).second.first; - if (filter.recordCount() > message.count()) - continue; - - int j=0; - for (j = 0; j < filter.recordCount();) { - if (message.at(j).typeNameFormat() != filter.recordAt(j).typeNameFormat - || message.at(j).type() != filter.recordAt(j).type ) { - break; + + QList<VerifyRecord> filterRecords; + for (int j = 0; j < filter.recordCount(); ++j) { + VerifyRecord vr; + vr.count = 0; + vr.filterRecord = filter.recordAt(j); + + filterRecords.append(vr); + } + + foreach (const QNdefRecord &record, message) { + for (int j = 0; matched && (j < filterRecords.count()); ++j) { + VerifyRecord &vr = filterRecords[j]; + + if (vr.filterRecord.typeNameFormat == record.typeNameFormat() && + ( vr.filterRecord.type == record.type() || + vr.filterRecord.type.isEmpty()) ) { + ++vr.count; + break; + } else { + if (filter.orderMatch()) { + if (vr.filterRecord.minimum <= vr.count && + vr.count <= vr.filterRecord.maximum) { + continue; + } else { + matched = false; + } + } + } } - j++; } - if (j == filter.recordCount()) + + for (int j = 0; matched && (j < filterRecords.count()); ++j) { + const VerifyRecord &vr = filterRecords.at(j); + + if (vr.filterRecord.minimum <= vr.count && vr.count <= vr.filterRecord.maximum) + continue; + else + matched = false; + } + + if (matched) { ndefFilterHandlers.at(i).second.second.invoke(ndefFilterHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target)); + } } } diff --git a/src/nfc/qnearfieldmanagervirtualbase.cpp b/src/nfc/qnearfieldmanagervirtualbase.cpp index cf25ed2b..b0f77e2f 100644 --- a/src/nfc/qnearfieldmanagervirtualbase.cpp +++ b/src/nfc/qnearfieldmanagervirtualbase.cpp @@ -172,7 +172,8 @@ void QNearFieldManagerPrivateVirtualBase::ndefReceived(const QNdefMessage &messa VerifyRecord &vr = filterRecords[j]; if (vr.filterRecord.typeNameFormat == record.typeNameFormat() && - vr.filterRecord.type == record.type()) { + ( vr.filterRecord.type == record.type() || + vr.filterRecord.type.isEmpty()) ) { ++vr.count; break; } else { diff --git a/src/nfc/qnearfieldtarget_android.cpp b/src/nfc/qnearfieldtarget_android.cpp new file mode 100644 index 00000000..891ce3fc --- /dev/null +++ b/src/nfc/qnearfieldtarget_android.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnearfieldtarget_android_p.h" +#include "android/androidjninfc_p.h" +#include "qdebug.h" + +#define NDEFTECHNOLOGY "android.nfc.tech.Ndef" +#define NDEFFORMATABLETECHNOLOGY "android.nfc.tech.NdefFormatable" +#define NFCATECHNOLOGY "android.nfc.tech.NfcA" +#define NFCBTECHNOLOGY "android.nfc.tech.NfcB" +#define NFCFTECHNOLOGY "android.nfc.tech.NfcF" +#define NFCVTECHNOLOGY "android.nfc.tech.NfcV" +#define MIFARECLASSICTECHNOLOGY "android.nfc.tech.MifareClassic" +#define MIFARECULTRALIGHTTECHNOLOGY "android.nfc.tech.MifareUltralight" + +#define MIFARETAG "com.nxp.ndef.mifareclassic" +#define NFCTAGTYPE1 "org.nfcforum.ndef.type1" +#define NFCTAGTYPE2 "org.nfcforum.ndef.type2" +#define NFCTAGTYPE3 "org.nfcforum.ndef.type3" +#define NFCTAGTYPE4 "org.nfcforum.ndef.type4" + +NearFieldTarget::NearFieldTarget(QAndroidJniObject intent, const QByteArray uid, QObject *parent) : + QNearFieldTarget(parent), + m_intent(intent), + m_uid(uid) +{ + updateTechList(); + updateType(); + setupTargetCheckTimer(); +} + +NearFieldTarget::~NearFieldTarget() +{ + releaseIntent(); + emit targetDestroyed(m_uid); +} + +QByteArray NearFieldTarget::uid() const +{ + return m_uid; +} + +QNearFieldTarget::Type NearFieldTarget::type() const +{ + return m_type; +} + +QNearFieldTarget::AccessMethods NearFieldTarget::accessMethods() const +{ + AccessMethods result = NdefAccess; + return result; +} + +bool NearFieldTarget::hasNdefMessage() +{ + return m_techList.contains(QStringLiteral(NDEFTECHNOLOGY)); +} + +QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages() +{ + // Making sure that target has NDEF messages + if (!hasNdefMessage()) + return QNearFieldTarget::RequestId(); + + // Making sure that target is still in range + QNearFieldTarget::RequestId requestId(new QNearFieldTarget::RequestIdPrivate); + if (!m_intent.isValid()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Getting Ndef technology object + QAndroidJniObject ndef = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY)); + if (!ndef.isValid()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnsupportedError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Connect + ndef.callMethod<void>("connect"); + if (catchJavaExceptions()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Get NdefMessage object + QAndroidJniObject ndefMessage = ndef.callObjectMethod("getNdefMessage", "()Landroid/nfc/NdefMessage;"); + if (catchJavaExceptions()) + ndefMessage = QAndroidJniObject(); + if (!ndefMessage.isValid()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefReadError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Convert to byte array + QAndroidJniObject ndefMessageBA = ndefMessage.callObjectMethod("toByteArray", "()[B"); + QByteArray ndefMessageQBA = jbyteArrayToQByteArray(ndefMessageBA.object<jbyteArray>()); + + // Closing connection + ndef.callMethod<void>("close"); + catchJavaExceptions(); // IOException at this point does not matter anymore. + + // Sending QNdefMessage, requestCompleted and exit. + QNdefMessage qNdefMessage = QNdefMessage::fromByteArray(ndefMessageQBA); + QMetaObject::invokeMethod(this, "ndefMessageRead", Qt::QueuedConnection, + Q_ARG(const QNdefMessage&, qNdefMessage)); + QMetaObject::invokeMethod(this, "requestCompleted", Qt::QueuedConnection, + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + QMetaObject::invokeMethod(this, "ndefMessageRead", Qt::QueuedConnection, + Q_ARG(const QNdefMessage&, qNdefMessage), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; +} + + +QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &command) +{ + Q_UNUSED(command); + Q_EMIT QNearFieldTarget::error(QNearFieldTarget::UnsupportedError, QNearFieldTarget::RequestId()); + return QNearFieldTarget::RequestId(); + + //Not supported for now + /*if (command.size() == 0) { + Q_EMIT QNearFieldTarget::error(QNearFieldTarget::InvalidParametersError, QNearFieldTarget::RequestId()); + return QNearFieldTarget::RequestId(); + } + + AndroidNfc::AttachedJNIEnv aenv; + JNIEnv *env = aenv.jniEnv; + + jobject tagTech; + if (m_techList.contains(QStringLiteral(NFCATECHNOLOGY))) { + tagTech = getTagTechnology(QStringLiteral(NFCATECHNOLOGY)); + } else if (m_techList.contains(QStringLiteral(NFCBTECHNOLOGY))) { + tagTech = getTagTechnology(QStringLiteral(NFCBTECHNOLOGY)); + } else if (m_techList.contains(QStringLiteral(NFCFTECHNOLOGY))) { + tagTech = getTagTechnology(QStringLiteral(NFCFTECHNOLOGY)); + } else if (m_techList.contains(QStringLiteral(NFCVTECHNOLOGY))) { + tagTech = getTagTechnology(QStringLiteral(NFCVTECHNOLOGY)); + } else { + Q_EMIT QNearFieldTarget::error(QNearFieldTarget::UnsupportedError, QNearFieldTarget::RequestId()); + return QNearFieldTarget::RequestId(); + } + + QByteArray ba(ba); + + jclass techClass = env->GetObjectClass(tagTech); + jmethodID tranceiveMID = env->GetMethodID(techClass, "tranceive", "([B)[B"); + Q_ASSERT_X(tranceiveMID != 0, "sendCommand", "could not find tranceive method"); + + jbyteArray jba = env->NewByteArray(ba.size()); + env->SetByteArrayRegion(jba, 0, ba.size(), reinterpret_cast<jbyte*>(ba.data())); + + jbyteArray rsp = reinterpret_cast<jbyteArray>(env->CallObjectMethod(tagTech, tranceiveMID, jba)); + + jsize len = env->GetArrayLength(rsp); + QByteArray rspQBA; + rspQBA.resize(len); + + env->GetByteArrayRegion(rsp, 0, len, reinterpret_cast<jbyte*>(rspQBA.data())); + + qDebug() << "Send command returned QBA size: " << rspQBA.size(); + + + env->DeleteLocalRef(jba); + + + return QNearFieldTarget::RequestId();*/ +} + +QNearFieldTarget::RequestId NearFieldTarget::sendCommands(const QList<QByteArray> &commands) +{ + QNearFieldTarget::RequestId requestId; + for (int i=0; i < commands.size(); i++){ + requestId = sendCommand(commands.at(i)); + } + return requestId; +} + +QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QList<QNdefMessage> &messages) +{ + if (messages.size() == 0) + return QNearFieldTarget::RequestId(); + + if (messages.size() > 1) + qWarning("QNearFieldTarget::writeNdefMessages: Android supports writing only one NDEF message per tag."); + + QAndroidJniEnvironment env; + const char *writeMethod; + QAndroidJniObject tagTechnology; + + // Getting write method + if (m_techList.contains(QStringLiteral(NDEFFORMATABLETECHNOLOGY))) { + tagTechnology = getTagTechnology(QStringLiteral(NDEFFORMATABLETECHNOLOGY)); + writeMethod = "format"; + } else if (m_techList.contains(QStringLiteral(NDEFTECHNOLOGY))) { + tagTechnology = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY)); + writeMethod = "writeNdefMessage"; + } else { + // An invalid request id will be returned if the target does not support writing NDEF messages. + return QNearFieldTarget::RequestId(); + } + + // Connecting + QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate()); + tagTechnology.callMethod<void>("connect"); + if (catchJavaExceptions()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Making NdefMessage object + const QNdefMessage &message = messages.first(); + QByteArray ba = message.toByteArray(); + QAndroidJniObject jba = env->NewByteArray(ba.size()); + env->SetByteArrayRegion(jba.object<jbyteArray>(), 0, ba.size(), reinterpret_cast<jbyte*>(ba.data())); + QAndroidJniObject jmessage = QAndroidJniObject("android/nfc/NdefMessage", "([B)V", jba.object<jbyteArray>()); + if (catchJavaExceptions()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnknownError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Writing + tagTechnology.callMethod<void>(writeMethod, "(Landroid/nfc/NdefMessage;)V", jmessage.object<jobject>()); + if (catchJavaExceptions()) { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::NdefWriteError), + Q_ARG(const QNearFieldTarget::RequestId&, requestId)); + return requestId; + } + + // Closing connection, sending signal and exit + tagTechnology.callMethod<void>("close"); + catchJavaExceptions(); // IOException at this point does not matter anymore. + QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection); + return requestId; +} + +void NearFieldTarget::setIntent(QAndroidJniObject intent) +{ + if (m_intent == intent) + return; + + releaseIntent(); + m_intent = intent; + if (m_intent.isValid()) { + // Updating tech list and type in case of there is another tag with same UID as one before. + updateTechList(); + updateType(); + m_targetCheckTimer->start(); + } +} + +void NearFieldTarget::checkIsTargetLost() +{ + if (!m_intent.isValid() || m_techList.isEmpty()) { + handleTargetLost(); + return; + } + // Using first available technology to check connection + QString techStr = m_techList.first(); + QAndroidJniObject tagTech = getTagTechnology(techStr); + tagTech.callMethod<void>("connect"); + if (catchJavaExceptions()) { + handleTargetLost(); + return; + } + tagTech.callMethod<void>("close"); + if (catchJavaExceptions()) + handleTargetLost(); +} + +void NearFieldTarget::releaseIntent() +{ + m_targetCheckTimer->stop(); + + m_intent = QAndroidJniObject(); +} + +void NearFieldTarget::updateTechList() +{ + if (!m_intent.isValid()) + return; + + // Getting tech list + QAndroidJniEnvironment env; + QAndroidJniObject tag = AndroidNfc::getTag(m_intent); + QAndroidJniObject techListArray = tag.callObjectMethod("getTechList", "()[Ljava/lang/String;"); + if (!techListArray.isValid()) { + handleTargetLost(); + return; + } + + // Converting tech list array to QStringList. + m_techList.clear(); + jsize techCount = env->GetArrayLength(techListArray.object<jobjectArray>()); + for (jsize i = 0; i < techCount; ++i) { + QAndroidJniObject tech = env->GetObjectArrayElement(techListArray.object<jobjectArray>(), i); + m_techList.append(tech.callObjectMethod<jstring>("toString").toString()); + } +} + +void NearFieldTarget::updateType() +{ + m_type = getTagType(); +} + +QNearFieldTarget::Type NearFieldTarget::getTagType() const +{ + QAndroidJniEnvironment env; + + if (m_techList.contains(QStringLiteral(NDEFTECHNOLOGY))) { + QAndroidJniObject ndef = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY)); + QString qtype = ndef.callObjectMethod("getType", "()Ljava/lang/String;").toString(); + + if (qtype.compare(QStringLiteral(MIFARETAG)) == 0) + return MifareTag; + if (qtype.compare(QStringLiteral(NFCTAGTYPE1)) == 0) + return NfcTagType1; + if (qtype.compare(QStringLiteral(NFCTAGTYPE2)) == 0) + return NfcTagType2; + if (qtype.compare(QStringLiteral(NFCTAGTYPE3)) == 0) + return NfcTagType3; + if (qtype.compare(QStringLiteral(NFCTAGTYPE4)) == 0) + return NfcTagType4; + return ProprietaryTag; + } else if (m_techList.contains(QStringLiteral(NFCATECHNOLOGY))) { + if (m_techList.contains(QStringLiteral(MIFARECLASSICTECHNOLOGY))) + return MifareTag; + + // Checking ATQA/SENS_RES + // xxx0 0000 xxxx xxxx: Identifies tag Type 1 platform + QAndroidJniObject nfca = getTagTechnology(QStringLiteral(NFCATECHNOLOGY)); + QAndroidJniObject atqaBA = nfca.callObjectMethod("getAtqa", "()[B"); + QByteArray atqaQBA = jbyteArrayToQByteArray(atqaBA.object<jbyteArray>()); + if (atqaQBA.isEmpty()) + return ProprietaryTag; + if ((atqaQBA[0] & 0x1F) == 0x00) + return NfcTagType1; + + // Checking SAK/SEL_RES + // xxxx xxxx x00x x0xx: Identifies tag Type 2 platform + // xxxx xxxx x01x x0xx: Identifies tag Type 4 platform + jshort sakS = nfca.callMethod<jshort>("getSak"); + if ((sakS & 0x0064) == 0x0000) + return NfcTagType2; + else if ((sakS & 0x0064) == 0x0020) + return NfcTagType4; + return ProprietaryTag; + } else if (m_techList.contains(QStringLiteral(NFCBTECHNOLOGY))) { + return NfcTagType4; + } else if (m_techList.contains(QStringLiteral(NFCFTECHNOLOGY))) { + return NfcTagType3; + } + + return ProprietaryTag; +} + +void NearFieldTarget::setupTargetCheckTimer() +{ + m_targetCheckTimer = new QTimer(this); + m_targetCheckTimer->setInterval(1000); + connect(m_targetCheckTimer, SIGNAL(timeout()), this, SLOT(checkIsTargetLost())); + m_targetCheckTimer->start(); +} + +void NearFieldTarget::handleTargetLost() +{ + releaseIntent(); + emit targetLost(this); +} + +QAndroidJniObject NearFieldTarget::getTagTechnology(const QString &tech) const +{ + QString techClass(tech); + techClass.replace(QLatin1Char('.'), QLatin1Char('/')); + + // Getting requested technology + QAndroidJniObject tag = AndroidNfc::getTag(m_intent); + const QString sig = QString::fromUtf8("(Landroid/nfc/Tag;)L%1;"); + QAndroidJniObject tagtech = QAndroidJniObject::callStaticObjectMethod(techClass.toUtf8().constData(), "get", + sig.arg(techClass).toUtf8().constData(), tag.object<jobject>()); + return tagtech; +} + +QByteArray NearFieldTarget::jbyteArrayToQByteArray(const jbyteArray &byteArray) const +{ + QAndroidJniEnvironment env; + QByteArray resultArray; + jsize len = env->GetArrayLength(byteArray); + resultArray.resize(len); + env->GetByteArrayRegion(byteArray, 0, len, reinterpret_cast<jbyte*>(resultArray.data())); + return resultArray; +} + +bool NearFieldTarget::catchJavaExceptions() const +{ + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return true; + } + return false; +} diff --git a/src/nfc/qnearfieldtarget_android_p.h b/src/nfc/qnearfieldtarget_android_p.h new file mode 100644 index 00000000..ef9b5fdc --- /dev/null +++ b/src/nfc/qnearfieldtarget_android_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Centria research and development +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtNfc module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNEARFIELDTARGET_ANDROID_P_H +#define QNEARFIELDTARGET_ANDROID_P_H + +#include "android/androidjninfc_p.h" +#include "qnearfieldtarget.h" +#include "qnearfieldtarget_p.h" +#include "qndefmessage.h" +#include "qlist.h" +#include "qstringlist.h" +#include <QTimer> + +#include <QtAndroidExtras/QAndroidJniObject> +#include <QtAndroidExtras/QAndroidJniEnvironment> + +QT_BEGIN_NAMESPACE + +class NearFieldTarget : public QNearFieldTarget +{ + Q_OBJECT +public: + NearFieldTarget(QAndroidJniObject intent, + const QByteArray uid, + QObject *parent = 0); + virtual ~NearFieldTarget(); + virtual QByteArray uid() const; + virtual Type type() const; + virtual AccessMethods accessMethods() const; + virtual bool hasNdefMessage(); + virtual RequestId readNdefMessages(); + virtual RequestId sendCommand(const QByteArray &command); + virtual RequestId sendCommands(const QList<QByteArray> &commands); + virtual RequestId writeNdefMessages(const QList<QNdefMessage> &messages); + void setIntent(QAndroidJniObject intent); + +signals: + void targetDestroyed(const QByteArray &tagId); + void targetLost(QNearFieldTarget *target); + void ndefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id); + +protected slots: + void checkIsTargetLost(); + +protected: + void releaseIntent(); + void updateTechList(); + void updateType(); + Type getTagType() const; + void setupTargetCheckTimer(); + void handleTargetLost(); + QAndroidJniObject getTagTechnology(const QString &tech) const; + QByteArray jbyteArrayToQByteArray(const jbyteArray &byteArray) const; + bool catchJavaExceptions() const; + +protected: + QAndroidJniObject m_intent; + QByteArray m_uid; + QStringList m_techList; + Type m_type; + QTimer *m_targetCheckTimer; +}; + +QT_END_NAMESPACE + +#endif // QNEARFIELDTARGET_ANDROID_P_H |