summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--.qmake.conf2
-rw-r--r--examples/bluetooth/bluetooth.pro2
-rw-r--r--examples/bluetooth/btchat/chatserver.cpp1
-rw-r--r--examples/bluetooth/heartlistener/assets/blue_heart.pngbin7283 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/assets/blue_heart_small.pngbin250 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/assets/busy_dark.pngbin1130 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/assets/draw.js74
-rw-r--r--examples/bluetooth/heartlistener/assets/home.qml186
-rw-r--r--examples/bluetooth/heartlistener/assets/monitor.qml154
-rw-r--r--examples/bluetooth/heartlistener/assets/results.qml291
-rw-r--r--examples/bluetooth/heartlistener/assets/star.pngbin262 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/images/heartratefound.pngbin26494 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/images/heartratemonitor.pngbin46437 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/images/heartrateresults.pngbin45087 -> 0 bytes
-rw-r--r--examples/bluetooth/heartlistener/heartlistener.pro20
-rw-r--r--examples/bluetooth/heartlistener/heartrate.cpp445
-rw-r--r--examples/bluetooth/heartlistener/resources.qrc16
-rw-r--r--examples/bluetooth/heartrate-game/README.md7
-rw-r--r--examples/bluetooth/heartrate-game/bluetoothbaseclass.cpp77
-rw-r--r--examples/bluetooth/heartrate-game/bluetoothbaseclass.h72
-rw-r--r--examples/bluetooth/heartrate-game/connectionhandler.cpp82
-rw-r--r--examples/bluetooth/heartrate-game/connectionhandler.h73
-rw-r--r--examples/bluetooth/heartrate-game/devicefinder.cpp167
-rw-r--r--examples/bluetooth/heartrate-game/devicefinder.h92
-rw-r--r--examples/bluetooth/heartrate-game/devicehandler.cpp354
-rw-r--r--examples/bluetooth/heartrate-game/devicehandler.h (renamed from examples/bluetooth/heartlistener/heartrate.h)139
-rw-r--r--examples/bluetooth/heartrate-game/deviceinfo.cpp (renamed from examples/bluetooth/heartlistener/deviceinfo.cpp)21
-rw-r--r--examples/bluetooth/heartrate-game/deviceinfo.h (renamed from examples/bluetooth/heartlistener/deviceinfo.h)10
-rw-r--r--examples/bluetooth/heartrate-game/doc/images/heartgame-result.pngbin0 -> 9468 bytes
-rw-r--r--examples/bluetooth/heartrate-game/doc/images/heartgame-running.pngbin0 -> 10951 bytes
-rw-r--r--examples/bluetooth/heartrate-game/doc/images/heartgame-search.pngbin0 -> 14591 bytes
-rw-r--r--examples/bluetooth/heartrate-game/doc/images/heartgame-start.pngbin0 -> 13214 bytes
-rw-r--r--examples/bluetooth/heartrate-game/doc/src/heartrate-game.qdoc (renamed from examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc)37
-rw-r--r--examples/bluetooth/heartrate-game/heartrate-game.pro29
-rw-r--r--examples/bluetooth/heartrate-game/heartrate-global.h (renamed from examples/bluetooth/heartlistener/assets/Point.qml)20
-rw-r--r--examples/bluetooth/heartrate-game/images.qrc7
-rw-r--r--examples/bluetooth/heartrate-game/main.cpp (renamed from examples/bluetooth/heartlistener/main.cpp)34
-rw-r--r--examples/bluetooth/heartrate-game/qml.qrc18
-rw-r--r--examples/bluetooth/heartrate-game/qml/App.qml120
-rw-r--r--examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml112
-rw-r--r--examples/bluetooth/heartrate-game/qml/BottomLine.qml (renamed from examples/bluetooth/heartlistener/assets/dialog.qml)24
-rw-r--r--examples/bluetooth/heartrate-game/qml/Connect.qml178
-rw-r--r--examples/bluetooth/heartrate-game/qml/GameButton.qml78
-rw-r--r--examples/bluetooth/heartrate-game/qml/GamePage.qml83
-rw-r--r--examples/bluetooth/heartrate-game/qml/GameSettings.qml91
-rw-r--r--examples/bluetooth/heartrate-game/qml/Measure.qml234
-rw-r--r--examples/bluetooth/heartrate-game/qml/SplashScreen.qml80
-rw-r--r--examples/bluetooth/heartrate-game/qml/Stats.qml (renamed from examples/bluetooth/heartlistener/assets/main.qml)77
-rw-r--r--examples/bluetooth/heartrate-game/qml/StatsLabel.qml (renamed from examples/bluetooth/heartlistener/assets/Button.qml)53
-rw-r--r--examples/bluetooth/heartrate-game/qml/TitleBar.qml87
-rw-r--r--examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.pngbin0 -> 6143 bytes
-rw-r--r--examples/bluetooth/heartrate-game/qml/images/heart.pngbin0 -> 2664 bytes
-rw-r--r--examples/bluetooth/heartrate-game/qml/images/logo.pngbin0 -> 31915 bytes
-rw-r--r--examples/bluetooth/heartrate-game/qml/main.qml95
-rw-r--r--examples/bluetooth/heartrate-game/qml/qmldir1
-rw-r--r--examples/bluetooth/heartrate-server/doc/src/heartrate-server.qdoc2
-rw-r--r--examples/bluetooth/heartrate-server/main.cpp2
-rw-r--r--src/android/bluetooth/bluetooth.pri5
-rw-r--r--src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java28
-rw-r--r--src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java585
-rw-r--r--src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java39
-rw-r--r--src/bluetooth/android/jni_android.cpp16
-rw-r--r--src/bluetooth/android/lowenergynotificationhub.cpp74
-rw-r--r--src/bluetooth/android/lowenergynotificationhub_p.h11
-rw-r--r--src/bluetooth/bluetooth.pro23
-rw-r--r--src/bluetooth/configure.json4
-rw-r--r--src/bluetooth/doc/src/bluetooth-index.qdoc26
-rw-r--r--src/bluetooth/doc/src/bluetooth-le-overview.qdoc27
-rw-r--r--src/bluetooth/doc/src/examples.qdoc2
-rw-r--r--src/bluetooth/osx/osxbtledeviceinquiry.mm9
-rw-r--r--src/bluetooth/qbluetooth.cpp2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.h2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h6
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp19
-rw-r--r--src/bluetooth/qbluetoothglobal.h1
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_bluez.cpp4
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.cpp10
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h28
-rw-r--r--src/bluetooth/qbluetoothserver.cpp3
-rw-r--r--src/bluetooth/qbluetoothserver.h2
-rw-r--r--src/bluetooth/qbluetoothserver_p.h26
-rw-r--r--src/bluetooth/qbluetoothserver_winrt.cpp255
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.cpp6
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.h4
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp10
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h29
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp607
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_p.h24
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_winrt.cpp450
-rw-r--r--src/bluetooth/qbluetoothsocket.cpp27
-rw-r--r--src/bluetooth/qbluetoothsocket_bluez.cpp2
-rw-r--r--src/bluetooth/qbluetoothsocket_p.h51
-rw-r--r--src/bluetooth/qbluetoothsocket_winrt.cpp668
-rw-r--r--src/bluetooth/qbluetoothtransfermanager.cpp8
-rw-r--r--src/bluetooth/qleadvertiser_p.h6
-rw-r--r--src/bluetooth/qlowenergyadvertisingdata.cpp9
-rw-r--r--src/bluetooth/qlowenergyconnectionparameters.cpp29
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp37
-rw-r--r--src/bluetooth/qlowenergycontroller_android.cpp711
-rw-r--r--src/bluetooth/qlowenergycontroller_bluez.cpp3
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h19
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h10
-rw-r--r--src/imports/bluetooth/plugin.cpp6
-rw-r--r--src/imports/bluetooth/plugins.qmltypes4
-rw-r--r--src/imports/nfc/plugin.cpp4
-rw-r--r--src/imports/nfc/plugins.qmltypes4
-rw-r--r--src/nfc/android/androidjninfc.cpp1
-rw-r--r--src/nfc/nfc.pro7
-rw-r--r--src/nfc/qnearfieldmanager_android.cpp4
-rw-r--r--src/nfc/qnearfieldtarget.cpp87
-rw-r--r--src/nfc/qnearfieldtarget.h13
-rw-r--r--src/nfc/qnearfieldtarget_android.cpp298
-rw-r--r--src/nfc/qnearfieldtarget_android_p.cpp71
-rw-r--r--src/nfc/qnearfieldtarget_android_p.h9
-rw-r--r--src/nfc/qnearfieldtarget_emulator.cpp15
-rw-r--r--src/nfc/qnearfieldtarget_neard_p.cpp68
-rw-r--r--src/nfc/qnearfieldtarget_p.cpp68
-rw-r--r--src/nfc/qnearfieldtarget_p.h13
-rw-r--r--src/nfc/targetemulator.cpp13
-rw-r--r--src/src.pro4
-rw-r--r--tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro4
-rw-r--r--tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp6
-rw-r--r--tests/auto/qbluetoothsocket/qbluetoothsocket.pro2
-rw-r--r--tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp2
125 files changed, 6646 insertions, 1731 deletions
diff --git a/.gitignore b/.gitignore
index 830bb63b..1b9be3d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,7 +39,8 @@ examples/bluetooth/btchat/btchat
examples/bluetooth/btfiletransfer/btfiletransfer
examples/bluetooth/btscanner/btscanner
examples/bluetooth/chat/qml_chat
-examples/bluetooth/heartlistener/heartlistener
+examples/bluetooth/heartrate-game/heartrate-game
+examples/bluetooth/heartrate-server/heartrate-server
examples/bluetooth/lowenergyscanner/lowenergyscanner
examples/bluetooth/pingpong/pingpong
examples/bluetooth/picturetransfer/qml_picturetransfer
@@ -48,8 +49,15 @@ examples/nfc/annotatedurl/annotatedurl
examples/nfc/ndefeditor/ndefeditor
examples/nfc/poster/qml_poster
examples/nfc/corkboard/qml_corkboard
+src/bluetooth/codeattributions.qdoc
src/bluetooth/doc/snippets/bluetooth_cppsnippet
+src/bluetooth/qtattributionsscanner_wrapper.sh
+src/bluetooth/qtbluetooth-config.h
+src/bluetooth/qtbluetooth-config.pri
+src/bluetooth/qtbluetooth-config_p.h
+src/nfc/codeattributions.qdoc
src/nfc/doc/snippets/nfc_cppsnippet
+src/nfc/qtattributionsscanner_wrapper.sh
tests/auto/cmake/build
tests/auto/qbluetoothaddress/tst_qbluetoothaddress
tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent
diff --git a/.qmake.conf b/.qmake.conf
index fec66b73..b1c22d3b 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,3 +1,3 @@
load(qt_build_config)
-MODULE_VERSION = 5.8.1
+MODULE_VERSION = 5.9.0
diff --git a/examples/bluetooth/bluetooth.pro b/examples/bluetooth/bluetooth.pro
index 1c09dade..bc18f571 100644
--- a/examples/bluetooth/bluetooth.pro
+++ b/examples/bluetooth/bluetooth.pro
@@ -12,5 +12,5 @@ qtHaveModule(quick): SUBDIRS += scanner \
picturetransfer \
pingpong \
lowenergyscanner \
- heartlistener \
+ heartrate-game \
chat
diff --git a/examples/bluetooth/btchat/chatserver.cpp b/examples/bluetooth/btchat/chatserver.cpp
index 6b21a507..dc1cda57 100644
--- a/examples/bluetooth/btchat/chatserver.cpp
+++ b/examples/bluetooth/btchat/chatserver.cpp
@@ -85,7 +85,6 @@ void ChatServer::startServer(const QBluetoothAddress& localAdapter)
classId.prepend(QVariant::fromValue(QBluetoothUuid(serviceUuid)));
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
- serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,classId);
//! [Class Uuuid must contain at least 1 entry]
diff --git a/examples/bluetooth/heartlistener/assets/blue_heart.png b/examples/bluetooth/heartlistener/assets/blue_heart.png
deleted file mode 100644
index 997ee699..00000000
--- a/examples/bluetooth/heartlistener/assets/blue_heart.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/assets/blue_heart_small.png b/examples/bluetooth/heartlistener/assets/blue_heart_small.png
deleted file mode 100644
index 7bd1f981..00000000
--- a/examples/bluetooth/heartlistener/assets/blue_heart_small.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/assets/busy_dark.png b/examples/bluetooth/heartlistener/assets/busy_dark.png
deleted file mode 100644
index 3a105953..00000000
--- a/examples/bluetooth/heartlistener/assets/busy_dark.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/assets/draw.js b/examples/bluetooth/heartlistener/assets/draw.js
deleted file mode 100644
index f884d472..00000000
--- a/examples/bluetooth/heartlistener/assets/draw.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/***************************************************************************
-**
-** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-var component;
-var size = 0;
-var counter = 0;
-var difference = 0;
-
-function start() {
- size = heartRate.measurementsSize();
- difference = (plot.width-topbar.width)/size;
- console.log(size +" "+ plot.width);
- for (var i = 0; i< size; i++) {
- var value = heartRate.measurements(i);
-
- drawIt(value);
- counter++;
- }
-}
-
-function drawIt(value) {
- if (component == null)
- component = Qt.createComponent("Point.qml");
- if (component.status == Component.Ready) {
- var dynamicObject = component.createObject(plot);
- if (dynamicObject == null) {
- console.log("error creating block");
- console.log(component.errorString());
- return false;
- }
- dynamicObject.x = 10+(counter*difference);
- console.log(plot.height)
- dynamicObject.y = plot.height -value;
-
- }
-}
-
diff --git a/examples/bluetooth/heartlistener/assets/home.qml b/examples/bluetooth/heartlistener/assets/home.qml
deleted file mode 100644
index e77081ae..00000000
--- a/examples/bluetooth/heartlistener/assets/home.qml
+++ /dev/null
@@ -1,186 +0,0 @@
-/***************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.0
-
-Rectangle {
- id: screen
- color: "#F0EBED"
- property string message: heartRate.message
- onMessageChanged: {
- if (heartRate.message != "Scanning for devices..." && heartRate.message != "Low Energy device found. Scanning for more...") {
- background.visible = false;
- demoMode.visible = true;
- }
- else {
- demoMode.visible = false;
- background.visible = true;
- }
- }
-
- Rectangle {
- id:select
- width: parent.width
- anchors.top: parent.top
- height: 80
- color: "#F0EBED"
- border.color: "#3870BA"
- border.width: 2
- radius: 10
-
- Text {
- id: selectText
- color: "#3870BA"
- font.pixelSize: 34
- anchors.centerIn: parent
- text: "Select Device"
- }
- }
-
- Rectangle {
- id: spinner
- width: parent.width
- anchors.top: select.bottom
- anchors.bottom: demoMode.top
- visible: false
- color: "#F0EBED"
- z: 100
-
- Rectangle {
- id: inside
- anchors.centerIn: parent
- Image {
- id: background
-
- width:100
- height:100
- anchors.horizontalCenter: parent.horizontalCenter
-
- source: "busy_dark.png"
- fillMode: Image.PreserveAspectFit
- NumberAnimation on rotation { duration: 3000; from:0; to: 360; loops: Animation.Infinite}
- }
-
- Text {
- id: infotext
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.top: background.bottom
- text: heartRate.message
- color: "#8F8F8F"
- }
- }
- }
-
- Component.onCompleted: {
- heartRate.deviceSearch();
- spinner.visible=true;
- }
-
- ListView {
- id: theListView
- width: parent.width
- onModelChanged: spinner.visible=false
- anchors.top: select.bottom
- anchors.bottom: demoMode.top
- model: heartRate.name
-
- delegate: Rectangle {
- id: box
- height:140
- width: parent.width
- color: "#3870BA"
- border.color: "#F0EBED"
- border.width: 5
- radius: 15
-
- MouseArea {
- anchors.fill: parent
- onPressed: { box.color= "#3265A7"; box.height=110}
- onClicked: {
- heartRate.connectToService(modelData.deviceAddress);
- pageLoader.source="monitor.qml";
- }
- }
-
- Text {
- id: device
- font.pixelSize: 30
- text: modelData.deviceName
- anchors.top: parent.top
- anchors.topMargin: 5
- anchors.horizontalCenter: parent.horizontalCenter
- color: "#F0EBED"
- }
-
- Text {
- id: deviceAddress
- font.pixelSize: 30
- text: modelData.deviceAddress
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 5
- anchors.horizontalCenter: parent.horizontalCenter
- color: "#F0EBED"
- }
- }
- }
-
- Button {
- id:demoMode
- buttonWidth: parent.width
- buttonHeight: 0.1*parent.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: scanAgain.top
- text: "Run Demo"
- onButtonClick: {
- heartRate.startDemo();
- pageLoader.source="monitor.qml";
- }
- }
-
- Button {
- id:scanAgain
- buttonWidth: parent.width
- buttonHeight: 0.1*parent.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
- text: "Menu"
- onButtonClick: pageLoader.source="main.qml"
- }
-}
diff --git a/examples/bluetooth/heartlistener/assets/monitor.qml b/examples/bluetooth/heartlistener/assets/monitor.qml
deleted file mode 100644
index 1d74ad84..00000000
--- a/examples/bluetooth/heartlistener/assets/monitor.qml
+++ /dev/null
@@ -1,154 +0,0 @@
-/***************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.0
-import QtQuick.Particles 2.0
-
-Rectangle {
- id: screenMonitor
- color: "#F0EBED"
-
- Button {
- id:menu
- buttonWidth: parent.width
- buttonHeight: 0.1 * parent.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.top: parent.top
- text: "Menu"
- onButtonClick: {
- heartRate.disconnectService();
- pageLoader.source="home.qml";
- }
- }
-
- Text {
- id: hrValue
- font.pointSize: 24; font.bold: true
- anchors.top:menu.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 25
-
- color: "#3870BA"
- text: heartRate.hr
- onTextChanged: {
- if (heartRate.hr > 0 && updatei != null && heartRate.numDevices() > 0) {
- updatei.destroy()
- }
- }
- }
-
- Rectangle {
- id: updatei
- width: parent.width
- height: 80
- anchors.bottom: stop.top
-
- color: "#F0EBED"
- border.color: "#3870BA"
- border.width: 2
-
- Text {
- id: logi
- text: heartRate.message
- anchors.centerIn: updatei
- color: "#3870BA"
- }
- }
-
- Image {
- id: background
- width: 300
- height: width
- anchors.centerIn: parent
- source: "blue_heart.png"
- fillMode: Image.PreserveAspectFit
- NumberAnimation on width {
- running: heartRate.hr > 0;
- duration: heartRate.hr/60*250;
- from:300; to: 350;
- loops: Animation.Infinite;
- }
-
- ParticleSystem {
- id: systwo
- anchors.fill: parent
-
- ImageParticle {
- system: systwo
- id: cptwo
- source: "star.png"
- colorVariation: 0.4
- color: "#000000FF"
- }
-
- Emitter {
- //burst on click
- id: burstytwo
- system: systwo
- enabled: true
- x: 160
- y: 150
- emitRate: heartRate.hr*100
- maximumEmitted: 4000
- acceleration: AngleDirection {angleVariation: 360; magnitude: 360; }
- size: 4
- endSize: 8
- sizeVariation: 4
- }
-
-
- }
-
- }
-
- Button {
- id:stop
- buttonWidth: parent.width
- buttonHeight: 0.1*parent.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
- text: "Stop Monitoring"
- onButtonClick: {
- burstytwo.enabled = false;
- heartRate.disconnectService();
- pageLoader.source = "results.qml";
- }
- }
-}
diff --git a/examples/bluetooth/heartlistener/assets/results.qml b/examples/bluetooth/heartlistener/assets/results.qml
deleted file mode 100644
index 301762c3..00000000
--- a/examples/bluetooth/heartlistener/assets/results.qml
+++ /dev/null
@@ -1,291 +0,0 @@
-/***************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.0
-import "draw.js" as DrawGraph
-
-Rectangle {
- id: results
- color: "#F0EBED"
-
- Component.onCompleted: heartRate.obtainResults()
-
- function getTime() {
- var t = heartRate.time;
- var min = Math.floor(t/60);
- var sec = t%60;
- var r = min + " min " + sec + " sec ";
- return r;
- }
-
- function drawGraph() {
- var b = plot.height/200;
- var ctx = canvas1.getContext('2d');
- ctx.beginPath()
- ctx.moveTo(10, plot.height- (b*60))
- var size = heartRate.measurementsSize();
- var difference = (plot.width-topbar.width)/size;
-
- for (var i = 0; i< size; i++) {
- var value = heartRate.measurements(i);
- if (i == 0) {
- ctx.moveTo(10+2, (plot.height- (value*b) + 2));
- ctx.rect((10 + i*difference), (plot.height- (value*b)), 4, 4);
-
- }
- else {
- ctx.lineTo((10+2 + i*difference), (plot.height- (value*b) + 2));
- ctx.rect((10 + i*difference), (plot.height- (value*b)), 4, 4);
- }
-
- }
- ctx.fillStyle = "#3870BA"
- ctx.fill()
- ctx.strokeStyle = "#3870BA"
- ctx.stroke()
- ctx.closePath()
- }
-
- Rectangle {
- id: res
- width: parent.width
- anchors.top: parent.top
- height: 80
- color: "#F0EBED"
- border.color: "#3870BA"
- border.width: 2
- radius: 10
- Text {
- id: restText
- color: "#3870BA"
- font.pixelSize: 34
- anchors.centerIn: parent
- text: "Results"
- }
- }
-
- Text {
- id: topbar
- text: "200"
- anchors.left: parent.left
- anchors.top: res.bottom
- anchors.rightMargin: 4
- color: "#3870BA"
- z: 50
- }
-
- Rectangle {
- id: level
- anchors.left: topbar.right
-
- anchors.top: res.bottom
- height: ((results.height -(res.height + menuLast.height + start.height ))/2)
- width: 3
- color: "#3870BA"
- }
-
- Text {
- id: middlebar
- anchors.verticalCenter: level.verticalCenter
- anchors.left: parent.left
- text: "100"
- color: "#3870BA"
- z: 50
- }
-
- Rectangle{
- id: downlevel
- anchors.bottom: cover.top
- width: parent.width
- anchors.left: level.right
- height: 3
- color: "#3870BA"
- z: 50
- }
-
- Rectangle {
- id: plot
- anchors.left: level.right
- anchors.leftMargin: 15
- width: results.width
- height: ((parent.height-(res.height+menuLast.height+start.height))/2)
-
- anchors.top: res.bottom
- color: "#F0EBED"
- Canvas {
- id: canvas1
- anchors.fill: parent
- z: 150
- onPaint: drawGraph()
- }
- }
-
- Rectangle {
- id: cover
- anchors.top: plot.bottom
- anchors.bottom: menuLast.top
- width: parent.width
- height: ((parent.height-(res.height+menuLast.height+start.height))/2)
- color: "#F0EBED"
- radius: 10
- border.color: "#3870BA"
- border.width: 2
-
- Flickable {
- id: scroll
- anchors.fill: parent
- anchors.margins: 5
- clip: true
- contentWidth: parent.width
- contentHeight: stresult.height
-
- Rectangle {
- id: stresult
- width: parent.width
- height: (results.height - (res.height + menuLast.height + start.height - 100))
- color: "#F0EBED"
- radius: 10
-
- Text {
- id: averageHR
- font.pixelSize: 30;
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.top: parent.top
-
- color: "#3870BA"
- text: "Average Heart Rate"
- }
-
- Text {
- id: averageHRt
- font.pixelSize: 40; font.bold: true
- anchors.top: averageHR.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 10
- color: "#3870BA"
- text: heartRate.average
- }
-
- Text {
- id: time
- font.pixelSize: 30;
- anchors.top: averageHRt.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 10
- color: "#3870BA"
- text: "Seconds measured:"
- }
-
- Text {
- id: timet
- font.pixelSize: 40; font.bold: true
- anchors.top: time.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 10
- color: "#3870BA"
- text: getTime()
- }
- Text {
- id: maxi
- font.pixelSize: 30;
- anchors.top: timet.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 20
- color: "#3870BA"
- text: " Max || Min "
- }
-
- Text {
- id: mini
- font.pixelSize: 40; font.bold: true
- anchors.top:maxi.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 10
- color: "#3870BA"
- text: " " + heartRate.maxHR + " || " + heartRate.minHR
- }
-
- Text {
- id: calories
- font.pixelSize: 30;
- anchors.top: mini.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 10
- color: "#3870BA"
- text: " Calories "
- }
-
- Text {
- id: caloriestext
- font.pixelSize: 40; font.bold: true
- anchors.top:calories.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.topMargin: 10
- color: "#3870BA"
- text: heartRate.calories.toFixed(3)
- }
- }
- }
- }
-
- Button {
- id:menuLast
- buttonWidth: parent.width
- buttonHeight: 0.1*parent.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: start.top
- text: "Menu"
- onButtonClick: { pageLoader.source="main.qml"}
- }
-
- Button {
- id:start
- buttonWidth: parent.width
- buttonHeight: 0.1*parent.height
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
- text: "Start Monitoring"
- onButtonClick: {
- heartRate.connectToService(heartRate.deviceAddress());
- pageLoader.source="monitor.qml";
- }
- }
-}
diff --git a/examples/bluetooth/heartlistener/assets/star.png b/examples/bluetooth/heartlistener/assets/star.png
deleted file mode 100644
index defbde53..00000000
--- a/examples/bluetooth/heartlistener/assets/star.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/images/heartratefound.png b/examples/bluetooth/heartlistener/doc/images/heartratefound.png
deleted file mode 100644
index 89517aa4..00000000
--- a/examples/bluetooth/heartlistener/doc/images/heartratefound.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/images/heartratemonitor.png b/examples/bluetooth/heartlistener/doc/images/heartratemonitor.png
deleted file mode 100644
index ed51ba86..00000000
--- a/examples/bluetooth/heartlistener/doc/images/heartratemonitor.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/images/heartrateresults.png b/examples/bluetooth/heartlistener/doc/images/heartrateresults.png
deleted file mode 100644
index 8180d1a4..00000000
--- a/examples/bluetooth/heartlistener/doc/images/heartrateresults.png
+++ /dev/null
Binary files differ
diff --git a/examples/bluetooth/heartlistener/heartlistener.pro b/examples/bluetooth/heartlistener/heartlistener.pro
deleted file mode 100644
index 7856b64e..00000000
--- a/examples/bluetooth/heartlistener/heartlistener.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-TEMPLATE = app
-TARGET = heartlistener
-
-QT += quick bluetooth
-
-# Input
-HEADERS += deviceinfo.h \
- heartrate.h
-SOURCES += deviceinfo.cpp \
- heartrate.cpp \
- main.cpp
-
-OTHER_FILES += assets/*.qml \
- assets/*.js
-
-RESOURCES += \
- resources.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/heartlistener
-INSTALLS += target
diff --git a/examples/bluetooth/heartlistener/heartrate.cpp b/examples/bluetooth/heartlistener/heartrate.cpp
deleted file mode 100644
index c206b69e..00000000
--- a/examples/bluetooth/heartlistener/heartrate.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-/***************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "heartrate.h"
-
-#include <QtEndian>
-
-HeartRate::HeartRate():
- m_currentDevice(QBluetoothDeviceInfo()), foundHeartRateService(false),
- m_max(0), m_min(0), calories(0), m_control(0), timer(0),
- m_service(0)
-{
- //! [devicediscovery-1]
- m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
- m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(5000);
-
- connect(m_deviceDiscoveryAgent, SIGNAL(deviceDiscovered(const QBluetoothDeviceInfo&)),
- this, SLOT(addDevice(const QBluetoothDeviceInfo&)));
- connect(m_deviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
- this, SLOT(deviceScanError(QBluetoothDeviceDiscoveryAgent::Error)));
- connect(m_deviceDiscoveryAgent, SIGNAL(finished()), this, SLOT(scanFinished()));
- //! [devicediscovery-1]
-
- // initialize random seed for demo mode
- qsrand(QTime::currentTime().msec());
-}
-
-HeartRate::~HeartRate()
-{
- qDeleteAll(m_devices);
- m_devices.clear();
-}
-
-void HeartRate::deviceSearch()
-{
- qDeleteAll(m_devices);
- m_devices.clear();
- //! [devicediscovery-2]
- m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
- //! [devicediscovery-2]
- setMessage("Scanning for devices...");
-}
-
-//! [devicediscovery-3]
-void HeartRate::addDevice(const QBluetoothDeviceInfo &device)
-{
- if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
- qWarning() << "Discovered LE Device name: " << device.name() << " Address: "
- << device.address().toString();
-//! [devicediscovery-3]
- DeviceInfo *dev = new DeviceInfo(device);
- m_devices.append(dev);
- setMessage("Low Energy device found. Scanning for more...");
-//! [devicediscovery-4]
- }
- //...
-}
-//! [devicediscovery-4]
-
-void HeartRate::scanFinished()
-{
- if (m_devices.size() == 0)
- setMessage("No Low Energy devices found");
- Q_EMIT nameChanged();
-}
-
-void HeartRate::deviceScanError(QBluetoothDeviceDiscoveryAgent::Error error)
-{
- if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError)
- setMessage("The Bluetooth adaptor is powered off, power it on before doing discovery.");
- else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError)
- setMessage("Writing or reading from the device resulted in an error.");
- else
- setMessage("An unknown error has occurred.");
-}
-
-
-void HeartRate::setMessage(QString message)
-{
- m_info = message;
- Q_EMIT messageChanged();
-}
-
-QString HeartRate::message() const
-{
- return m_info;
-}
-
-QVariant HeartRate::name()
-{
- return QVariant::fromValue(m_devices);
-}
-
-void HeartRate::connectToService(const QString &address)
-{
- m_measurements.clear();
-
- bool deviceFound = false;
- for (int i = 0; i < m_devices.size(); i++) {
- if (((DeviceInfo*)m_devices.at(i))->getAddress() == address ) {
- m_currentDevice.setDevice(((DeviceInfo*)m_devices.at(i))->getDevice());
- setMessage("Connecting to device...");
- deviceFound = true;
- break;
- }
- }
- // we are running demo mode
- if (!deviceFound) {
- startDemo();
- return;
- }
-
- if (m_control) {
- m_control->disconnectFromDevice();
- delete m_control;
- m_control = 0;
-
- }
- //! [Connect signals]
- m_control = new QLowEnergyController(m_currentDevice.getDevice(), this);
- connect(m_control, SIGNAL(serviceDiscovered(QBluetoothUuid)),
- this, SLOT(serviceDiscovered(QBluetoothUuid)));
- connect(m_control, SIGNAL(discoveryFinished()),
- this, SLOT(serviceScanDone()));
- connect(m_control, SIGNAL(error(QLowEnergyController::Error)),
- this, SLOT(controllerError(QLowEnergyController::Error)));
- connect(m_control, SIGNAL(connected()),
- this, SLOT(deviceConnected()));
- connect(m_control, SIGNAL(disconnected()),
- this, SLOT(deviceDisconnected()));
-
- m_control->connectToDevice();
- //! [Connect signals]
-}
-
-//! [Connecting to service]
-
-void HeartRate::deviceConnected()
-{
- m_control->discoverServices();
-}
-
-void HeartRate::deviceDisconnected()
-{
- setMessage("Heart Rate service disconnected");
- qWarning() << "Remote device disconnected";
-}
-
-//! [Connecting to service]
-
-//! [Filter HeartRate service 1]
-void HeartRate::serviceDiscovered(const QBluetoothUuid &gatt)
-{
- if (gatt == QBluetoothUuid(QBluetoothUuid::HeartRate)) {
- setMessage("Heart Rate service discovered. Waiting for service scan to be done...");
- foundHeartRateService = true;
- }
-}
-//! [Filter HeartRate service 1]
-
-void HeartRate::serviceScanDone()
-{
- delete m_service;
- m_service = 0;
-
- //! [Filter HeartRate service 2]
- if (foundHeartRateService) {
- setMessage("Connecting to service...");
- m_service = m_control->createServiceObject(
- QBluetoothUuid(QBluetoothUuid::HeartRate), this);
- }
-
- if (!m_service) {
- setMessage("Heart Rate Service not found.");
- return;
- }
-
- connect(m_service, SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
- this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState)));
- connect(m_service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),
- this, SLOT(updateHeartRateValue(QLowEnergyCharacteristic,QByteArray)));
- connect(m_service, SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray)),
- this, SLOT(confirmedDescriptorWrite(QLowEnergyDescriptor,QByteArray)));
-
- m_service->discoverDetails();
- //! [Filter HeartRate service 2]
-}
-
-void HeartRate::disconnectService()
-{
- foundHeartRateService = false;
- m_stop = QDateTime::currentDateTime();
-
- if (m_devices.isEmpty()) {
- if (timer)
- timer->stop();
- return;
- }
-
- //disable notifications before disconnecting
- if (m_notificationDesc.isValid() && m_service
- && m_notificationDesc.value() == QByteArray::fromHex("0100"))
- {
- m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0000"));
- } else {
- m_control->disconnectFromDevice();
- delete m_service;
- m_service = 0;
- }
-}
-
-//! [Error handling]
-void HeartRate::controllerError(QLowEnergyController::Error error)
-{
- setMessage("Cannot connect to remote device.");
- qWarning() << "Controller Error:" << error;
-}
-//! [Error handling]
-
-
-//! [Find HRM characteristic]
-void HeartRate::serviceStateChanged(QLowEnergyService::ServiceState s)
-{
- switch (s) {
- case QLowEnergyService::ServiceDiscovered:
- {
- const QLowEnergyCharacteristic hrChar = m_service->characteristic(
- QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement));
- if (!hrChar.isValid()) {
- setMessage("HR Data not found.");
- break;
- }
-
- m_notificationDesc = hrChar.descriptor(
- QBluetoothUuid::ClientCharacteristicConfiguration);
- if (m_notificationDesc.isValid()) {
- m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
- setMessage("Measuring");
- m_start = QDateTime::currentDateTime();
- }
-
- break;
- }
- default:
- //nothing for now
- break;
- }
-}
-//! [Find HRM characteristic]
-
-void HeartRate::serviceError(QLowEnergyService::ServiceError e)
-{
- switch (e) {
- case QLowEnergyService::DescriptorWriteError:
- setMessage("Cannot obtain HR notifications");
- break;
- default:
- qWarning() << "HR service error:" << e;
- }
-}
-
-//! [Reading value 1]
-void HeartRate::updateHeartRateValue(const QLowEnergyCharacteristic &c,
- const QByteArray &value)
-{
- // ignore any other characteristic change -> shouldn't really happen though
- if (c.uuid() != QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement))
- return;
-
-
- const quint8 *data = reinterpret_cast<const quint8 *>(value.constData());
- quint8 flags = data[0];
-
- //Heart Rate
- if (flags & 0x1) { // HR 16 bit? otherwise 8 bit
- const quint16 heartRate = qFromLittleEndian<quint16>(data[1]);
- //qDebug() << "16 bit HR value:" << heartRate;
- m_measurements.append(heartRate);
- } else {
- const quint8 *heartRate = &data[1];
- m_measurements.append(*heartRate);
- //qDebug() << "8 bit HR value:" << *heartRate;
- }
-
- //Energy Expended
- if (flags & 0x8) {
- int index = (flags & 0x1) ? 5 : 3;
- const quint16 energy = qFromLittleEndian<quint16>(data[index]);
- qDebug() << "Used Energy:" << energy;
- }
- //! [Reading value 1]
-
- Q_EMIT hrChanged();
-//! [Reading value 2]
-}
-//! [Reading value 2]
-
-void HeartRate::confirmedDescriptorWrite(const QLowEnergyDescriptor &d,
- const QByteArray &value)
-{
- if (d.isValid() && d == m_notificationDesc && value == QByteArray::fromHex("0000")) {
- //disabled notifications -> assume disconnect intent
- m_control->disconnectFromDevice();
- delete m_service;
- m_service = 0;
- }
-}
-
-int HeartRate::hR() const
-{
- if (m_measurements.isEmpty())
- return 0;
- return m_measurements.last();
-}
-
-void HeartRate::obtainResults()
-{
- Q_EMIT timeChanged();
- Q_EMIT averageChanged();
- Q_EMIT caloriesChanged();
-}
-
-int HeartRate::time()
-{
- return m_start.secsTo(m_stop);
-}
-
-int HeartRate::maxHR() const
-{
- return m_max;
-}
-
-int HeartRate::minHR() const
-{
- return m_min;
-}
-
-float HeartRate::average()
-{
- if (m_measurements.size() == 0) {
- return 0;
- } else {
- m_max = 0;
- m_min = 1000;
- int sum = 0;
- for (int i = 0; i < m_measurements.size(); i++) {
- sum += (int) m_measurements.value(i);
- if (((int)m_measurements.value(i)) > m_max)
- m_max = (int)m_measurements.value(i);
- if (((int)m_measurements.value(i)) < m_min)
- m_min = (int)m_measurements.value(i);
- }
- return sum/m_measurements.size();
- }
-}
-
-int HeartRate::measurements(int index) const
-{
- if (index > m_measurements.size())
- return 0;
- else
- return (int)m_measurements.value(index);
-}
-
-int HeartRate::measurementsSize() const
-{
- return m_measurements.size();
-}
-
-QString HeartRate::deviceAddress() const
-{
- return m_currentDevice.getAddress();
-}
-
-float HeartRate::caloriesCalculation()
-{
- calories = ((-55.0969 + (0.6309 * average()) + (0.1988 * 94) + (0.2017 * 24)) / 4.184) * 60 * time()/3600 ;
- return calories;
-}
-
-int HeartRate::numDevices() const
-{
- return m_devices.size();
-}
-
-void HeartRate::startDemo()
-{
- m_start = QDateTime::currentDateTime();
- if (!timer) {
- timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()), this, SLOT(receiveDemo()));
- }
- timer->start(1000);
- setMessage("This is Demo mode");
-}
-
-void HeartRate::receiveDemo()
-{
- m_measurements.append(randomPulse());
- Q_EMIT hrChanged();
-}
-
-int HeartRate::randomPulse() const
-{
- // random number between 50 and 70
- return qrand() % (70 - 50) + 50;
-}
diff --git a/examples/bluetooth/heartlistener/resources.qrc b/examples/bluetooth/heartlistener/resources.qrc
deleted file mode 100644
index ac6f9c83..00000000
--- a/examples/bluetooth/heartlistener/resources.qrc
+++ /dev/null
@@ -1,16 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>assets/blue_heart.png</file>
- <file>assets/busy_dark.png</file>
- <file>assets/Button.qml</file>
- <file>assets/dialog.qml</file>
- <file>assets/draw.js</file>
- <file>assets/home.qml</file>
- <file>assets/main.qml</file>
- <file>assets/monitor.qml</file>
- <file>assets/Point.qml</file>
- <file>assets/results.qml</file>
- <file>assets/star.png</file>
- <file>assets/blue_heart_small.png</file>
- </qresource>
-</RCC>
diff --git a/examples/bluetooth/heartrate-game/README.md b/examples/bluetooth/heartrate-game/README.md
new file mode 100644
index 00000000..fd7c6fd6
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/README.md
@@ -0,0 +1,7 @@
+# HeartRateGame #
+
+Demonstrates how to check a Bluetooth-connection, discover LE-devices, connect
+to devices, discover services and finally connect to a heartrate-service.
+The purpose of the game is increase the heartrate so much as possible in 60s.
+Relax before starting the game. Don't be too nervous, it increases the heartrate!
+
diff --git a/examples/bluetooth/heartrate-game/bluetoothbaseclass.cpp b/examples/bluetooth/heartrate-game/bluetoothbaseclass.cpp
new file mode 100644
index 00000000..0d7f0bbe
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/bluetoothbaseclass.cpp
@@ -0,0 +1,77 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bluetoothbaseclass.h"
+
+BluetoothBaseClass::BluetoothBaseClass(QObject *parent) : QObject(parent)
+{
+}
+
+QString BluetoothBaseClass::error() const
+{
+ return m_error;
+}
+
+QString BluetoothBaseClass::info() const
+{
+ return m_info;
+}
+
+void BluetoothBaseClass::setError(const QString &error)
+{
+ if (m_error != error) {
+ m_error = error;
+ emit errorChanged();
+ }
+}
+
+void BluetoothBaseClass::setInfo(const QString &info)
+{
+ if (m_info != info) {
+ m_info = info;
+ emit infoChanged();
+ }
+}
+
+void BluetoothBaseClass::clearMessages()
+{
+ setInfo("");
+ setError("");
+}
diff --git a/examples/bluetooth/heartrate-game/bluetoothbaseclass.h b/examples/bluetooth/heartrate-game/bluetoothbaseclass.h
new file mode 100644
index 00000000..ccdb60e3
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/bluetoothbaseclass.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BLUETOOTHBASECLASS_H
+#define BLUETOOTHBASECLASS_H
+
+#include <QObject>
+
+class BluetoothBaseClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString error READ error WRITE setError NOTIFY errorChanged)
+ Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged)
+
+public:
+ explicit BluetoothBaseClass(QObject *parent = 0);
+
+ QString error() const;
+ void setError(const QString& error);
+
+ QString info() const;
+ void setInfo(const QString& info);
+
+ void clearMessages();
+
+signals:
+ void errorChanged();
+ void infoChanged();
+
+private:
+ QString m_error;
+ QString m_info;
+};
+
+#endif // BLUETOOTHBASECLASS_H
diff --git a/examples/bluetooth/heartrate-game/connectionhandler.cpp b/examples/bluetooth/heartrate-game/connectionhandler.cpp
new file mode 100644
index 00000000..40572b71
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/connectionhandler.cpp
@@ -0,0 +1,82 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "heartrate-global.h"
+#include "connectionhandler.h"
+#include <QtBluetooth/qtbluetooth-config.h>
+
+ConnectionHandler::ConnectionHandler(QObject *parent) : QObject(parent)
+{
+ connect(&m_localDevice, &QBluetoothLocalDevice::hostModeStateChanged,
+ this, &ConnectionHandler::hostModeChanged);
+}
+
+bool ConnectionHandler::alive() const
+{
+#ifdef SIMULATOR
+ return true;
+#else
+ return m_localDevice.isValid() && m_localDevice.hostMode() != QBluetoothLocalDevice::HostPoweredOff;
+#endif
+}
+
+bool ConnectionHandler::requiresAddressType() const
+{
+#if QT_CONFIG(bluez)
+ return true;
+#else
+ return false;
+#endif
+}
+
+QString ConnectionHandler::name() const
+{
+ return m_localDevice.name();
+}
+
+QString ConnectionHandler::address() const
+{
+ return m_localDevice.address().toString();
+}
+
+void ConnectionHandler::hostModeChanged(QBluetoothLocalDevice::HostMode /*mode*/)
+{
+ emit deviceChanged();
+}
diff --git a/examples/bluetooth/heartrate-game/connectionhandler.h b/examples/bluetooth/heartrate-game/connectionhandler.h
new file mode 100644
index 00000000..23168b28
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/connectionhandler.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CONNECTIONHANDLER_H
+#define CONNECTIONHANDLER_H
+
+#include <QObject>
+#include <QBluetoothLocalDevice>
+
+class ConnectionHandler : public QObject
+{
+ Q_PROPERTY(bool alive READ alive NOTIFY deviceChanged)
+ Q_PROPERTY(QString name READ name NOTIFY deviceChanged)
+ Q_PROPERTY(QString address READ address NOTIFY deviceChanged)
+ Q_PROPERTY(bool requiresAddressType READ requiresAddressType CONSTANT)
+
+ Q_OBJECT
+public:
+ explicit ConnectionHandler(QObject *parent = 0);
+
+ bool alive() const;
+ bool requiresAddressType() const;
+ QString name() const;
+ QString address() const;
+
+signals:
+ void deviceChanged();
+
+private slots:
+ void hostModeChanged(QBluetoothLocalDevice::HostMode mode);
+
+private:
+ QBluetoothLocalDevice m_localDevice;
+};
+
+#endif // CONNECTIONHANDLER_H
diff --git a/examples/bluetooth/heartrate-game/devicefinder.cpp b/examples/bluetooth/heartrate-game/devicefinder.cpp
new file mode 100644
index 00000000..aed47b6c
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/devicefinder.cpp
@@ -0,0 +1,167 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "devicefinder.h"
+#include "devicehandler.h"
+#include "deviceinfo.h"
+
+DeviceFinder::DeviceFinder(DeviceHandler *handler, QObject *parent):
+ BluetoothBaseClass(parent),
+ m_deviceHandler(handler)
+{
+ //! [devicediscovery-1]
+ m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
+ m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(5000);
+
+ connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::addDevice);
+ connect(m_deviceDiscoveryAgent, static_cast<void (QBluetoothDeviceDiscoveryAgent::*)(QBluetoothDeviceDiscoveryAgent::Error)>(&QBluetoothDeviceDiscoveryAgent::error),
+ this, &DeviceFinder::scanError);
+
+ connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::scanFinished);
+ connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::scanFinished);
+ //! [devicediscovery-1]
+
+
+#ifdef SIMULATOR
+ m_demoTimer.setSingleShot(true);
+ m_demoTimer.setInterval(2000);
+ connect(&m_demoTimer, &QTimer::timeout, this, &DeviceFinder::scanFinished);
+#endif
+}
+
+DeviceFinder::~DeviceFinder()
+{
+ qDeleteAll(m_devices);
+ m_devices.clear();
+}
+
+void DeviceFinder::startSearch()
+{
+ clearMessages();
+ m_deviceHandler->setDevice(0);
+ qDeleteAll(m_devices);
+ m_devices.clear();
+
+ emit devicesChanged();
+
+#ifdef SIMULATOR
+ m_demoTimer.start();
+#else
+ //! [devicediscovery-2]
+ m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
+ //! [devicediscovery-2]
+#endif
+ emit scanningChanged();
+ setInfo(tr("Scanning for devices..."));
+}
+
+//! [devicediscovery-3]
+void DeviceFinder::addDevice(const QBluetoothDeviceInfo &device)
+{
+ // If device is LowEnergy-device, add it to the list
+ if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
+ m_devices.append(new DeviceInfo(device));
+ setInfo(tr("Low Energy device found. Scanning more..."));
+//! [devicediscovery-3]
+ emit devicesChanged();
+//! [devicediscovery-4]
+ }
+ //...
+}
+//! [devicediscovery-4]
+
+void DeviceFinder::scanError(QBluetoothDeviceDiscoveryAgent::Error error)
+{
+ if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError)
+ setError(tr("The Bluetooth adaptor is powered off."));
+ else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError)
+ setError(tr("Writing or reading from the device resulted in an error."));
+ else
+ setError(tr("An unknown error has occurred."));
+}
+
+void DeviceFinder::scanFinished()
+{
+#ifdef SIMULATOR
+ // Only for testing
+ for (int i = 0; i < 4; i++)
+ m_devices.append(new DeviceInfo(QBluetoothDeviceInfo()));
+#endif
+
+ if (m_devices.size() == 0)
+ setError(tr("No Low Energy devices found."));
+ else
+ setInfo(tr("Scanning done."));
+
+ emit scanningChanged();
+ emit devicesChanged();
+}
+
+void DeviceFinder::connectToService(const QString &address)
+{
+ m_deviceDiscoveryAgent->stop();
+
+ DeviceInfo *currentDevice = 0;
+ for (int i = 0; i < m_devices.size(); i++) {
+ if (((DeviceInfo*)m_devices.at(i))->getAddress() == address ) {
+ currentDevice = (DeviceInfo*)m_devices.at(i);
+ break;
+ }
+ }
+
+ if (currentDevice)
+ m_deviceHandler->setDevice(currentDevice);
+
+ clearMessages();
+}
+
+bool DeviceFinder::scanning() const
+{
+#ifdef SIMULATOR
+ return m_demoTimer.isActive();
+#else
+ return m_deviceDiscoveryAgent->isActive();
+#endif
+}
+
+QVariant DeviceFinder::devices()
+{
+ return QVariant::fromValue(m_devices);
+}
diff --git a/examples/bluetooth/heartrate-game/devicefinder.h b/examples/bluetooth/heartrate-game/devicefinder.h
new file mode 100644
index 00000000..c82c0fe5
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/devicefinder.h
@@ -0,0 +1,92 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEVICEFINDER_H
+#define DEVICEFINDER_H
+
+#include "heartrate-global.h"
+#include "bluetoothbaseclass.h"
+
+#include <QTimer>
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QBluetoothDeviceInfo>
+#include <QVariant>
+
+class DeviceInfo;
+class DeviceHandler;
+
+class DeviceFinder: public BluetoothBaseClass
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged)
+ Q_PROPERTY(QVariant devices READ devices NOTIFY devicesChanged)
+
+public:
+ DeviceFinder(DeviceHandler *handler, QObject *parent = 0);
+ ~DeviceFinder();
+
+ bool scanning() const;
+ QVariant devices();
+
+public slots:
+ void startSearch();
+ void connectToService(const QString &address);
+
+private slots:
+ void addDevice(const QBluetoothDeviceInfo&);
+ void scanError(QBluetoothDeviceDiscoveryAgent::Error error);
+ void scanFinished();
+
+signals:
+ void scanningChanged();
+ void devicesChanged();
+
+private:
+ DeviceHandler *m_deviceHandler;
+ QBluetoothDeviceDiscoveryAgent *m_deviceDiscoveryAgent;
+ QList<QObject*> m_devices;
+
+#ifdef SIMULATOR
+ QTimer m_demoTimer;
+#endif
+};
+
+#endif // DEVICEFINDER_H
diff --git a/examples/bluetooth/heartrate-game/devicehandler.cpp b/examples/bluetooth/heartrate-game/devicehandler.cpp
new file mode 100644
index 00000000..dfc514b5
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/devicehandler.cpp
@@ -0,0 +1,354 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "heartrate-global.h"
+#include "devicehandler.h"
+#include "deviceinfo.h"
+#include <QtEndian>
+
+DeviceHandler::DeviceHandler(QObject *parent) :
+ BluetoothBaseClass(parent),
+ m_control(0),
+ m_service(0),
+ m_currentDevice(0),
+ m_foundHeartRateService(false),
+ m_measuring(false),
+ m_currentValue(0),
+ m_min(0), m_max(0), m_sum(0), m_avg(0), m_calories(0)
+{
+#ifdef SIMULATOR
+ m_demoTimer.setSingleShot(false);
+ m_demoTimer.setInterval(2000);
+ connect(&m_demoTimer, &QTimer::timeout, this, &DeviceHandler::updateDemoHR);
+ m_demoTimer.start();
+ updateDemoHR();
+#endif
+}
+
+void DeviceHandler::setAddressType(AddressType type)
+{
+ switch (type) {
+ case DeviceHandler::AddressType::PublicAddress:
+ m_addressType = QLowEnergyController::PublicAddress;
+ break;
+ case DeviceHandler::AddressType::RandomAddress:
+ m_addressType = QLowEnergyController::RandomAddress;
+ break;
+ }
+}
+
+DeviceHandler::AddressType DeviceHandler::addressType() const
+{
+ if (m_addressType == QLowEnergyController::RandomAddress)
+ return DeviceHandler::AddressType::RandomAddress;
+
+ return DeviceHandler::AddressType::PublicAddress;
+}
+
+void DeviceHandler::setDevice(DeviceInfo *device)
+{
+ clearMessages();
+ m_currentDevice = device;
+
+#ifdef SIMULATOR
+ setInfo(tr("Demo device connected."));
+ return;
+#endif
+
+ // Disconnect and delete old connection
+ if (m_control) {
+ m_control->disconnectFromDevice();
+ delete m_control;
+ m_control = 0;
+ }
+
+ // Create new controller and connect it if device available
+ if (m_currentDevice) {
+
+ // Make connections
+ //! [Connect-Signals-1]
+ m_control = new QLowEnergyController(m_currentDevice->getDevice(), this);
+ //! [Connect-Signals-1]
+ m_control->setRemoteAddressType(m_addressType);
+ //! [Connect-Signals-2]
+ connect(m_control, &QLowEnergyController::serviceDiscovered,
+ this, &DeviceHandler::serviceDiscovered);
+ connect(m_control, &QLowEnergyController::discoveryFinished,
+ this, &DeviceHandler::serviceScanDone);
+
+ connect(m_control, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
+ this, [this](QLowEnergyController::Error error) {
+ Q_UNUSED(error);
+ setError("Cannot connect to remote device.");
+ });
+ connect(m_control, &QLowEnergyController::connected, this, [this]() {
+ setInfo("Controller connected. Search services...");
+ m_control->discoverServices();
+ });
+ connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
+ setError("LowEnergy controller disconnected");
+ });
+
+ // Connect
+ m_control->connectToDevice();
+ //! [Connect-Signals-2]
+ }
+}
+
+void DeviceHandler::startMeasurement()
+{
+ if (alive()) {
+ m_start = QDateTime::currentDateTime();
+ m_min = 0;
+ m_max = 0;
+ m_avg = 0;
+ m_sum = 0;
+ m_calories = 0;
+ m_measuring = true;
+ m_measurements.clear();
+ emit measuringChanged();
+ }
+}
+
+void DeviceHandler::stopMeasurement()
+{
+ m_measuring = false;
+ emit measuringChanged();
+}
+
+//! [Filter HeartRate service 1]
+void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt)
+{
+ if (gatt == QBluetoothUuid(QBluetoothUuid::HeartRate)) {
+ setInfo("Heart Rate service discovered. Waiting for service scan to be done...");
+ m_foundHeartRateService = true;
+ }
+}
+//! [Filter HeartRate service 1]
+
+void DeviceHandler::serviceScanDone()
+{
+ setInfo("Service scan done.");
+
+ // Delete old service if available
+ if (m_service) {
+ delete m_service;
+ m_service = 0;
+ }
+
+//! [Filter HeartRate service 2]
+ // If heartRateService found, create new service
+ if (m_foundHeartRateService)
+ m_service = m_control->createServiceObject(QBluetoothUuid(QBluetoothUuid::HeartRate), this);
+
+ if (m_service) {
+ connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
+ connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateHeartRateValue);
+ connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
+ m_service->discoverDetails();
+ } else {
+ setError("Heart Rate Service not found.");
+ }
+//! [Filter HeartRate service 2]
+}
+
+// Service functions
+//! [Find HRM characteristic]
+void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
+{
+ switch (s) {
+ case QLowEnergyService::DiscoveringServices:
+ setInfo(tr("Discovering services..."));
+ break;
+ case QLowEnergyService::ServiceDiscovered:
+ {
+ setInfo(tr("Service discovered."));
+
+ const QLowEnergyCharacteristic hrChar = m_service->characteristic(QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement));
+ if (!hrChar.isValid()) {
+ setError("HR Data not found.");
+ break;
+ }
+
+ m_notificationDesc = hrChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ if (m_notificationDesc.isValid())
+ m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
+
+ break;
+ }
+ default:
+ //nothing for now
+ break;
+ }
+
+ emit aliveChanged();
+}
+//! [Find HRM characteristic]
+
+//! [Reading value]
+void DeviceHandler::updateHeartRateValue(const QLowEnergyCharacteristic &c, const QByteArray &value)
+{
+ // ignore any other characteristic change -> shouldn't really happen though
+ if (c.uuid() != QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement))
+ return;
+
+ const quint8 *data = reinterpret_cast<const quint8 *>(value.constData());
+ quint8 flags = data[0];
+
+ //Heart Rate
+ int hrvalue = 0;
+ if (flags & 0x1) // HR 16 bit? otherwise 8 bit
+ hrvalue = (int)qFromLittleEndian<quint16>(data[1]);
+ else
+ hrvalue = (int)data[1];
+
+ addMeasurement(hrvalue);
+}
+//! [Reading value]
+
+#ifdef SIMULATOR
+void DeviceHandler::updateDemoHR()
+{
+ int randomValue = 0;
+ if (m_currentValue < 30) { // Initial value
+ randomValue = 55 + qrand()%30;
+ } else if (!m_measuring) { // Value when relax
+ randomValue = qBound(55, m_currentValue - 2 + qrand()%5, 75);
+ } else { // Measuring
+ randomValue = m_currentValue + qrand()%10 - 2;
+ }
+
+ addMeasurement(randomValue);
+}
+#endif
+
+void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, const QByteArray &value)
+{
+ if (d.isValid() && d == m_notificationDesc && value == QByteArray::fromHex("0000")) {
+ //disabled notifications -> assume disconnect intent
+ m_control->disconnectFromDevice();
+ delete m_service;
+ m_service = 0;
+ }
+}
+
+void DeviceHandler::disconnectService()
+{
+ m_foundHeartRateService = false;
+
+ //disable notifications
+ if (m_notificationDesc.isValid() && m_service
+ && m_notificationDesc.value() == QByteArray::fromHex("0100")) {
+ m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0000"));
+ } else {
+ if (m_control)
+ m_control->disconnectFromDevice();
+
+ delete m_service;
+ m_service = 0;
+ }
+}
+
+bool DeviceHandler::measuring() const
+{
+ return m_measuring;
+}
+
+bool DeviceHandler::alive() const
+{
+#ifdef SIMULATOR
+ return true;
+#endif
+
+ if (m_service)
+ return m_service->state() == QLowEnergyService::ServiceDiscovered;
+
+ return false;
+}
+
+int DeviceHandler::hr() const
+{
+ return m_currentValue;
+}
+
+int DeviceHandler::time() const
+{
+ return m_start.secsTo(m_stop);
+}
+
+int DeviceHandler::maxHR() const
+{
+ return m_max;
+}
+
+int DeviceHandler::minHR() const
+{
+ return m_min;
+}
+
+float DeviceHandler::average() const
+{
+ return m_avg;
+}
+
+float DeviceHandler::calories() const
+{
+ return m_calories;
+}
+
+void DeviceHandler::addMeasurement(int value)
+{
+ m_currentValue = value;
+
+ // If measuring and value is appropriate
+ if (m_measuring && value > 30 && value < 250) {
+
+ m_stop = QDateTime::currentDateTime();
+ m_measurements << value;
+
+ m_min = m_min == 0 ? value : qMin(value, m_min);
+ m_max = qMax(value, m_max);
+ m_sum += value;
+ m_avg = (double)m_sum / m_measurements.size();
+ m_calories = ((-55.0969 + (0.6309 * m_avg) + (0.1988 * 94) + (0.2017 * 24)) / 4.184) * 60 * time()/3600;
+ }
+
+ emit statsChanged();
+}
diff --git a/examples/bluetooth/heartlistener/heartrate.h b/examples/bluetooth/heartrate-game/devicehandler.h
index 1766ce3a..950ed6e9 100644
--- a/examples/bluetooth/heartlistener/heartrate.h
+++ b/examples/bluetooth/heartrate-game/devicehandler.h
@@ -1,7 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -39,73 +38,71 @@
**
****************************************************************************/
-#ifndef HEARTRATE_H
-#define HEARTRATE_H
+#ifndef DEVICEHANDLER_H
+#define DEVICEHANDLER_H
-#include "deviceinfo.h"
+#include "bluetoothbaseclass.h"
-#include <QString>
-#include <QDebug>
#include <QDateTime>
#include <QVector>
#include <QTimer>
-#include <QBluetoothDeviceDiscoveryAgent>
-#include <QBluetoothDeviceInfo>
#include <QLowEnergyController>
#include <QLowEnergyService>
+class DeviceInfo;
-QT_USE_NAMESPACE
-class HeartRate: public QObject
+class DeviceHandler : public BluetoothBaseClass
{
Q_OBJECT
- Q_PROPERTY(QVariant name READ name NOTIFY nameChanged)
- Q_PROPERTY(QString message READ message NOTIFY messageChanged)
- Q_PROPERTY(int hr READ hR NOTIFY hrChanged)
- Q_PROPERTY(int maxHR READ maxHR NOTIFY averageChanged)
- Q_PROPERTY(int minHR READ minHR NOTIFY averageChanged)
- Q_PROPERTY(float average READ average NOTIFY averageChanged)
- Q_PROPERTY(int time READ time NOTIFY timeChanged)
- Q_PROPERTY(float calories READ caloriesCalculation NOTIFY caloriesChanged)
+
+ Q_PROPERTY(bool measuring READ measuring NOTIFY measuringChanged)
+ Q_PROPERTY(bool alive READ alive NOTIFY aliveChanged)
+ Q_PROPERTY(int hr READ hr NOTIFY statsChanged)
+ Q_PROPERTY(int maxHR READ maxHR NOTIFY statsChanged)
+ Q_PROPERTY(int minHR READ minHR NOTIFY statsChanged)
+ Q_PROPERTY(float average READ average NOTIFY statsChanged)
+ Q_PROPERTY(int time READ time NOTIFY statsChanged)
+ Q_PROPERTY(float calories READ calories NOTIFY statsChanged)
+ Q_PROPERTY(AddressType addressType READ addressType WRITE setAddressType)
public:
- HeartRate();
- ~HeartRate();
- void setMessage(QString message);
- QString message() const;
- QVariant name();
- int hR() const;
- int time();
- float average();
+ enum class AddressType {
+ PublicAddress,
+ RandomAddress
+ };
+ Q_ENUM(AddressType)
+
+ DeviceHandler(QObject *parent = 0);
+
+ void setDevice(DeviceInfo *device);
+ void setAddressType(AddressType type);
+ AddressType addressType() const;
+
+ bool measuring() const;
+ bool alive() const;
+
+ // Statistics
+ int hr() const;
+ int time() const;
+ float average() const;
int maxHR() const;
int minHR() const;
- float caloriesCalculation();
+ float calories() const;
+
+signals:
+ void measuringChanged();
+ void aliveChanged();
+ void statsChanged();
public slots:
- void deviceSearch();
- void connectToService(const QString &address);
+ void startMeasurement();
+ void stopMeasurement();
void disconnectService();
- void startDemo();
-
- void obtainResults();
- int measurements(int index) const;
- int measurementsSize() const;
- QString deviceAddress() const;
- int numDevices() const;
-
-private slots:
- //QBluetothDeviceDiscoveryAgent
- void addDevice(const QBluetoothDeviceInfo&);
- void scanFinished();
- void deviceScanError(QBluetoothDeviceDiscoveryAgent::Error);
+private:
//QLowEnergyController
void serviceDiscovered(const QBluetoothUuid &);
void serviceScanDone();
- void controllerError(QLowEnergyController::Error);
- void deviceConnected();
- void deviceDisconnected();
-
//QLowEnergyService
void serviceStateChanged(QLowEnergyService::ServiceState s);
@@ -113,37 +110,33 @@ private slots:
const QByteArray &value);
void confirmedDescriptorWrite(const QLowEnergyDescriptor &d,
const QByteArray &value);
- void serviceError(QLowEnergyService::ServiceError e);
-
- //DemoMode
- void receiveDemo();
-
-Q_SIGNALS:
- void messageChanged();
- void nameChanged();
- void hrChanged();
- void averageChanged();
- void timeChanged();
- void caloriesChanged();
+#ifdef SIMULATOR
+ void updateDemoHR();
+#endif
private:
- int randomPulse() const;
+ void addMeasurement(int value);
- DeviceInfo m_currentDevice;
- QBluetoothDeviceDiscoveryAgent *m_deviceDiscoveryAgent;
+ QLowEnergyController *m_control;
+ QLowEnergyService *m_service;
QLowEnergyDescriptor m_notificationDesc;
- QList<QObject*> m_devices;
- QString m_info;
- bool foundHeartRateService;
- QVector<quint16> m_measurements;
+ DeviceInfo *m_currentDevice;
+
+ bool m_foundHeartRateService;
+ bool m_measuring;
+ int m_currentValue, m_min, m_max, m_sum;
+ float m_avg, m_calories;
+
+ // Statistics
QDateTime m_start;
QDateTime m_stop;
- int m_max;
- int m_min;
- float calories;
- QLowEnergyController *m_control;
- QTimer *timer; // for demo application
- QLowEnergyService *m_service;
+
+ QVector<int> m_measurements;
+ QLowEnergyController::RemoteAddressType m_addressType = QLowEnergyController::PublicAddress;
+
+#ifdef SIMULATOR
+ QTimer m_demoTimer;
+#endif
};
-#endif // HEARTRATE_H
+#endif // DEVICEHANDLER_H
diff --git a/examples/bluetooth/heartlistener/deviceinfo.cpp b/examples/bluetooth/heartrate-game/deviceinfo.cpp
index cfddf56d..cf364393 100644
--- a/examples/bluetooth/heartlistener/deviceinfo.cpp
+++ b/examples/bluetooth/heartrate-game/deviceinfo.cpp
@@ -1,7 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -39,9 +38,10 @@
**
****************************************************************************/
-#include <qbluetoothuuid.h>
-
+#include "heartrate-global.h"
#include "deviceinfo.h"
+#include <QBluetoothAddress>
+#include <QBluetoothUuid>
DeviceInfo::DeviceInfo(const QBluetoothDeviceInfo &info):
QObject(), m_device(info)
@@ -53,9 +53,20 @@ QBluetoothDeviceInfo DeviceInfo::getDevice() const
return m_device;
}
+QString DeviceInfo::getName() const
+{
+#ifdef SIMULATOR
+ return "Demo device";
+#else
+ return m_device.name();
+#endif
+}
+
QString DeviceInfo::getAddress() const
{
-#ifdef Q_OS_MAC
+#ifdef SIMULATOR
+ return "00:11:22:33:44:55";
+#elif defined Q_OS_DARWIN
// workaround for Core Bluetooth:
return m_device.deviceUuid().toString();
#else
diff --git a/examples/bluetooth/heartlistener/deviceinfo.h b/examples/bluetooth/heartrate-game/deviceinfo.h
index 636f7905..bbddbdd4 100644
--- a/examples/bluetooth/heartlistener/deviceinfo.h
+++ b/examples/bluetooth/heartrate-game/deviceinfo.h
@@ -1,7 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -44,18 +43,19 @@
#include <QString>
#include <QObject>
-#include <qbluetoothdeviceinfo.h>
-#include <qbluetoothaddress.h>
+#include <QBluetoothDeviceInfo>
class DeviceInfo: public QObject
{
Q_OBJECT
Q_PROPERTY(QString deviceName READ getName NOTIFY deviceChanged)
Q_PROPERTY(QString deviceAddress READ getAddress NOTIFY deviceChanged)
+
public:
DeviceInfo(const QBluetoothDeviceInfo &device);
+
void setDevice(const QBluetoothDeviceInfo &device);
- QString getName() const { return m_device.name(); }
+ QString getName() const;
QString getAddress() const;
QBluetoothDeviceInfo getDevice() const;
diff --git a/examples/bluetooth/heartrate-game/doc/images/heartgame-result.png b/examples/bluetooth/heartrate-game/doc/images/heartgame-result.png
new file mode 100644
index 00000000..2dad1b30
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/doc/images/heartgame-result.png
Binary files differ
diff --git a/examples/bluetooth/heartrate-game/doc/images/heartgame-running.png b/examples/bluetooth/heartrate-game/doc/images/heartgame-running.png
new file mode 100644
index 00000000..05485f0e
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/doc/images/heartgame-running.png
Binary files differ
diff --git a/examples/bluetooth/heartrate-game/doc/images/heartgame-search.png b/examples/bluetooth/heartrate-game/doc/images/heartgame-search.png
new file mode 100644
index 00000000..4b0b0497
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/doc/images/heartgame-search.png
Binary files differ
diff --git a/examples/bluetooth/heartrate-game/doc/images/heartgame-start.png b/examples/bluetooth/heartrate-game/doc/images/heartgame-start.png
new file mode 100644
index 00000000..21cc641f
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/doc/images/heartgame-start.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc b/examples/bluetooth/heartrate-game/doc/src/heartrate-game.qdoc
index 2bb59ae1..2b2b75d0 100644
--- a/examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc
+++ b/examples/bluetooth/heartrate-game/doc/src/heartrate-game.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -26,19 +26,18 @@
****************************************************************************/
/*!
- \example heartlistener
- \title Bluetooth Low Energy Heart Listener Example
- \brief An example demonstrating the interaction with a Bluetooth Low Energy Heart Rate
- device/service. The example demonstrates the use of all Qt Bluetooth
- Low Energy classes.
+ \example heartrate-game
+ \title Bluetooth Low Energy Heart Rate Game
+ \brief A game demonstrating the interaction with a Bluetooth Low Energy Heart Rate
+ device/service.
- The Bluetooth Low Energy Heart Listener Example shows how to develop a Bluetooth
- Low Energy application using the Qt Bluetooth API. The application covers
- the scanning for Bluetooth Low Energy devices, connecting to a Heart Rate service
- on the device, writing characteristics and descriptors and receiving updates from device
- once the heart rate has changed.
+ The Bluetooth Low Energy Heart Rate Game shows how to develop a Bluetooth Low Energy
+ application using the Qt Bluetooth API. The application covers the scanning for
+ Bluetooth Low Energy devices, connecting to a Heart Rate service on the device, writing
+ characteristics and descriptors, and receiving updates from the device once the heart rate
+ has changed.
- \image heartratemonitor.png
+ \image heartgame-start.png
The example introduces the following Qt classes:
@@ -56,6 +55,8 @@
If no such device can be found, the example uses a demo mode which creates and displays
random values.
+ The goal of the game is to increase the measured heart rate as much as possible.
+
The \l {lowenergyscanner}{Bluetooth Low Energy Scanner} example might be more suitable
if a heart rate device is not available. The scanner example works with any type of Bluetooth
Low Energy peripheral device.
@@ -66,20 +67,20 @@
The application searches for all Bluetooth Low Energy peripheral devices in the vicinity.
It is assumed that the remote devices advertise their presence. The found devices are
- presented in a list. Note that it shows all Bluetooth Low Energy devices even those which
- do not offer a Heart Rate service.
+ presented in a list. Note that all found Bluetooth Low Energy devices are listed even
+ if they do not offer a Heart Rate service.
- \image heartratefound.png
+ \image heartgame-search.png
After the user has selected a target device, the example connects to its Heart Rate service
if one is available. It automatically enables notification updates for the Heart Rate value
and presents the current value on the screen.
- \image heartratemonitor.png
+ \image heartgame-running.png
Once the monitoring process is canceled, a small graph presents a summary of the received
values.
- \image heartrateresults.png
-*/
+ \image heartgame-result.png
+*/
diff --git a/examples/bluetooth/heartrate-game/heartrate-game.pro b/examples/bluetooth/heartrate-game/heartrate-game.pro
new file mode 100644
index 00000000..fcec0bd2
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/heartrate-game.pro
@@ -0,0 +1,29 @@
+TEMPLATE = app
+TARGET = heartrate-game
+
+QT += qml quick bluetooth
+CONFIG += c++11
+
+HEADERS += \
+ connectionhandler.h \
+ deviceinfo.h \
+ devicefinder.h \
+ devicehandler.h \
+ bluetoothbaseclass.h \
+ heartrate-global.h
+
+SOURCES += main.cpp \
+ connectionhandler.cpp \
+ deviceinfo.cpp \
+ devicefinder.cpp \
+ devicehandler.cpp \
+ bluetoothbaseclass.cpp
+
+RESOURCES += qml.qrc \
+ images.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/heartrate-game
+INSTALLS += target
diff --git a/examples/bluetooth/heartlistener/assets/Point.qml b/examples/bluetooth/heartrate-game/heartrate-global.h
index 0341d11a..376c7990 100644
--- a/examples/bluetooth/heartlistener/assets/Point.qml
+++ b/examples/bluetooth/heartrate-game/heartrate-global.h
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -38,14 +38,14 @@
**
****************************************************************************/
-import QtQuick 2.0
+#ifndef HEARTRATEGLOBAL_H
+#define HEARTRATEGLOBAL_H
-Rectangle {
- id: point
+//#define USE_SIMULATOR
- Image {
- width: 10; height: 7
- smooth: true
- source: "blue_heart_small.png"
- }
-}
+#if defined(Q_OS_WIN32) || defined(USE_SIMULATOR)
+#define SIMULATOR
+#endif
+
+
+#endif // HEARTRATEGLOBAL_H
diff --git a/examples/bluetooth/heartrate-game/images.qrc b/examples/bluetooth/heartrate-game/images.qrc
new file mode 100644
index 00000000..38058de0
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/images.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/images/logo.png</file>
+ <file>qml/images/bt_off_to_on.png</file>
+ <file>qml/images/heart.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/bluetooth/heartlistener/main.cpp b/examples/bluetooth/heartrate-game/main.cpp
index 7aa17080..0b0cae12 100644
--- a/examples/bluetooth/heartlistener/main.cpp
+++ b/examples/bluetooth/heartrate-game/main.cpp
@@ -1,7 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -39,23 +38,32 @@
**
****************************************************************************/
-#include <QtCore/QLoggingCategory>
-#include <QQmlContext>
#include <QGuiApplication>
-#include <QQuickView>
-#include "heartrate.h"
+#include <QQmlApplicationEngine>
+#include <QQmlContext>
+#include <QtCore/QLoggingCategory>
+
+#include "connectionhandler.h"
+#include "devicefinder.h"
+#include "devicehandler.h"
int main(int argc, char *argv[])
{
//QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
QGuiApplication app(argc, argv);
- HeartRate heartRate;
- QQuickView *view = new QQuickView;
- view->rootContext()->setContextProperty("heartRate", &heartRate);
- view->setSource(QUrl("qrc:/assets/main.qml"));
- view->setResizeMode(QQuickView::SizeRootObjectToView);
- view->show();
- return app.exec();
+ ConnectionHandler connectionHandler;
+ DeviceHandler deviceHandler;
+ DeviceFinder deviceFinder(&deviceHandler);
+
+ qmlRegisterUncreatableType<DeviceHandler>("Shared", 1, 0, "AddressType", "Enum is not a type");
+
+ QQmlApplicationEngine engine;
+ engine.rootContext()->setContextProperty("connectionHandler", &connectionHandler);
+ engine.rootContext()->setContextProperty("deviceFinder", &deviceFinder);
+ engine.rootContext()->setContextProperty("deviceHandler", &deviceHandler);
+ engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
+
+ return app.exec();
}
diff --git a/examples/bluetooth/heartrate-game/qml.qrc b/examples/bluetooth/heartrate-game/qml.qrc
new file mode 100644
index 00000000..bab96355
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml.qrc
@@ -0,0 +1,18 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/BluetoothAlarmDialog.qml</file>
+ <file>qml/main.qml</file>
+ <file>qml/SplashScreen.qml</file>
+ <file>qml/GameSettings.qml</file>
+ <file>qml/App.qml</file>
+ <file>qml/TitleBar.qml</file>
+ <file>qml/Connect.qml</file>
+ <file>qml/Measure.qml</file>
+ <file>qml/Stats.qml</file>
+ <file>qml/GameButton.qml</file>
+ <file>qml/GamePage.qml</file>
+ <file>qml/BottomLine.qml</file>
+ <file>qml/StatsLabel.qml</file>
+ <file>qml/qmldir</file>
+ </qresource>
+</RCC>
diff --git a/examples/bluetooth/heartrate-game/qml/App.qml b/examples/bluetooth/heartrate-game/qml/App.qml
new file mode 100644
index 00000000..fc8b1c89
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/App.qml
@@ -0,0 +1,120 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+
+Item {
+ id: app
+ anchors.fill: parent
+ opacity: 0.0
+
+ Behavior on opacity { NumberAnimation { duration: 500 } }
+
+ property var lastPages: []
+ property int __currentIndex: 0
+
+ function init()
+ {
+ opacity = 1.0
+ showPage("Connect.qml")
+ }
+
+ function prevPage()
+ {
+ lastPages.pop()
+ pageLoader.setSource(lastPages[lastPages.length-1])
+ __currentIndex = lastPages.length-1;
+ }
+
+ function showPage(name)
+ {
+ lastPages.push(name)
+ pageLoader.setSource(name)
+ __currentIndex = lastPages.length-1;
+ }
+
+ TitleBar {
+ id: titleBar
+ currentIndex: __currentIndex
+
+ onTitleClicked: {
+ if (index < __currentIndex)
+ pageLoader.item.close()
+ }
+ }
+
+ Loader {
+ id: pageLoader
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: titleBar.bottom
+ anchors.bottom: parent.bottom
+
+ onStatusChanged: {
+ if (status === Loader.Ready)
+ {
+ pageLoader.item.init();
+ pageLoader.item.forceActiveFocus()
+ }
+ }
+ }
+
+ Keys.onReleased: {
+ switch (event.key) {
+ case Qt.Key_Escape:
+ case Qt.Key_Back: {
+ if (__currentIndex > 0) {
+ pageLoader.item.close()
+ event.accepted = true
+ } else {
+ Qt.quit()
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+
+ BluetoothAlarmDialog {
+ id: btAlarmDialog
+ anchors.fill: parent
+ visible: !connectionHandler.alive
+ }
+}
diff --git a/examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml b/examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml
new file mode 100644
index 00000000..ef32f5ea
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml
@@ -0,0 +1,112 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+
+Item {
+ id: root
+ anchors.fill: parent
+
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ opacity: 0.9
+ }
+
+ MouseArea {
+ id: eventEater
+ }
+
+ Rectangle {
+ id: dialogFrame
+
+ anchors.centerIn: parent
+ width: parent.width * 0.8
+ height: parent.height * 0.6
+ border.color: "#454545"
+ color: GameSettings.backgroundColor
+ radius: width * 0.05
+
+ Item {
+ id: dialogContainer
+ anchors.fill: parent
+ anchors.margins: parent.width*0.05
+
+ Image {
+ id: offOnImage
+ anchors.left: quitButton.left
+ anchors.right: quitButton.right
+ anchors.top: parent.top
+ height: GameSettings.heightForWidth(width, sourceSize)
+ source: "images/bt_off_to_on.png"
+ }
+
+ Text {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: offOnImage.bottom
+ anchors.bottom: quitButton.top
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.WordWrap
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ text: qsTr("This application cannot be used without Bluetooth. Please switch Bluetooth ON to continue.")
+ }
+
+ GameButton {
+ id: quitButton
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: dialogContainer.width * 0.6
+ height: GameSettings.buttonHeight
+ onClicked: Qt.quit()
+
+ Text {
+ anchors.centerIn: parent
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.bigFontSize
+ text: qsTr("Quit")
+ }
+ }
+ }
+ }
+}
+
diff --git a/examples/bluetooth/heartlistener/assets/dialog.qml b/examples/bluetooth/heartrate-game/qml/BottomLine.qml
index bcaf299f..af644228 100644
--- a/examples/bluetooth/heartlistener/assets/dialog.qml
+++ b/examples/bluetooth/heartrate-game/qml/BottomLine.qml
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -38,24 +38,12 @@
**
****************************************************************************/
-import QtQuick 2.0
+import QtQuick 2.5
Rectangle {
- id: root
- opacity: 0.8
- color: "#E0DEDF"
anchors.horizontalCenter: parent.horizontalCenter
-
- property int hr: heartRate.hr
- Text {
- text: heartRate.message
- color: "#3870BA"
-
- }
-
- onHrChanged: {
- if (heartRate.hr > 0) {
- root.destroy()
- }
- }
+ anchors.bottom: parent.bottom
+ width: parent.width * 0.85
+ height: parent.height * 0.05
+ radius: height*0.5
}
diff --git a/examples/bluetooth/heartrate-game/qml/Connect.qml b/examples/bluetooth/heartrate-game/qml/Connect.qml
new file mode 100644
index 00000000..9e4efb17
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/Connect.qml
@@ -0,0 +1,178 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import Shared 1.0
+
+GamePage {
+
+ errorMessage: deviceFinder.error
+ infoMessage: deviceFinder.info
+
+ Rectangle {
+ id: viewContainer
+ anchors.top: parent.top
+ anchors.bottom:
+ // only BlueZ platform has address type selection
+ connectionHandler.requiresAddressType ? addressTypeButton.top : searchButton.top
+ anchors.topMargin: GameSettings.fieldMargin + messageHeight
+ anchors.bottomMargin: GameSettings.fieldMargin
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - GameSettings.fieldMargin*2
+ color: GameSettings.viewColor
+ radius: GameSettings.buttonRadius
+
+
+ Text {
+ id: title
+ width: parent.width
+ height: GameSettings.fieldHeight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.mediumFontSize
+ text: qsTr("FOUND DEVICES")
+
+ BottomLine {
+ height: 1;
+ width: parent.width
+ color: "#898989"
+ }
+ }
+
+
+ ListView {
+ id: devices
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.top: title.bottom
+ model: deviceFinder.devices
+ clip: true
+
+ delegate: Rectangle {
+ id: box
+ height:GameSettings.fieldHeight * 1.2
+ width: parent.width
+ color: index % 2 === 0 ? GameSettings.delegate1Color : GameSettings.delegate2Color
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ deviceFinder.connectToService(modelData.deviceAddress);
+ app.showPage("Measure.qml")
+ }
+ }
+
+ Text {
+ id: device
+ font.pixelSize: GameSettings.smallFontSize
+ text: modelData.deviceName
+ anchors.top: parent.top
+ anchors.topMargin: parent.height * 0.1
+ anchors.leftMargin: parent.height * 0.1
+ anchors.left: parent.left
+ color: GameSettings.textColor
+ }
+
+ Text {
+ id: deviceAddress
+ font.pixelSize: GameSettings.smallFontSize
+ text: modelData.deviceAddress
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: parent.height * 0.1
+ anchors.rightMargin: parent.height * 0.1
+ anchors.right: parent.right
+ color: Qt.darker(GameSettings.textColor)
+ }
+ }
+ }
+ }
+
+ GameButton {
+ id: addressTypeButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: searchButton.top
+ anchors.bottomMargin: GameSettings.fieldMargin*0.5
+ width: viewContainer.width
+ height: GameSettings.fieldHeight
+ visible: connectionHandler.requiresAddressType // only required on BlueZ
+ state: "public"
+ onClicked: state == "public" ? state = "random" : state = "public"
+
+ states: [
+ State {
+ name: "public"
+ PropertyChanges { target: addressTypeText; text: qsTr("Public Address") }
+ PropertyChanges { target: deviceHandler; addressType: AddressType.PublicAddress }
+ },
+ State {
+ name: "random"
+ PropertyChanges { target: addressTypeText; text: qsTr("Random Address") }
+ PropertyChanges { target: deviceHandler; addressType: AddressType.RandomAddress }
+ }
+ ]
+
+ Text {
+ id: addressTypeText
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ color: GameSettings.textColor
+ }
+ }
+
+ GameButton {
+ id: searchButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: GameSettings.fieldMargin
+ width: viewContainer.width
+ height: GameSettings.fieldHeight
+ enabled: !deviceFinder.scanning
+ onClicked: deviceFinder.startSearch()
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ text: qsTr("START SEARCH")
+ color: searchButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate-game/qml/GameButton.qml b/examples/bluetooth/heartrate-game/qml/GameButton.qml
new file mode 100644
index 00000000..e7fc36af
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/GameButton.qml
@@ -0,0 +1,78 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import "."
+
+Rectangle {
+ id: button
+ color: baseColor
+ onEnabledChanged: checkColor()
+ radius: GameSettings.buttonRadius
+
+ property color baseColor: GameSettings.buttonColor
+ property color pressedColor: GameSettings.buttonPressedColor
+ property color disabledColor: GameSettings.disabledButtonColor
+
+ signal clicked()
+
+ function checkColor()
+ {
+ if (!button.enabled) {
+ button.color = disabledColor
+ } else {
+ if (mouseArea.containsPress)
+ button.color = pressedColor
+ else
+ button.color = baseColor
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onPressed: checkColor()
+ onReleased: checkColor()
+ onClicked: {
+ checkColor()
+ button.clicked()
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate-game/qml/GamePage.qml b/examples/bluetooth/heartrate-game/qml/GamePage.qml
new file mode 100644
index 00000000..663eb047
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/GamePage.qml
@@ -0,0 +1,83 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import "."
+
+Item {
+ anchors.fill: parent
+
+ property string errorMessage: ""
+ property string infoMessage: ""
+ property real messageHeight: msg.height
+ property bool hasError: errorMessage != ""
+ property bool hasInfo: infoMessage != ""
+
+ function init()
+ {
+ }
+
+ function close()
+ {
+ app.prevPage()
+ }
+
+ Rectangle {
+ id: msg
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: GameSettings.fieldHeight
+ color: hasError ? GameSettings.errorColor : GameSettings.infoColor
+ visible: hasError || hasInfo
+
+ Text {
+ id: error
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ minimumPixelSize: 5
+ font.pixelSize: GameSettings.smallFontSize
+ fontSizeMode: Text.Fit
+ color: GameSettings.textColor
+ text: hasError ? errorMessage : infoMessage
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate-game/qml/GameSettings.qml b/examples/bluetooth/heartrate-game/qml/GameSettings.qml
new file mode 100644
index 00000000..868f32ac
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/GameSettings.qml
@@ -0,0 +1,91 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+pragma Singleton
+import QtQuick 2.5
+
+Item {
+ property int wHeight
+ property int wWidth
+
+ // Colors
+ readonly property color backgroundColor: "#2d3037"
+ readonly property color buttonColor: "#202227"
+ readonly property color buttonPressedColor: "#6ccaf2"
+ readonly property color disabledButtonColor: "#555555"
+ readonly property color viewColor: "#202227"
+ readonly property color delegate1Color: Qt.darker(viewColor, 1.2)
+ readonly property color delegate2Color: Qt.lighter(viewColor, 1.2)
+ readonly property color textColor: "#ffffff"
+ readonly property color textDarkColor: "#232323"
+ readonly property color disabledTextColor: "#777777"
+ readonly property color sliderColor: "#6ccaf2"
+ readonly property color errorColor: "#ba3f62"
+ readonly property color infoColor: "#3fba62"
+
+ // Font sizes
+ property real microFontSize: hugeFontSize * 0.2
+ property real tinyFontSize: hugeFontSize * 0.4
+ property real smallTinyFontSize: hugeFontSize * 0.5
+ property real smallFontSize: hugeFontSize * 0.6
+ property real mediumFontSize: hugeFontSize * 0.7
+ property real bigFontSize: hugeFontSize * 0.8
+ property real largeFontSize: hugeFontSize * 0.9
+ property real hugeFontSize: (wWidth + wHeight) * 0.03
+ property real giganticFontSize: (wWidth + wHeight) * 0.04
+
+ // Some other values
+ property real fieldHeight: wHeight * 0.08
+ property real fieldMargin: fieldHeight * 0.5
+ property real buttonHeight: wHeight * 0.08
+ property real buttonRadius: buttonHeight * 0.1
+
+ // Some help functions
+ function widthForHeight(h, ss)
+ {
+ return h/ss.height * ss.width;
+ }
+
+ function heightForWidth(w, ss)
+ {
+ return w/ss.width * ss.height;
+ }
+
+}
diff --git a/examples/bluetooth/heartrate-game/qml/Measure.qml b/examples/bluetooth/heartrate-game/qml/Measure.qml
new file mode 100644
index 00000000..f8c51eb4
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/Measure.qml
@@ -0,0 +1,234 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+
+GamePage {
+ id: measurePage
+
+ errorMessage: deviceHandler.error
+ infoMessage: deviceHandler.info
+
+ property real __timeCounter: 0;
+ property real __maxTimeCount: 60
+ property string relaxText: qsTr("Relax!\nWhen you are ready, press Start. You have %1s time to increase heartrate so much as possible.\nGood luck!").arg(__maxTimeCount)
+
+ function close()
+ {
+ deviceHandler.stopMeasurement();
+ deviceHandler.disconnectService();
+ app.prevPage();
+ }
+
+ function start()
+ {
+ if (!deviceHandler.measuring) {
+ __timeCounter = 0;
+ deviceHandler.startMeasurement()
+ }
+ }
+
+ function stop()
+ {
+ if (deviceHandler.measuring) {
+ deviceHandler.stopMeasurement()
+ }
+
+ app.showPage("Stats.qml")
+ }
+
+ Timer {
+ id: measureTimer
+ interval: 1000
+ running: deviceHandler.measuring
+ repeat: true
+ onTriggered: {
+ __timeCounter++;
+ if (__timeCounter >= __maxTimeCount)
+ measurePage.stop()
+ }
+ }
+
+ Column {
+ anchors.centerIn: parent
+ spacing: GameSettings.fieldHeight * 0.5
+
+ Rectangle {
+ id: circle
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: Math.min(measurePage.width, measurePage.height-GameSettings.fieldHeight*4) - 2*GameSettings.fieldMargin
+ height: width
+ radius: width*0.5
+ color: GameSettings.viewColor
+
+ Text {
+ id: hintText
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -parent.height*0.1
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ width: parent.width * 0.8
+ height: parent.height * 0.6
+ wrapMode: Text.WordWrap
+ text: measurePage.relaxText
+ visible: !deviceHandler.measuring
+ color: GameSettings.textColor
+ fontSizeMode: Text.Fit
+ minimumPixelSize: 10
+ font.pixelSize: GameSettings.mediumFontSize
+ }
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -parent.height*0.15
+ font.pixelSize: parent.width * 0.45
+ text: deviceHandler.hr
+ visible: deviceHandler.measuring
+ color: GameSettings.textColor
+ }
+
+ Item {
+ id: minMaxContainer
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width*0.7
+ height: parent.height * 0.15
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: parent.height*0.16
+ visible: deviceHandler.measuring
+
+ Text {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ text: deviceHandler.minHR
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.hugeFontSize
+
+ Text {
+ anchors.left: parent.left
+ anchors.bottom: parent.top
+ font.pixelSize: parent.font.pixelSize*0.8
+ color: parent.color
+ text: "MIN"
+ }
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ text: deviceHandler.maxHR
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.hugeFontSize
+
+ Text {
+ anchors.right: parent.right
+ anchors.bottom: parent.top
+ font.pixelSize: parent.font.pixelSize*0.8
+ color: parent.color
+ text: "MAX"
+ }
+ }
+ }
+
+ Image {
+ id: heart
+ anchors.horizontalCenter: minMaxContainer.horizontalCenter
+ anchors.verticalCenter: minMaxContainer.bottom
+ width: parent.width * 0.2
+ height: width
+ source: "images/heart.png"
+ smooth: true
+ antialiasing: true
+
+ SequentialAnimation{
+ id: heartAnim
+ running: deviceHandler.alive
+ loops: Animation.Infinite
+ alwaysRunToEnd: true
+ PropertyAnimation { target: heart; property: "scale"; to: 1.2; duration: 500; easing.type: Easing.InQuad }
+ PropertyAnimation { target: heart; property: "scale"; to: 1.0; duration: 500; easing.type: Easing.OutQuad }
+ }
+ }
+ }
+
+ Rectangle {
+ id: timeSlider
+ color: GameSettings.viewColor
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: circle.width
+ height: GameSettings.fieldHeight
+ radius: GameSettings.buttonRadius
+
+ Rectangle {
+ height: parent.height
+ radius: parent.radius
+ color: GameSettings.sliderColor
+ width: Math.min(1.0,__timeCounter / __maxTimeCount) * parent.width
+ }
+
+ Text {
+ anchors.centerIn: parent
+ color: "gray"
+ text: (__maxTimeCount - __timeCounter).toFixed(0) + " s"
+ font.pixelSize: GameSettings.bigFontSize
+ }
+ }
+ }
+
+ GameButton {
+ id: startButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: GameSettings.fieldMargin
+ width: circle.width
+ height: GameSettings.fieldHeight
+ enabled: !deviceHandler.measuring
+ radius: GameSettings.buttonRadius
+
+ onClicked: start()
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ text: qsTr("START")
+ color: startButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate-game/qml/SplashScreen.qml b/examples/bluetooth/heartrate-game/qml/SplashScreen.qml
new file mode 100644
index 00000000..f480125e
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/SplashScreen.qml
@@ -0,0 +1,80 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import "."
+
+Item {
+ id: root
+ anchors.fill: parent
+
+ property bool appIsReady: false
+ property bool splashIsReady: false
+
+ property bool ready: appIsReady && splashIsReady
+ onReadyChanged: if (ready) readyToGo();
+
+ signal readyToGo()
+
+ function appReady()
+ {
+ appIsReady = true
+ }
+
+ function errorInLoadingApp()
+ {
+ Qt.quit()
+ }
+
+ Image {
+ anchors.centerIn: parent
+ width: Math.min(parent.height, parent.width)*0.6
+ height: GameSettings.heightForWidth(width, sourceSize)
+ source: "images/logo.png"
+ }
+
+ Timer {
+ id: splashTimer
+ interval: 1000
+ onTriggered: splashIsReady = true
+ }
+
+ Component.onCompleted: splashTimer.start()
+}
diff --git a/examples/bluetooth/heartlistener/assets/main.qml b/examples/bluetooth/heartrate-game/qml/Stats.qml
index 89d9f600..6e34d3bc 100644
--- a/examples/bluetooth/heartlistener/assets/main.qml
+++ b/examples/bluetooth/heartrate-game/qml/Stats.qml
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -38,47 +38,52 @@
**
****************************************************************************/
-import QtQuick 2.0
+import QtQuick 2.5
-Item {
- width: 400
- height: 600
- id: begin
+GamePage {
- Rectangle {
- color: "#F0EBED"
- anchors.fill: parent
- Rectangle {
- id: about
- width: 0.75*parent.width
- height: 0.1*parent.height
- anchors.top: parent.top
- anchors.topMargin: 20
+ Column {
+ anchors.centerIn: parent
+ width: parent.width
+
+ Text {
anchors.horizontalCenter: parent.horizontalCenter
- color: "#F0EBED"
- border.color: "#3870BA"
- border.width: 2
- radius: 10
- Text {
- id: aboutinfo
- anchors.centerIn: parent
- color: "#3870BA"
- text: "Welcome to the Heart Listener Application"
- }
+ font.pixelSize: GameSettings.hugeFontSize
+ color: GameSettings.textColor
+ text: qsTr("RESULT")
}
- Button {
- id:call
- buttonWidth: 0.75*parent.width
- buttonHeight: 0.15*parent.height
- anchors.centerIn: parent
- text: "Scan for Devices"
- onButtonClick: pageLoader.source="home.qml"
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: GameSettings.giganticFontSize*3
+ color: GameSettings.textColor
+ text: (deviceHandler.maxHR - deviceHandler.minHR).toFixed(0)
+ }
+
+ Item {
+ height: GameSettings.fieldHeight
+ width: 1
+ }
+
+ StatsLabel {
+ title: qsTr("MIN")
+ value: deviceHandler.minHR.toFixed(0)
}
- }
- Loader {
- id: pageLoader
- anchors.fill: parent
+ StatsLabel {
+ title: qsTr("MAX")
+ value: deviceHandler.maxHR.toFixed(0)
+ }
+
+ StatsLabel {
+ title: qsTr("AVG")
+ value: deviceHandler.average.toFixed(1)
+ }
+
+
+ StatsLabel {
+ title: qsTr("CALORIES")
+ value: deviceHandler.calories.toFixed(3)
+ }
}
}
diff --git a/examples/bluetooth/heartlistener/assets/Button.qml b/examples/bluetooth/heartrate-game/qml/StatsLabel.qml
index 51c377b1..dd57852f 100644
--- a/examples/bluetooth/heartlistener/assets/Button.qml
+++ b/examples/bluetooth/heartrate-game/qml/StatsLabel.qml
@@ -1,6 +1,6 @@
/***************************************************************************
**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
@@ -38,40 +38,35 @@
**
****************************************************************************/
-import QtQuick 2.0
+import QtQuick 2.5
+import "."
-Rectangle {
- id:button
- //color: "#3870BA"
+Item {
+ height: GameSettings.fieldHeight
+ width: parent.width
- property real buttonWidth: 300
- property real buttonHeight: 80
- property string text: "Button"
-
- signal buttonClick()
- width: click.pressed ? (buttonWidth - 15) : buttonWidth
- height: click.pressed ? (buttonHeight - 15) :buttonHeight
-
- color: click.pressed ? "#3265A7" : "#3870BA"
-
- border.color: "#F0EBED"
- border.width: 5
- radius: 10
+ property alias title: leftText.text
+ property alias value: rightText.text
Text {
- id: label
- font.pixelSize: 30; font.bold: true
- horizontalAlignment: Text.AlignHCenter
+ id: leftText
+ anchors.left: parent.left
+ height: parent.height
+ width: parent.width * 0.45
+ horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
- anchors.fill: parent
- elide: Text.ElideMiddle
- color: "#F0EBED"
- text: button.text
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
}
- MouseArea {
- id: click
- anchors.fill: parent
- onClicked: buttonClick()
+ Text {
+ id: rightText
+ anchors.right: parent.right
+ height: parent.height
+ width: parent.width * 0.45
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
}
}
diff --git a/examples/bluetooth/heartrate-game/qml/TitleBar.qml b/examples/bluetooth/heartrate-game/qml/TitleBar.qml
new file mode 100644
index 00000000..46d641e6
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/TitleBar.qml
@@ -0,0 +1,87 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+
+Rectangle {
+ id: titleBar
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: GameSettings.fieldHeight
+ color: GameSettings.viewColor
+
+ property var __titles: ["CONNECT", "MEASURE", "STATS"]
+ property int currentIndex: 0
+
+ signal titleClicked(int index)
+
+ Repeater {
+ model: 3
+ Text {
+ width: titleBar.width / 3
+ height: titleBar.height
+ x: index * width
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: __titles[index]
+ font.pixelSize: GameSettings.tinyFontSize
+ color: titleBar.currentIndex === index ? GameSettings.textColor : GameSettings.disabledTextColor
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: titleClicked(index)
+ }
+ }
+ }
+
+
+ Item {
+ anchors.bottom: parent.bottom
+ width: parent.width / 3
+ height: parent.height
+ x: currentIndex * width
+
+ BottomLine{}
+
+ Behavior on x { NumberAnimation { duration: 200 } }
+ }
+
+}
diff --git a/examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png b/examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png
new file mode 100644
index 00000000..5ea1f3f0
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png
Binary files differ
diff --git a/examples/bluetooth/heartrate-game/qml/images/heart.png b/examples/bluetooth/heartrate-game/qml/images/heart.png
new file mode 100644
index 00000000..f2b3c0a3
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/images/heart.png
Binary files differ
diff --git a/examples/bluetooth/heartrate-game/qml/images/logo.png b/examples/bluetooth/heartrate-game/qml/images/logo.png
new file mode 100644
index 00000000..ea0af7e0
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/images/logo.png
Binary files differ
diff --git a/examples/bluetooth/heartrate-game/qml/main.qml b/examples/bluetooth/heartrate-game/qml/main.qml
new file mode 100644
index 00000000..66649a7a
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/main.qml
@@ -0,0 +1,95 @@
+/***************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.7
+import QtQuick.Window 2.2
+import "."
+
+Window {
+ id: wroot
+ visible: true
+ width: 720 * .7
+ height: 1240 * .7
+ title: qsTr("HeartRateGame")
+ color: GameSettings.backgroundColor
+
+ Component.onCompleted: {
+ GameSettings.wWidth = Qt.binding(function() {return width})
+ GameSettings.wHeight = Qt.binding(function() {return height})
+ }
+
+ Loader {
+ id: splashLoader
+ anchors.fill: parent
+ source: "SplashScreen.qml"
+ asynchronous: false
+ visible: true
+
+ onStatusChanged: {
+ if (status === Loader.Ready) {
+ appLoader.setSource("App.qml");
+ }
+ }
+ }
+
+ Connections {
+ target: splashLoader.item
+ onReadyToGo: {
+ appLoader.visible = true
+ appLoader.item.init()
+ splashLoader.visible = false
+ splashLoader.setSource("")
+ appLoader.item.forceActiveFocus();
+ }
+ }
+
+ Loader {
+ id: appLoader
+ anchors.fill: parent
+ visible: false
+ asynchronous: true
+ onStatusChanged: {
+ if (status === Loader.Ready)
+ splashLoader.item.appReady()
+ if (status === Loader.Error)
+ splashLoader.item.errorInLoadingApp();
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate-game/qml/qmldir b/examples/bluetooth/heartrate-game/qml/qmldir
new file mode 100644
index 00000000..5e0d2b54
--- /dev/null
+++ b/examples/bluetooth/heartrate-game/qml/qmldir
@@ -0,0 +1 @@
+singleton GameSettings 1.0 GameSettings.qml
diff --git a/examples/bluetooth/heartrate-server/doc/src/heartrate-server.qdoc b/examples/bluetooth/heartrate-server/doc/src/heartrate-server.qdoc
index 22c6fcd1..34f1b633 100644
--- a/examples/bluetooth/heartrate-server/doc/src/heartrate-server.qdoc
+++ b/examples/bluetooth/heartrate-server/doc/src/heartrate-server.qdoc
@@ -51,7 +51,7 @@
\endlist
The example implements a server application, which means it has no graphical user interface.
- To visualize what it is doing, you can use the \l {heartlistener}{Heart Listener}
+ To visualize what it is doing, you can use the \l {heartrate-game}{Heart Rate Game}
example, which is basically the client-side counterpart to this application.
\note On Linux, advertising requires privileged access, so you need to run
diff --git a/examples/bluetooth/heartrate-server/main.cpp b/examples/bluetooth/heartrate-server/main.cpp
index 779dbb6a..5822831c 100644
--- a/examples/bluetooth/heartrate-server/main.cpp
+++ b/examples/bluetooth/heartrate-server/main.cpp
@@ -49,11 +49,13 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qlist.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qtimer.h>
int main(int argc, char *argv[])
{
+ //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
QCoreApplication app(argc, argv);
//! [Advertising Data]
diff --git a/src/android/bluetooth/bluetooth.pri b/src/android/bluetooth/bluetooth.pri
index 16d3b612..fa811ac1 100644
--- a/src/android/bluetooth/bluetooth.pri
+++ b/src/android/bluetooth/bluetooth.pri
@@ -1,6 +1,6 @@
CONFIG += java
DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar
-API_VERSION = android-18
+API_VERSION = android-21
PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/bluetooth
@@ -9,7 +9,8 @@ JAVASOURCES += \
$$PATHPREFIX/QtBluetoothBroadcastReceiver.java \
$$PATHPREFIX/QtBluetoothSocketServer.java \
$$PATHPREFIX/QtBluetoothInputStreamThread.java \
- $$PATHPREFIX/QtBluetoothLE.java
+ $$PATHPREFIX/QtBluetoothLE.java \
+ $$PATHPREFIX/QtBluetoothLEServer.java
# install
target.path = $$[QT_INSTALL_PREFIX]/jar
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
index d2373051..59ed0992 100644
--- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
+++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java
@@ -53,6 +53,7 @@ import android.os.Looper;
import android.util.Log;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;
@@ -61,6 +62,7 @@ import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
+
public class QtBluetoothLE {
private static final String TAG = "QtBluetoothGatt";
private final BluetoothAdapter mBluetoothAdapter;
@@ -1315,6 +1317,32 @@ public class QtBluetoothLE {
return modifiedHandle;
}
+ // Directly called from public Qt API
+ public boolean requestConnectionUpdatePriority(double minimalInterval)
+ {
+ if (mBluetoothGatt == null)
+ return false;
+
+ try {
+ //Android API v21
+ Method connectionUpdateMethod = mBluetoothGatt.getClass().getDeclaredMethod(
+ "requestConnectionPriority", int.class);
+ if (connectionUpdateMethod == null)
+ return false;
+
+ int requestPriority = 0; // BluetoothGatt.CONNECTION_PRIORITY_BALANCED
+ if (minimalInterval < 30)
+ requestPriority = 1; // BluetoothGatt.CONNECTION_PRIORITY_HIGH
+ else if (minimalInterval > 100)
+ requestPriority = 2; //BluetoothGatt/CONNECTION_PRIORITY_LOW_POWER
+
+ Object result = connectionUpdateMethod.invoke(mBluetoothGatt, requestPriority);
+ return (Boolean) result;
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
public native void leConnectionStateChange(long qtObject, int wasErrorTransition, int newState);
public native void leServicesDiscovered(long qtObject, int errorCode, String uuidList);
public native void leServiceDetailDiscoveryFinished(long qtObject, final String serviceUuid,
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java
new file mode 100644
index 00000000..00217904
--- /dev/null
+++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java
@@ -0,0 +1,585 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2016 The Qt Company Ltd.
+ ** Contact: https://www.qt.io/licensing/
+ **
+ ** This file is part of the QtBluetooth module of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** 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 https://www.qt.io/terms-conditions. For further
+ ** information use the contact form at https://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 3 as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.LGPL3 included in the
+ ** packaging of this file. Please review the following information to
+ ** ensure the GNU Lesser General Public License version 3 requirements
+ ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU
+ ** General Public License version 2.0 or (at your option) the GNU General
+ ** Public license version 3 or any later version approved by the KDE Free
+ ** Qt Foundation. The licenses are as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+ ** included in the packaging of this file. Please review the following
+ ** information to ensure the GNU General Public License requirements will
+ ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+ ** https://www.gnu.org/licenses/gpl-3.0.html.
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+package org.qtproject.qt5.android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.content.Context;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattServer;
+import android.bluetooth.BluetoothGattServerCallback;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseData.Builder;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.HashMap;
+import java.util.UUID;
+
+public class QtBluetoothLEServer {
+ private static final String TAG = "QtBluetoothGattServer";
+
+ /* Pointer to the Qt object that "owns" the Java object */
+ @SuppressWarnings({"CanBeFinal", "WeakerAccess"})
+ long qtObject = 0;
+ @SuppressWarnings("WeakerAccess")
+
+ private Context qtContext = null;
+
+ // Bluetooth members
+ private final BluetoothAdapter mBluetoothAdapter;
+ private BluetoothGattServer mGattServer = null;
+ private BluetoothLeAdvertiser mLeAdvertiser = null;
+
+ /*
+ As per Bluetooth specification each connected device can have individual and persistent
+ Client characteristic configurations (see Bluetooth Spec 5.0 Vol 3 Part G 3.3.3.3)
+ This class manages the existing configurrations.
+ */
+ private class ClientCharacteristicManager {
+ private final HashMap<BluetoothGattCharacteristic, List<Entry>> notificationStore = new HashMap<BluetoothGattCharacteristic, List<Entry>>();
+
+ private class Entry {
+ BluetoothDevice device = null;
+ byte[] value = null;
+ boolean isConnected = false;
+ }
+
+ public void insertOrUpdate(BluetoothGattCharacteristic characteristic,
+ BluetoothDevice device, byte[] newValue)
+ {
+ if (notificationStore.containsKey(characteristic)) {
+
+ List<Entry> entries = notificationStore.get(characteristic);
+ for (int i = 0; i < entries.size(); i++) {
+ if (entries.get(i).device.equals(device)) {
+ Entry e = entries.get(i);
+ e.value = newValue;
+ entries.set(i, e);
+ return;
+ }
+ }
+
+ // not match so far -> add device to list
+ Entry e = new Entry();
+ e.device = device;
+ e.value = newValue;
+ e.isConnected = true;
+ entries.add(e);
+ return;
+ }
+
+ // new characteristic
+ Entry e = new Entry();
+ e.device = device;
+ e.value = newValue;
+ e.isConnected = true;
+ List<Entry> list = new LinkedList<Entry>();
+ list.add(e);
+ notificationStore.put(characteristic, list);
+ }
+
+ /*
+ Marks client characteristic configuration entries as (in)active based the associated
+ devices general connectivity state.
+ This function avoids that existing configurations are not acted
+ upon when the associated device is not connected.
+ */
+ public void markDeviceConnectivity(BluetoothDevice device, boolean isConnected)
+ {
+ final Iterator<BluetoothGattCharacteristic> keys = notificationStore.keySet().iterator();
+ while (keys.hasNext()) {
+ final BluetoothGattCharacteristic characteristic = keys.next();
+ final List<Entry> entries = notificationStore.get(characteristic);
+ if (entries == null)
+ continue;
+
+ ListIterator<Entry> charConfig = entries.listIterator();
+ while (charConfig.hasNext()) {
+ Entry e = charConfig.next();
+ if (e.device.equals(device))
+ e.isConnected = isConnected;
+ }
+ }
+ }
+
+ // Returns list of all BluetoothDevices which require notification or indication.
+ // No match returns an empty list.
+ List<BluetoothDevice> getToBeUpdatedDevices(BluetoothGattCharacteristic characteristic)
+ {
+ ArrayList<BluetoothDevice> result = new ArrayList<BluetoothDevice>();
+ if (!notificationStore.containsKey(characteristic))
+ return result;
+
+ final ListIterator<Entry> iter = notificationStore.get(characteristic).listIterator();
+ while (iter.hasNext())
+ result.add(iter.next().device);
+
+ return result;
+ }
+
+ // Returns null if no match; otherwise the configured actual client characteristic
+ // configuration value
+ byte[] valueFor(BluetoothGattCharacteristic characteristic, BluetoothDevice device)
+ {
+ if (!notificationStore.containsKey(characteristic))
+ return null;
+
+ List<Entry> entries = notificationStore.get(characteristic);
+ for (int i = 0; i < entries.size(); i++) {
+ final Entry entry = entries.get(i);
+ if (entry.device.equals(device) && entry.isConnected == true)
+ return entries.get(i).value;
+ }
+
+ return null;
+ }
+ }
+
+ private static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_UUID = UUID
+ .fromString("00002902-0000-1000-8000-00805f9b34fb");
+ ClientCharacteristicManager clientCharacteristicManager = new ClientCharacteristicManager();
+
+ public QtBluetoothLEServer(Context context)
+ {
+ qtContext = context;
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ if (mBluetoothAdapter == null || qtContext == null) {
+ Log.w(TAG, "Missing Bluetooth adapter or Qt context. Peripheral role disabled.");
+ return;
+ }
+
+ BluetoothManager manager = (BluetoothManager) qtContext.getSystemService(Context.BLUETOOTH_SERVICE);
+ if (manager == null) {
+ Log.w(TAG, "Bluetooth service not available.");
+ return;
+ }
+
+ mGattServer = manager.openGattServer(qtContext, mGattServerListener);
+ mLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
+
+ if (!mBluetoothAdapter.isMultipleAdvertisementSupported())
+ Log.w(TAG, "Device does not support Bluetooth Low Energy advertisement.");
+ else
+ Log.w(TAG, "Let's do BTLE Peripheral.");
+ }
+
+ /*
+ * Call back handler for the Gatt Server.
+ */
+ private BluetoothGattServerCallback mGattServerListener = new BluetoothGattServerCallback()
+ {
+ @Override
+ public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
+ Log.w(TAG, "Our gatt server connection state changed, new state: " + newState);
+ super.onConnectionStateChange(device, status, newState);
+
+ int qtControllerState = 0;
+ switch (newState) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ qtControllerState = 0; // QLowEnergyController::UnconnectedState
+ clientCharacteristicManager.markDeviceConnectivity(device, false);
+ break;
+ case BluetoothProfile.STATE_CONNECTED:
+ clientCharacteristicManager.markDeviceConnectivity(device, true);
+ qtControllerState = 2; // QLowEnergyController::ConnectedState
+ break;
+ }
+
+ int qtErrorCode;
+ switch (status) {
+ case BluetoothGatt.GATT_SUCCESS:
+ qtErrorCode = 0; break;
+ default:
+ Log.w(TAG, "Unhandled error code on peripheral connectionStateChanged: " + status + " " + newState);
+ qtErrorCode = status;
+ break;
+ }
+
+ leServerConnectionStateChange(qtObject, qtErrorCode, qtControllerState);
+ }
+
+ @Override
+ public void onServiceAdded(int status, BluetoothGattService service) {
+ super.onServiceAdded(status, service);
+ }
+
+ @Override
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic)
+ {
+ byte[] dataArray;
+ try {
+ dataArray = Arrays.copyOfRange(characteristic.getValue(), offset, characteristic.getValue().length);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, dataArray);
+ } catch (Exception ex) {
+ Log.w(TAG, "onCharacteristicReadRequest: " + requestId + " " + offset + " " + characteristic.getValue().length);
+ ex.printStackTrace();
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, offset, null);
+ }
+
+ super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
+ }
+
+ @Override
+ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic,
+ boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)
+ {
+ Log.w(TAG, "onCharacteristicWriteRequest");
+ int resultStatus = BluetoothGatt.GATT_SUCCESS;
+ boolean sendNotificationOrIndication = false;
+ if (!preparedWrite) { // regular write
+ if (offset == 0) {
+ characteristic.setValue(value);
+ leServerCharacteristicChanged(qtObject, characteristic, value);
+ sendNotificationOrIndication = true;
+ } else {
+ // This should not really happen as per Bluetooth spec
+ Log.w(TAG, "onCharacteristicWriteRequest: !preparedWrite, offset " + offset + ", Not supported");
+ resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
+ }
+
+
+ } else {
+ Log.w(TAG, "onCharacteristicWriteRequest: preparedWrite, offset " + offset + ", Not supported");
+ resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
+
+ // TODO we need to record all requests and execute them in one go once onExecuteWrite() is received
+ // we use a queue to remember the pending requests
+ // TODO we are ignoring the device identificator for now -> Bluetooth spec requires a queue per device
+ }
+
+
+ if (responseNeeded)
+ mGattServer.sendResponse(device, requestId, resultStatus, offset, value);
+ if (sendNotificationOrIndication)
+ sendNotificationsOrIndications(characteristic);
+
+ super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
+ }
+
+ @Override
+ public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor)
+ {
+ byte[] dataArray = descriptor.getValue();
+ try {
+ if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
+ dataArray = clientCharacteristicManager.valueFor(descriptor.getCharacteristic(), device);
+ if (dataArray == null)
+ dataArray = descriptor.getValue();
+ }
+
+ dataArray = Arrays.copyOfRange(dataArray, offset, dataArray.length);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, dataArray);
+ } catch (Exception ex) {
+ Log.w(TAG, "onDescriptorReadRequest: " + requestId + " " + offset + " " + dataArray.length);
+ ex.printStackTrace();
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, offset, null);
+ }
+
+ super.onDescriptorReadRequest(device, requestId, offset, descriptor);
+ }
+
+ @Override
+ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor,
+ boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)
+ {
+ int resultStatus = BluetoothGatt.GATT_SUCCESS;
+ if (!preparedWrite) { // regular write
+ if (offset == 0) {
+ descriptor.setValue(value);
+
+ if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) {
+ clientCharacteristicManager.insertOrUpdate(descriptor.getCharacteristic(),
+ device, value);
+ }
+
+ leServerDescriptorWritten(qtObject, descriptor, value);
+ } else {
+ // This should not really happen as per Bluetooth spec
+ Log.w(TAG, "onDescriptorWriteRequest: !preparedWrite, offset " + offset + ", Not supported");
+ resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
+ }
+
+
+ } else {
+ Log.w(TAG, "onDescriptorWriteRequest: preparedWrite, offset " + offset + ", Not supported");
+ resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED;
+ // TODO we need to record all requests and execute them in one go once onExecuteWrite() is received
+ // we use a queue to remember the pending requests
+ // TODO we are ignoring the device identificator for now -> Bluetooth spec requires a queue per device
+ }
+
+
+ if (responseNeeded)
+ mGattServer.sendResponse(device, requestId, resultStatus, offset, value);
+
+ super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
+ }
+
+ @Override
+ public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute)
+ {
+ // TODO not yet implemented -> return proper GATT error for it
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED, 0, null);
+
+ super.onExecuteWrite(device, requestId, execute);
+ }
+
+ @Override
+ public void onNotificationSent(BluetoothDevice device, int status) {
+ super.onNotificationSent(device, status);
+ Log.w(TAG, "onNotificationSent" + device + " " + status);
+ }
+
+ // MTU change disabled since it requires API level 22. Right now we only enforce lvl 21
+// @Override
+// public void onMtuChanged(BluetoothDevice device, int mtu) {
+// super.onMtuChanged(device, mtu);
+// }
+ };
+
+ public boolean connectServer()
+ {
+ if (mGattServer == null)
+ return false;
+
+ return true;
+ }
+
+ public void disconnectServer()
+ {
+ if (mGattServer == null)
+ return;
+
+ mGattServer.close();
+ }
+
+ public boolean startAdvertising(AdvertiseData advertiseData,
+ AdvertiseData scanResponse,
+ AdvertiseSettings settings)
+ {
+ if (mLeAdvertiser == null)
+ return false;
+
+ connectServer();
+
+ Log.w(TAG, "Starting to advertise.");
+ mLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseListener);
+
+ return true;
+ }
+
+ public void stopAdvertising()
+ {
+ if (mLeAdvertiser == null)
+ return;
+
+ mLeAdvertiser.stopAdvertising(mAdvertiseListener);
+ Log.w(TAG, "Advertisement stopped.");
+ }
+
+ public void addService(BluetoothGattService service)
+ {
+ if (mGattServer == null)
+ return;
+
+ mGattServer.addService(service);
+ }
+
+ /*
+ Check the client characteristics configuration for the given characteristic
+ and sends notifications or indications as per required.
+ */
+ private void sendNotificationsOrIndications(BluetoothGattCharacteristic characteristic)
+ {
+ final ListIterator<BluetoothDevice> iter =
+ clientCharacteristicManager.getToBeUpdatedDevices(characteristic).listIterator();
+
+ // TODO This quick loop over multiple devices should be synced with onNotificationSent().
+ // The next notifyCharacteristicChanged() call must wait until onNotificationSent()
+ // was received. At this becomes an issue when the server accepts multiple remote
+ // devices at the same time.
+ while (iter.hasNext()) {
+ final BluetoothDevice device = iter.next();
+ final byte[] clientCharacteristicConfig = clientCharacteristicManager.valueFor(characteristic, device);
+ if (clientCharacteristicConfig != null) {
+ if (Arrays.equals(clientCharacteristicConfig, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
+ mGattServer.notifyCharacteristicChanged(device, characteristic, false);
+ } else if (Arrays.equals(clientCharacteristicConfig, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
+ mGattServer.notifyCharacteristicChanged(device, characteristic, true);
+ }
+ }
+ }
+ }
+
+ /*
+ Updates the local database value for the given characteristic with \a charUuid and
+ \a newValue. If notifications for this task are enabled an approproiate notification will
+ be send to the remote client.
+
+ This function is called from the Qt thread.
+ */
+ public boolean writeCharacteristic(BluetoothGattService service, UUID charUuid, byte[] newValue)
+ {
+ BluetoothGattCharacteristic foundChar = null;
+ List<BluetoothGattCharacteristic> charList = service.getCharacteristics();
+ for (BluetoothGattCharacteristic iter: charList) {
+ if (iter.getUuid().equals(charUuid) && foundChar == null) {
+ foundChar = iter;
+ // don't break here since we want to check next condition below on next iteration
+ } else if (iter.getUuid().equals(charUuid)) {
+ Log.w(TAG, "Found second char with same UUID. Wrong char may have been selected.");
+ break;
+ }
+ }
+
+ if (foundChar == null) {
+ Log.w(TAG, "writeCharacteristic: update for unknown characteristic failed");
+ return false;
+ }
+
+ foundChar.setValue(newValue);
+ sendNotificationsOrIndications(foundChar);
+
+ return true;
+ }
+
+ /*
+ Updates the local database value for the given \a descUuid to \a newValue.
+
+ This function is called from the Qt thread.
+ */
+ public boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid,
+ byte[] newValue)
+ {
+ BluetoothGattDescriptor foundDesc = null;
+ BluetoothGattCharacteristic foundChar = null;
+ final List<BluetoothGattCharacteristic> charList = service.getCharacteristics();
+ for (BluetoothGattCharacteristic iter: charList) {
+ if (!iter.getUuid().equals(charUuid))
+ continue;
+
+ if (foundChar == null) {
+ foundChar = iter;
+ } else {
+ Log.w(TAG, "Found second char with same UUID. Wrong char may have been selected.");
+ break;
+ }
+ }
+
+ if (foundChar != null)
+ foundDesc = foundChar.getDescriptor(descUuid);
+
+ if (foundChar == null || foundDesc == null) {
+ Log.w(TAG, "writeDescriptor: update for unknown char or desc failed (" + foundChar + ")");
+ return false;
+ }
+
+ // we even write CLIENT_CHARACTERISTIC_CONFIGURATION_UUID this way as we choose
+ // to interpret the server's call as a change of the default value.
+ foundDesc.setValue(newValue);
+
+ return true;
+ }
+
+ /*
+ * Call back handler for Advertisement requests.
+ */
+ private AdvertiseCallback mAdvertiseListener = new AdvertiseCallback()
+ {
+ @Override
+ public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+ super.onStartSuccess(settingsInEffect);
+ }
+
+ @Override
+ public void onStartFailure(int errorCode) {
+ Log.e(TAG, "Advertising failure: " + errorCode);
+ super.onStartFailure(errorCode);
+
+ // changing errorCode here implies changes to errorCode handling on Qt side
+ int qtErrorCode = 0;
+ switch (errorCode) {
+ case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
+ return; // ignore -> noop
+ case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
+ qtErrorCode = 1;
+ break;
+ case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
+ qtErrorCode = 2;
+ break;
+ default: // default maps to internal error
+ case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
+ qtErrorCode = 3;
+ break;
+ case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
+ qtErrorCode = 4;
+ break;
+ }
+
+ if (qtErrorCode > 0)
+ leServerAdvertisementError(qtObject, qtErrorCode);
+ }
+ };
+
+ public native void leServerConnectionStateChange(long qtObject, int errorCode, int newState);
+ public native void leServerAdvertisementError(long qtObject, int status);
+ public native void leServerCharacteristicChanged(long qtObject,
+ BluetoothGattCharacteristic characteristic,
+ byte[] newValue);
+ public native void leServerDescriptorWritten(long qtObject,
+ BluetoothGattDescriptor descriptor,
+ byte[] newValue);
+}
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
index 47dcf1bf..345b87d3 100644
--- a/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java
+++ b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java
@@ -62,22 +62,25 @@ public class 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 Context m_context = null;
+ static public Activity m_activity = null;
static public void setContext(Context context)
{
- if (!(context instanceof Activity)) {
- Log.w(TAG, "NFC only works with Android activities and not in Android services. " +
- "NFC has been disabled.");
+ m_context = context;
+ if (context instanceof Activity) m_activity = (Activity) context;
+ m_adapter = NfcAdapter.getDefaultAdapter(context);
+
+ if (m_activity == null) {
+ Log.w(TAG, "New NFC tags will only be recognized with Android activities and not with Android services.");
return;
}
- m_activity = (Activity)context;
- m_adapter = NfcAdapter.getDefaultAdapter(m_activity);
if (m_adapter == null) {
//Log.e(TAG, "No NFC available");
return;
}
+
m_pendingIntent = PendingIntent.getActivity(
m_activity,
0,
@@ -86,7 +89,7 @@ public class QtNfc
//Log.d(TAG, "Pending intent:" + m_pendingIntent);
- IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
+ IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
m_filters = new IntentFilter[]{
filter
@@ -103,22 +106,26 @@ public class QtNfc
static public boolean start()
{
- if (m_adapter == null) return false;
+ if (m_adapter == null || m_activity == null) return false;
+
m_activity.runOnUiThread(new Runnable() {
public void run() {
//Log.d(TAG, "Enabling NFC");
- IntentFilter[] filters = new IntentFilter[2];
+ IntentFilter[] filters = new IntentFilter[3];
filters[0] = new IntentFilter();
- filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
+ filters[0].addAction(NfcAdapter.ACTION_TAG_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
+ filters[1] = new IntentFilter();
+ filters[1].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
+ filters[1].addCategory(Intent.CATEGORY_DEFAULT);
try {
- filters[0].addDataType("*/*");
+ filters[1].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);
+ filters[2] = new IntentFilter();
+ filters[2].addAction(NfcAdapter.ACTION_TECH_DISCOVERED);
String[][] techList = new String[][]{
{"android.nfc.tech.Ndef"},
{"android.nfc.tech.NdefFormatable"}
@@ -136,7 +143,8 @@ public class QtNfc
static public boolean stop()
{
- if (m_adapter == null) return false;
+ if (m_adapter == null || m_activity == null) return false;
+
m_activity.runOnUiThread(new Runnable() {
public void run() {
//Log.d(TAG, "Disabling NFC");
@@ -153,11 +161,11 @@ public class QtNfc
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();
}
@@ -165,6 +173,7 @@ public class QtNfc
{
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()) ||
diff --git a/src/bluetooth/android/jni_android.cpp b/src/bluetooth/android/jni_android.cpp
index ae07608a..68e291e5 100644
--- a/src/bluetooth/android/jni_android.cpp
+++ b/src/bluetooth/android/jni_android.cpp
@@ -227,6 +227,17 @@ static JNINativeMethod methods_le[] = {
(void *) LowEnergyNotificationHub::lowEnergy_serviceError},
};
+static JNINativeMethod methods_leServer[] = {
+ {"leServerConnectionStateChange", "(JII)V",
+ (void *) LowEnergyNotificationHub::lowEnergy_connectionChange},
+ {"leServerAdvertisementError", "(JI)V",
+ (void *) LowEnergyNotificationHub::lowEnergy_advertisementError},
+ {"leServerCharacteristicChanged", "(JLandroid/bluetooth/BluetoothGattCharacteristic;[B)V",
+ (void *) LowEnergyNotificationHub::lowEnergy_serverCharacteristicChanged},
+ {"leServerDescriptorWritten", "(JLandroid/bluetooth/BluetoothGattDescriptor;[B)V",
+ (void *) LowEnergyNotificationHub::lowEnergy_serverDescriptorWritten},
+};
+
static JNINativeMethod methods_server[] = {
{"errorOccurred", "(JI)V",
(void *) QtBluetoothSocketServer_errorOccurred},
@@ -267,6 +278,11 @@ static bool registerNatives(JNIEnv *env)
return false;
}
+ FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer");
+ if (env->RegisterNatives(clazz, methods_leServer, sizeof(methods_leServer) / sizeof(methods_leServer[0])) < 0) {
+ __android_log_print(ANDROID_LOG_FATAL, logTag, "RegisterNatives for QBLuetoothLEServer failed");
+ return false;
+ }
FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/bluetooth/QtBluetoothSocketServer");
if (env->RegisterNatives(clazz, methods_server, sizeof(methods_server) / sizeof(methods_server[0])) < 0) {
diff --git a/src/bluetooth/android/lowenergynotificationhub.cpp b/src/bluetooth/android/lowenergynotificationhub.cpp
index 2a6cdce3..53f2d6cc 100644
--- a/src/bluetooth/android/lowenergynotificationhub.cpp
+++ b/src/bluetooth/android/lowenergynotificationhub.cpp
@@ -53,18 +53,25 @@ QReadWriteLock LowEnergyNotificationHub::lock;
Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
-LowEnergyNotificationHub::LowEnergyNotificationHub(
- const QBluetoothAddress &remote, QObject *parent)
+LowEnergyNotificationHub::LowEnergyNotificationHub(const QBluetoothAddress &remote,
+ bool isPeripheral, QObject *parent)
: QObject(parent), javaToCtoken(0)
{
QAndroidJniEnvironment env;
- const QAndroidJniObject address =
+
+ if (isPeripheral) {
+ qCDebug(QT_BT_ANDROID) << "Creating Android Peripheral/Server support for BTLE";
+ jBluetoothLe = QAndroidJniObject("org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer",
+ "(Landroid/content/Context;)V", QtAndroidPrivate::context());
+ } else {
+ qCDebug(QT_BT_ANDROID) << "Creating Android Central/Client support for BTLE";
+ const QAndroidJniObject address =
QAndroidJniObject::fromString(remote.toString());
- jBluetoothLe = QAndroidJniObject("org/qtproject/qt5/android/bluetooth/QtBluetoothLE",
+ jBluetoothLe = QAndroidJniObject("org/qtproject/qt5/android/bluetooth/QtBluetoothLE",
"(Ljava/lang/String;Landroid/content/Context;)V",
address.object<jstring>(),
QtAndroidPrivate::activity() ? QtAndroidPrivate::activity() : QtAndroidPrivate::service());
-
+ }
if (env->ExceptionCheck() || !jBluetoothLe.isValid()) {
env->ExceptionDescribe();
@@ -266,6 +273,28 @@ void LowEnergyNotificationHub::lowEnergy_descriptorWritten(
(QLowEnergyService::ServiceError)errorCode));
}
+void LowEnergyNotificationHub::lowEnergy_serverDescriptorWritten(
+ JNIEnv *env, jobject, jlong qtObject, jobject descriptor, jbyteArray newValue)
+{
+ lock.lockForRead();
+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject);
+ lock.unlock();
+ if (!hub)
+ return;
+
+ QByteArray payload;
+ if (newValue) { //empty Java byte array is 0x0
+ jsize length = env->GetArrayLength(newValue);
+ payload.resize(length);
+ env->GetByteArrayRegion(newValue, 0, length,
+ reinterpret_cast<signed char*>(payload.data()));
+ }
+
+ QMetaObject::invokeMethod(hub, "serverDescriptorWritten", Qt::QueuedConnection,
+ Q_ARG(QAndroidJniObject, descriptor),
+ Q_ARG(QByteArray, payload));
+}
+
void LowEnergyNotificationHub::lowEnergy_characteristicChanged(
JNIEnv *env, jobject, jlong qtObject, jint charHandle, jbyteArray data)
{
@@ -287,6 +316,28 @@ void LowEnergyNotificationHub::lowEnergy_characteristicChanged(
Q_ARG(int, charHandle), Q_ARG(QByteArray, payload));
}
+void LowEnergyNotificationHub::lowEnergy_serverCharacteristicChanged(
+ JNIEnv *env, jobject, jlong qtObject, jobject characteristic, jbyteArray newValue)
+{
+ lock.lockForRead();
+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject);
+ lock.unlock();
+ if (!hub)
+ return;
+
+ QByteArray payload;
+ if (newValue) { //empty Java byte array is 0x0
+ jsize length = env->GetArrayLength(newValue);
+ payload.resize(length);
+ env->GetByteArrayRegion(newValue, 0, length,
+ reinterpret_cast<signed char*>(payload.data()));
+ }
+
+ QMetaObject::invokeMethod(hub, "serverCharacteristicChanged", Qt::QueuedConnection,
+ Q_ARG(QAndroidJniObject, characteristic),
+ Q_ARG(QByteArray, payload));
+}
+
void LowEnergyNotificationHub::lowEnergy_serviceError(
JNIEnv *, jobject, jlong qtObject, jint attributeHandle, int errorCode)
{
@@ -302,4 +353,17 @@ void LowEnergyNotificationHub::lowEnergy_serviceError(
(QLowEnergyService::ServiceError)errorCode));
}
+void LowEnergyNotificationHub::lowEnergy_advertisementError(
+ JNIEnv *, jobject, jlong qtObject, jint status)
+{
+ lock.lockForRead();
+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject);
+ lock.unlock();
+ if (!hub)
+ return;
+
+ QMetaObject::invokeMethod(hub, "advertisementError", Qt::QueuedConnection,
+ Q_ARG(int, status));
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/android/lowenergynotificationhub_p.h b/src/bluetooth/android/lowenergynotificationhub_p.h
index a957aac0..4a384a8c 100644
--- a/src/bluetooth/android/lowenergynotificationhub_p.h
+++ b/src/bluetooth/android/lowenergynotificationhub_p.h
@@ -67,7 +67,7 @@ class LowEnergyNotificationHub : public QObject
{
Q_OBJECT
public:
- explicit LowEnergyNotificationHub(const QBluetoothAddress &remote,
+ explicit LowEnergyNotificationHub(const QBluetoothAddress &remote, bool isPeripheral,
QObject *parent = 0);
~LowEnergyNotificationHub();
@@ -91,10 +91,16 @@ public:
static void lowEnergy_descriptorWritten(JNIEnv *, jobject, jlong qtObject,
jint descHandle, jbyteArray data,
jint errorCode);
+ static void lowEnergy_serverDescriptorWritten(JNIEnv *, jobject, jlong qtObject,
+ jobject descriptor, jbyteArray newValue);
static void lowEnergy_characteristicChanged(JNIEnv *, jobject, jlong qtObject,
jint charHandle, jbyteArray data);
+ static void lowEnergy_serverCharacteristicChanged(JNIEnv *, jobject, jlong qtObject,
+ jobject characteristic, jbyteArray newValue);
static void lowEnergy_serviceError(JNIEnv *, jobject, jlong qtObject,
jint attributeHandle, int errorCode);
+ static void lowEnergy_advertisementError(JNIEnv *, jobject, jlong qtObject,
+ jint status);
QAndroidJniObject javaObject()
{
@@ -116,8 +122,11 @@ signals:
QLowEnergyService::ServiceError errorCode);
void descriptorWritten(int descHandle, const QByteArray &data,
QLowEnergyService::ServiceError errorCode);
+ void serverDescriptorWritten(const QAndroidJniObject &descriptor, const QByteArray &newValue);
void characteristicChanged(int charHandle, const QByteArray &data);
+ void serverCharacteristicChanged(const QAndroidJniObject &characteristic, const QByteArray &newValue);
void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode);
+ void advertisementError(int status);
public slots:
private:
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index bf2dce74..6d3dacc3 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -78,9 +78,8 @@ SOURCES += \
qlowenergycontroller.cpp \
qlowenergyserviceprivate.cpp
-qtConfig(bluez):qtHaveModule(dbus) {
+qtConfig(bluez) {
QT_FOR_PRIVATE += dbus
- DEFINES += QT_BLUEZ_BLUETOOTH
include(bluez/bluez.pri)
@@ -190,21 +189,25 @@ qtConfig(bluez):qtHaveModule(dbus) {
SOURCES -= qlowenergycontroller_p.cpp
SOURCES -= qlowenergyservice.cpp
SOURCES -= qlowenergycontroller.cpp
-} else:if(winphone|winrt-*-msvc2015) {
+} else:winrt {
DEFINES += QT_WINRT_BLUETOOTH
QT += core-private
- # remove dummy warning once platform port is complete
- include(dummy/dummy.pri)
-
SOURCES += \
qbluetoothdevicediscoveryagent_winrt.cpp \
qbluetoothlocaldevice_p.cpp \
- qbluetoothserver_p.cpp \
- qbluetoothservicediscoveryagent_p.cpp \
- qbluetoothserviceinfo_p.cpp \
- qbluetoothsocket_p.cpp \
+ qbluetoothserver_winrt.cpp \
+ qbluetoothservicediscoveryagent_winrt.cpp \
+ qbluetoothserviceinfo_winrt.cpp \
+ qbluetoothsocket_winrt.cpp \
qlowenergycontroller_winrt.cpp
+
+ WINRT_SDK_VERSION_STRING = $$(UCRTVersion)
+ WINRT_SDK_VERSION = $$member($$list($$split(WINRT_SDK_VERSION_STRING, .)), 2)
+ lessThan(WINRT_SDK_VERSION, 14393) {
+ DEFINES += QT_WINRT_LIMITED_SERVICEDISCOVERY
+ DEFINES += QT_UCRTVERSION=$$WINRT_SDK_VERSION
+ }
} else {
message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.")
message("Either no Qt D-Bus found or no BlueZ headers available.")
diff --git a/src/bluetooth/configure.json b/src/bluetooth/configure.json
index 1cf43f36..e348030b 100644
--- a/src/bluetooth/configure.json
+++ b/src/bluetooth/configure.json
@@ -29,8 +29,8 @@
"features": {
"bluez": {
"label": "BlueZ",
- "condition": "libs.bluez",
- "output": [ "privateFeature" ]
+ "condition": "libs.bluez && features.dbus",
+ "output": [ "publicFeature" ]
},
"bluez_le": {
"label": "BlueZ Low Energy",
diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc
index 92b846b4..804b6763 100644
--- a/src/bluetooth/doc/src/bluetooth-index.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-index.qdoc
@@ -50,7 +50,7 @@ Currently, the API is supported on the following platforms:
\li x
\li x
\li x
- \li
+ \li x
\li
\row
\li Bluetooth LE Central
@@ -62,7 +62,7 @@ Currently, the API is supported on the following platforms:
\li
\row
\li Bluetooth LE Peripheral
- \li
+ \li x
\li x
\li x
\li x
@@ -146,18 +146,32 @@ The \l QtBluetooth module exports the following
\li Enables logging of cross platform code path in QtBluetooth
\row
\li qt.bluetooth.android
- \li Enables logging of the Android implementation
+ \li Enables logging of the \l {Qt for Android} {Android} implementation
\row
\li qt.bluetooth.bluez
\li Enables logging of the BLuez/Linux implementation
\row
\li qt.bluetooth.ios
- \li Enables logging of the iOS implementation
+ \li Enables logging of the \l {Qt for iOS} {iOS} implementation
\row
\li qt.bluetooth.osx
- \li Enables logging of the \macos implementation
+ \li Enables logging of the \l {Qt for macOS} {macOS} implementation
+\row
+ \li qt.bluetooth.qml
+ \li Enables logging of the QtBluetooth QML implementation
+\row
+ \li qt.bluetooth.winrt
+ \li Enables logging of the \l {Qt for WinRT} {WinRT} implementation
\endtable
+Logging categories can be used to enable additional warning and debug output
+for QtBluetooth. More detailed information about logging can be found in \l QLoggingCategory.
+A quick way to enable all QtBluetooth logging is to add the following line to the \c main() function:
+
+\code
+ QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+\endcode
+
\section2 Examples
\list
\li QML
@@ -166,7 +180,7 @@ The \l QtBluetooth module exports the following
\li \l {picturetransfer}{QML Bluetooth Picture Push}
\li \l {pingpong}{QML Bluetooth PingPong}
\li \l {chat}{QML Bluetooth Chat}
- \li \l {heartlistener}{Bluetooth Low Energy Heart Listener}
+ \li \l {heartrate-game}{Bluetooth Low Energy Heart Rate Game}
\li \l {heartrate-server}{Bluetooth Low Energy Heart Rate Server}
\li \l {lowenergyscanner}{Bluetooth Low Energy Scanner}
\endlist
diff --git a/src/bluetooth/doc/src/bluetooth-le-overview.qdoc b/src/bluetooth/doc/src/bluetooth-le-overview.qdoc
index a2fb10d9..a29f88d0 100644
--- a/src/bluetooth/doc/src/bluetooth-le-overview.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-le-overview.qdoc
@@ -165,7 +165,7 @@ Low Energy devices.
their services, as well as reading and writing data stored on the device.
On the server side, it allows to set up services, advertise them, and get notified when the
client writes characteristics.
- The example code below is taken from the \l {heartlistener}{Heart Listener} and
+ The example code below is taken from the \l {heartrate-game}{Heart Rate Game} and
\l {heartrate-server}{Heart Rate Server} examples.
\section2 Establishing a Connection
@@ -176,15 +176,15 @@ Low Energy devices.
\l QBluetoothDeviceDiscoveryAgent class. We connect to its \l {QBluetoothDeviceDiscoveryAgent::deviceDiscovered()}
signal and start the search with \l {QBluetoothDeviceDiscoveryAgent::start()}{start()}:
- \snippet heartlistener/heartrate.cpp devicediscovery-1
- \snippet heartlistener/heartrate.cpp devicediscovery-2
+ \snippet heartrate-game/devicefinder.cpp devicediscovery-1
+ \snippet heartrate-game/devicefinder.cpp devicediscovery-2
Since we are only interested in Low Energy devices we filter the device type within the
receiving slot. The device type can be ascertained using the \l QBluetoothDeviceInfo::coreConfigurations()
flag:
- \snippet heartlistener/heartrate.cpp devicediscovery-3
- \snippet heartlistener/heartrate.cpp devicediscovery-4
+ \snippet heartrate-game/devicefinder.cpp devicediscovery-3
+ \snippet heartrate-game/devicefinder.cpp devicediscovery-4
Once the address of the peripheral device is known we use the \l QLowEnergyController class.
This class is the entry point for all Bluetooth Low Energy development. The constructor of the class
@@ -192,20 +192,20 @@ Low Energy devices.
directly connect to the device using
\l {QLowEnergyController::connectToDevice()}{connectToDevice()}:
- \snippet heartlistener/heartrate.cpp Connect signals
+ \snippet heartrate-game/devicehandler.cpp Connect-Signals-1
+ \snippet heartrate-game/devicehandler.cpp Connect-Signals-2
\section2 Service Search
- As soon as the connection is established we initiate the service discovery:
-
- \snippet heartlistener/heartrate.cpp Connecting to service
+ The above code snippet how the application initiates the service discovery once the connection has
+ been established.
The \c serviceDiscovered() slot below is triggered as a result of the
\l {QLowEnergyController::serviceDiscovered()} signal and provides an intermittent progress report.
Since we are talking about the heart listener app which monitors HeartRate devices in the vicinity
we ignore any service that is not of type \l QBluetoothUuid::HeartRate.
- \snippet heartlistener/heartrate.cpp Filter HeartRate service 1
+ \snippet heartrate-game/devicehandler.cpp Filter HeartRate service 1
Eventually the \l {QLowEnergyController::discoveryFinished()} signal is emitted to indicate
the successful completion of the service discovery. Provided a HeartRate service was found,
@@ -213,14 +213,14 @@ Low Energy devices.
provides the required signals for update notifications and the discovery of service details
is triggered using \l QLowEnergyService::discoverDetails():
- \snippet heartlistener/heartrate.cpp Filter HeartRate service 2
+ \snippet heartrate-game/devicehandler.cpp Filter HeartRate service 2
During the detail search the service's \l {QLowEnergyService::state()}{state()} transitions
from \l {QLowEnergyService::DiscoveryRequired}{DiscoveryRequired} to
\l {QLowEnergyService::DiscoveringServices}{DiscoveringServices} and eventually ends with
\l {QLowEnergyService::ServiceDiscovered}{ServiceDiscovered}:
- \snippet heartlistener/heartrate.cpp Find HRM characteristic
+ \snippet heartrate-game/devicehandler.cpp Find HRM characteristic
\section2 Interaction with the Peripheral Device
@@ -235,8 +235,7 @@ Low Energy devices.
Finally, we process the value of the HeartRate characteristic, as per Bluetooth Low Energy standard:
- \snippet heartlistener/heartrate.cpp Reading value 1
- \snippet heartlistener/heartrate.cpp Reading value 2
+ \snippet heartrate-game/devicehandler.cpp Reading value
In general a characteristic value is a series of bytes. The precise interpretation of
those bytes depends on the characteristic type and value structure.
diff --git a/src/bluetooth/doc/src/examples.qdoc b/src/bluetooth/doc/src/examples.qdoc
index d21c7932..32152c84 100644
--- a/src/bluetooth/doc/src/examples.qdoc
+++ b/src/bluetooth/doc/src/examples.qdoc
@@ -73,7 +73,7 @@
\li \l{scanner}{QML Bluetooth Scanner}
\li Scan for Bluetooth devices and services.
\row
- \li \l{heartlistener}{QML Bluetooth Low Energy Heart Listener}
+ \li \l{heartrate-game}{QML Bluetooth Low Energy Heart Rate Game}
\li Connect to Bluetooth Low Energy heart rate belts and receive
measurements such as the current pulse.
\row
diff --git a/src/bluetooth/osx/osxbtledeviceinquiry.mm b/src/bluetooth/osx/osxbtledeviceinquiry.mm
index 2dabd0c1..3ce66d49 100644
--- a/src/bluetooth/osx/osxbtledeviceinquiry.mm
+++ b/src/bluetooth/osx/osxbtledeviceinquiry.mm
@@ -364,13 +364,10 @@ QT_USE_NAMESPACE
return;
}
- QString name;
- if (peripheral.name)
- name = QString::fromNSString(peripheral.name);
-
const AdvertisementData qtAdvData(advertisementData);
- if (!name.size()) // Probably, it's not possible to have one and not the other.
- name = qtAdvData.localName;
+ QString name(qtAdvData.localName);
+ if (!name.size() && peripheral.name)
+ name = QString::fromNSString(peripheral.name);
// TODO: fix 'classOfDevice' (0 for now).
QBluetoothDeviceInfo newDeviceInfo(deviceUuid, name, 0);
diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp
index 37a4774d..6eab11fb 100644
--- a/src/bluetooth/qbluetooth.cpp
+++ b/src/bluetooth/qbluetooth.cpp
@@ -101,6 +101,6 @@ namespace QBluetooth {
Q_LOGGING_CATEGORY(QT_BT, "qt.bluetooth")
Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android")
Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez")
-Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winphone")
+Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winrt")
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h
index 84087605..17827656 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h
@@ -120,7 +120,7 @@ private:
Q_DECLARE_PRIVATE(QBluetoothDeviceDiscoveryAgent)
QBluetoothDeviceDiscoveryAgentPrivate *d_ptr;
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
Q_PRIVATE_SLOT(d_func(), void _q_deviceFound(const QString &address, const QVariantMap &dict))
Q_PRIVATE_SLOT(d_func(), void _q_propertyChanged(const QString &name, const QDBusVariant &value))
Q_PRIVATE_SLOT(d_func(), void _q_InterfacesAdded(const QDBusObjectPath &path, InterfaceList interfaceList))
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
index de8006be..45764c1a 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
@@ -63,7 +63,7 @@
#include <QtBluetooth/QBluetoothAddress>
#include <QtBluetooth/QBluetoothLocalDevice>
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
#include "bluez/bluez5_helper_p.h"
class OrgBluezManagerInterface;
@@ -103,7 +103,7 @@ public:
void stop();
bool isActive() const;
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
void _q_deviceFound(const QString &address, const QVariantMap &dict);
void _q_propertyChanged(const QString &name, const QDBusVariant &value);
void _q_InterfacesAdded(const QDBusObjectPath &object_path,
@@ -141,7 +141,7 @@ private:
QTimer *leScanTimeout;
bool pendingCancel, pendingStart;
-#elif defined(QT_BLUEZ_BLUETOOTH)
+#elif QT_CONFIG(bluez)
QBluetoothAddress m_adapterAddress;
bool pendingCancel;
bool pendingStart;
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
index 7813bfdc..5e5364e1 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp
@@ -46,18 +46,15 @@
#include <windows.foundation.collections.h>
#include <windows.storage.streams.h>
-#ifndef Q_OS_WINPHONE
#include <windows.devices.bluetooth.advertisement.h>
-using namespace ABI::Windows::Devices::Bluetooth::Advertisement;
-#endif // !Q_OS_WINPHONE
-
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Devices;
using namespace ABI::Windows::Devices::Bluetooth;
+using namespace ABI::Windows::Devices::Bluetooth::Advertisement;
using namespace ABI::Windows::Devices::Enumeration;
QT_BEGIN_NAMESPACE
@@ -112,11 +109,9 @@ public:
quint8 requestedModes;
private:
-#ifndef Q_OS_WINPHONE
ComPtr<IBluetoothLEAdvertisementWatcher> m_leWatcher;
EventRegistrationToken m_leDeviceAddedToken;
QVector<quint64> m_foundLEDevices;
-#endif // !Q_OS_WINPHONE
int m_pendingPairedDevices;
ComPtr<IBluetoothDeviceStatics> m_deviceStatics;
@@ -158,7 +153,6 @@ void QWinRTBluetoothDeviceDiscoveryWorker::start()
void QWinRTBluetoothDeviceDiscoveryWorker::stop()
{
-#ifndef Q_OS_WINPHONE
if (m_leWatcher) {
HRESULT hr = m_leWatcher->Stop();
Q_ASSERT_SUCCEEDED(hr);
@@ -167,7 +161,6 @@ void QWinRTBluetoothDeviceDiscoveryWorker::stop()
Q_ASSERT_SUCCEEDED(hr);
}
}
-#endif // !Q_OS_WINPHONE
}
void QWinRTBluetoothDeviceDiscoveryWorker::startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
@@ -230,7 +223,6 @@ void QWinRTBluetoothDeviceDiscoveryWorker::gatherMultipleDeviceInformation(IVect
void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
{
-#ifndef Q_OS_WINPHONE
HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher).Get(), &m_leWatcher);
Q_ASSERT_SUCCEEDED(hr);
hr = m_leWatcher->add_Received(Callback<ITypedEventHandler<BluetoothLEAdvertisementWatcher *, BluetoothLEAdvertisementReceivedEventArgs *>>([this](IBluetoothLEAdvertisementWatcher *, IBluetoothLEAdvertisementReceivedEventArgs *args) {
@@ -248,7 +240,6 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
Q_ASSERT_SUCCEEDED(hr);
hr = m_leWatcher->Start();
Q_ASSERT_SUCCEEDED(hr);
-#endif // !Q_OS_WINPHONE
}
void QWinRTBluetoothDeviceDiscoveryWorker::handleLeTimeout()
@@ -429,13 +420,16 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
}
if (pairingCheck == CheckForPairing) {
-#ifndef Q_OS_WINPHONE
ComPtr<IBluetoothLEDevice2> device2;
HRESULT hr = device.As(&device2);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IDeviceInformation> deviceInfo;
hr = device2->get_DeviceInformation(&deviceInfo);
Q_ASSERT_SUCCEEDED(hr);
+ if (!deviceInfo) {
+ qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: Could not obtain device information";
+ return S_OK;
+ }
ComPtr<IDeviceInformation2> deviceInfo2;
hr = deviceInfo.As(&deviceInfo2);
Q_ASSERT_SUCCEEDED(hr);
@@ -473,9 +467,6 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IB
}).Get());
return S_OK;
}
-#else // !Q_OS_WINPHONE
- Q_ASSERT(false);
-#endif // Q_OS_WINPHONE
}
UINT64 address;
diff --git a/src/bluetooth/qbluetoothglobal.h b/src/bluetooth/qbluetoothglobal.h
index 3576b4ef..45867e94 100644
--- a/src/bluetooth/qbluetoothglobal.h
+++ b/src/bluetooth/qbluetoothglobal.h
@@ -40,6 +40,7 @@
#define QTBLUETOOTH_H
#include <QtCore/qglobal.h>
+#include <QtBluetooth/qtbluetooth-config.h> // for feature detection
QT_BEGIN_NAMESPACE
diff --git a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp
index 8da29a36..c8c5ffbf 100644
--- a/src/bluetooth/qbluetoothlocaldevice_bluez.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_bluez.cpp
@@ -1164,7 +1164,7 @@ QString QBluetoothLocalDevicePrivate::RequestPinCode(const QDBusObjectPath &in0)
Q_Q(QBluetoothLocalDevice);
qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << in0.path();
// seeded in constructor, 6 digit pin
- QString pin = QString::fromLatin1("%1").arg(qrand()&1000000);
+ QString pin = QString::fromLatin1("%1").arg(qrand() % 1000000);
pin = QString::fromLatin1("%1").arg(pin, 6, QLatin1Char('0'));
emit q->pairingDisplayPinCode(address, pin);
@@ -1276,7 +1276,7 @@ uint QBluetoothLocalDevicePrivate::RequestPasskey(const QDBusObjectPath &in0)
{
Q_UNUSED(in0);
qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO;
- return qrand()&0x1000000;
+ return (qrand() % 1000000);
}
// Bluez 4
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.cpp b/src/bluetooth/qbluetoothlocaldevice_p.cpp
index e9177f39..7a9d8221 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice_p.cpp
@@ -51,7 +51,7 @@ QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
QObject(parent),
d_ptr(0)
{
-#ifndef QT_IOS_BLUETOOTH
+#if !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH)
printDummyWarning();
#endif
registerQBluetoothLocalDeviceMetaType();
@@ -85,7 +85,11 @@ void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
{
+#ifdef QT_WINRT_BLUETOOTH
+ return HostConnectable;
+#else
return HostPoweredOff;
+#endif
}
QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
@@ -112,7 +116,11 @@ QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
const QBluetoothAddress &address) const
{
Q_UNUSED(address);
+#ifdef QT_WINRT_BLUETOOTH
+ return Paired;
+#else
return Unpaired;
+#endif
}
void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index eecc0d1c..acd88083 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -55,7 +55,7 @@
#include "qbluetoothlocaldevice.h"
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
#include <QObject>
#include <QDBusContext>
#include <QDBusObjectPath>
@@ -128,7 +128,7 @@ public:
QList<QBluetoothAddress> connectedDevices;
};
-#elif defined(QT_BLUEZ_BLUETOOTH)
+#elif QT_CONFIG(bluez)
class QBluetoothLocalDevicePrivate : public QObject, protected QDBusContext
{
Q_OBJECT
@@ -207,6 +207,30 @@ private:
void initializeAdapter();
void initializeAdapterBluez5();
};
+#elif defined(QT_WINRT_BLUETOOTH)
+class QBluetoothLocalDevicePrivate : public QObject
+{
+public:
+ QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
+ QBluetoothAddress localAddress = QBluetoothAddress())
+ : q_ptr(q)
+ {
+ Q_UNUSED(localAddress);
+ }
+
+ ~QBluetoothLocalDevicePrivate()
+ {
+ }
+
+
+ bool isValid() const
+ {
+ return true;
+ }
+
+private:
+ QBluetoothLocalDevice *q_ptr;
+};
#elif !defined(QT_OSX_BLUETOOTH)
class QBluetoothLocalDevicePrivate : public QObject
{
diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp
index a27eb385..99d3b53a 100644
--- a/src/bluetooth/qbluetoothserver.cpp
+++ b/src/bluetooth/qbluetoothserver.cpp
@@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE
Call serverPort() to get the channel number that is being used.
If the \l QBluetoothServiceInfo::Protocol is not supported by a platform, \l listen() will return \c false.
+ Android and WinRT only support RFCOMM for example.
\sa QBluetoothServiceInfo, QBluetoothSocket
*/
@@ -257,7 +258,7 @@ bool QBluetoothServer::isListening() const
{
Q_D(const QBluetoothServer);
-#ifdef QT_ANDROID_BLUETOOTH
+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH)
return d->isListening();
#endif
diff --git a/src/bluetooth/qbluetoothserver.h b/src/bluetooth/qbluetoothserver.h
index e54c4452..568ca3d9 100644
--- a/src/bluetooth/qbluetoothserver.h
+++ b/src/bluetooth/qbluetoothserver.h
@@ -105,7 +105,7 @@ protected:
private:
Q_DECLARE_PRIVATE(QBluetoothServer)
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
Q_PRIVATE_SLOT(d_func(), void _q_newConnection())
#endif
};
diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h
index 8797cebd..4abadc72 100644
--- a/src/bluetooth/qbluetoothserver_p.h
+++ b/src/bluetooth/qbluetoothserver_p.h
@@ -57,7 +57,7 @@
#include "qbluetoothserver.h"
#include "qbluetooth.h"
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
QT_FORWARD_DECLARE_CLASS(QSocketNotifier)
#endif
@@ -69,6 +69,12 @@ QT_FORWARD_DECLARE_CLASS(QSocketNotifier)
class ServerAcceptanceThread;
#endif
+#ifdef QT_WINRT_BLUETOOTH
+#include <wrl.h>
+// No forward declares because QBluetoothServerPrivate::listener does not work with them
+#include <windows.networking.sockets.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothAddress;
@@ -86,7 +92,7 @@ public:
QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType);
~QBluetoothServerPrivate();
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
void _q_newConnection();
void setSocketSecurityLevel(QBluetooth::SecurityFlags requestedSecLevel, int *errnoCode);
QBluetooth::SecurityFlags socketSecurityLevel() const;
@@ -104,7 +110,7 @@ protected:
private:
QBluetoothServer::Error m_lastError;
-#if defined(QT_BLUEZ_BLUETOOTH)
+#if QT_CONFIG(bluez)
QSocketNotifier *socketNotifier;
#elif defined(QT_ANDROID_BLUETOOTH)
ServerAcceptanceThread *thread;
@@ -114,7 +120,21 @@ public:
bool isListening() const;
bool initiateActiveListening(const QBluetoothUuid& uuid, const QString &serviceName);
bool deactivateActiveListening();
+#elif defined(QT_WINRT_BLUETOOTH)
+ EventRegistrationToken connectionToken {-1};
+
+ mutable QMutex pendingConnectionsMutex;
+ QVector<Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket>> pendingConnections;
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> socketListener;
+ HRESULT handleClientConnection(ABI::Windows::Networking::Sockets::IStreamSocketListener *listener,
+ ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs *args);
+public:
+ bool isListening() const;
+ Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> listener() { return socketListener; }
+ bool initiateActiveListening(const QString &serviceName);
+ bool deactivateActiveListening();
#endif
};
diff --git a/src/bluetooth/qbluetoothserver_winrt.cpp b/src/bluetooth/qbluetoothserver_winrt.cpp
new file mode 100644
index 00000000..61134c1f
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_winrt.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserver.h"
+#include "qbluetoothserver_p.h"
+#include "qbluetoothsocket.h"
+#include "qbluetoothsocket_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/private/qeventdispatcher_winrt_p.h>
+#include <qfunctions_winrt.h>
+
+#include <windows.networking.h>
+#include <windows.networking.connectivity.h>
+#include <windows.networking.sockets.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Devices;
+using namespace ABI::Windows::Devices::Enumeration;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Sockets;
+using namespace ABI::Windows::Networking::Connectivity;
+
+typedef ITypedEventHandler<StreamSocketListener *, StreamSocketListenerConnectionReceivedEventArgs *> ClientConnectedHandler;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+
+QHash<QBluetoothServerPrivate *, int> __fakeServerPorts;
+
+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType)
+ : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), socket(0)
+{
+ socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
+}
+
+QBluetoothServerPrivate::~QBluetoothServerPrivate()
+{
+ deactivateActiveListening();
+ __fakeServerPorts.remove(this);
+ if (socket)
+ delete socket;
+}
+
+bool QBluetoothServerPrivate::isListening() const
+{
+ return __fakeServerPorts.contains(const_cast<QBluetoothServerPrivate *>(this));
+}
+
+bool QBluetoothServerPrivate::initiateActiveListening(const QString &serviceName)
+{
+ HStringReference serviceNameRef(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
+
+ ComPtr<IAsyncAction> bindAction;
+ HRESULT hr = socketListener->BindServiceNameAsync(serviceNameRef.Get(), &bindAction);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = QWinRTFunctions::await(bindAction);
+ Q_ASSERT_SUCCEEDED(hr);
+ return true;
+}
+
+bool QBluetoothServerPrivate::deactivateActiveListening()
+{
+ if (!isListening())
+ return true;
+
+ HRESULT hr;
+ hr = socketListener->remove_ConnectionReceived(connectionToken);
+ Q_ASSERT_SUCCEEDED(hr);
+ return true;
+}
+
+HRESULT QBluetoothServerPrivate::handleClientConnection(IStreamSocketListener *listener,
+ IStreamSocketListenerConnectionReceivedEventArgs *args)
+{
+ Q_Q(QBluetoothServer);
+ if (!socketListener || socketListener.Get() != listener) {
+ qCDebug(QT_BT_WINRT) << "Accepting connection from wrong listener. We should not be here.";
+ Q_UNREACHABLE();
+ return S_OK;
+ }
+
+ HRESULT hr;
+ ComPtr<IStreamSocket> socket;
+ hr = args->get_Socket(&socket);
+ Q_ASSERT_SUCCEEDED(hr);
+ QMutexLocker locker(&pendingConnectionsMutex);
+ if (pendingConnections.count() < maxPendingConnections) {
+ qCDebug(QT_BT_WINRT) << "Accepting connection";
+ pendingConnections.append(socket);
+ q->newConnection();
+ } else {
+ qCDebug(QT_BT_WINRT) << "Refusing connection";
+ }
+
+ return S_OK;
+}
+
+void QBluetoothServer::close()
+{
+ Q_D(QBluetoothServer);
+
+ d->deactivateActiveListening();
+ __fakeServerPorts.remove(d);
+}
+
+bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
+{
+ Q_UNUSED(address);
+ Q_D(QBluetoothServer);
+ if (serverType() != QBluetoothServiceInfo::RfcommProtocol) {
+ d->m_lastError = UnsupportedProtocolError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ if (isListening())
+ return false;
+
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([d, this] ()
+ {
+ HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(),
+ &d->socketListener);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->socketListener->add_ConnectionReceived(Callback<ClientConnectedHandler>(d, &QBluetoothServerPrivate::handleClientConnection).Get(),
+ &d->connectionToken);
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+
+ //We can not register an actual Rfcomm port, because the platform does not allow it
+ //but we need a way to associate a server with a service
+ if (port == 0) { //Try to assign a non taken port id
+ for (int i = 1; ; i++){
+ if (__fakeServerPorts.key(i) == 0) {
+ port = i;
+ break;
+ }
+ }
+ }
+
+ if (__fakeServerPorts.key(port) == 0) {
+ __fakeServerPorts[d] = port;
+
+ qCDebug(QT_BT_WINRT) << "Port" << port << "registered";
+ } else {
+ qCWarning(QT_BT_WINRT) << "server with port" << port << "already registered or port invalid";
+ d->m_lastError = ServiceAlreadyRegisteredError;
+ emit error(d->m_lastError);
+ return false;
+ }
+
+ return true;
+}
+
+void QBluetoothServer::setMaxPendingConnections(int numConnections)
+{
+ Q_D(QBluetoothServer);
+ QMutexLocker locker(&d->pendingConnectionsMutex);
+ if (d->pendingConnections.count() > numConnections) {
+ qCWarning(QT_BT_WINRT) << "There are currently more than" << numConnections << "connections"
+ << "pending. Number of maximum pending connections was not changed.";
+ return;
+ }
+
+ d->maxPendingConnections = numConnections;
+}
+
+bool QBluetoothServer::hasPendingConnections() const
+{
+ Q_D(const QBluetoothServer);
+ QMutexLocker locker(&d->pendingConnectionsMutex);
+ return !d->pendingConnections.isEmpty();
+}
+
+QBluetoothSocket *QBluetoothServer::nextPendingConnection()
+{
+ Q_D(QBluetoothServer);
+
+ ComPtr<IStreamSocket> socket = d->pendingConnections.takeFirst();
+
+ QBluetoothSocket *newSocket = new QBluetoothSocket();
+ bool success = newSocket->d_ptr->setSocketDescriptor(socket, d->serverType);
+ if (!success) {
+ delete newSocket;
+ newSocket = 0;
+ }
+
+ return newSocket;
+}
+
+QBluetoothAddress QBluetoothServer::serverAddress() const
+{
+ return QBluetoothAddress();
+}
+
+quint16 QBluetoothServer::serverPort() const
+{
+ //We return the fake port
+ Q_D(const QBluetoothServer);
+ return __fakeServerPorts.value((QBluetoothServerPrivate*)d, 0);
+}
+
+void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
+{
+ Q_UNUSED(security);
+}
+
+QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
+{
+ return QBluetooth::NoSecurity;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
index cd28cc25..8998d608 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
@@ -164,6 +164,8 @@ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent
\l InvalidBluetoothAdapterError. Therefore it is recommended to test the error flag immediately after
using this constructor.
+ \note On WinRT the passed adapter address will be ignored.
+
\sa error()
*/
QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent)
@@ -311,7 +313,7 @@ void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode)
if (d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive
&& d->error != InvalidBluetoothAdapterError) {
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
// done to avoid repeated parsing for adapter address
// on Bluez5
d->foundHostAdapterPath.clear();
@@ -424,7 +426,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery()
Q_Q(QBluetoothServiceDiscoveryAgent);
if (!deviceDiscoveryAgent) {
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(m_deviceAdapterAddress, q);
#else
deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(q);
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h
index 7bc656ce..ba0b91b8 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.h
@@ -48,7 +48,7 @@
#include <QtBluetooth/QBluetoothUuid>
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
#include <QtCore/qprocess.h>
#endif
@@ -117,7 +117,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error))
Q_PRIVATE_SLOT(d_func(), void _q_serviceDiscoveryFinished())
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
Q_PRIVATE_SLOT(d_func(), void _q_discoveredServices(QDBusPendingCallWatcher*))
Q_PRIVATE_SLOT(d_func(), void _q_createdDevice(QDBusPendingCallWatcher*))
Q_PRIVATE_SLOT(d_func(), void _q_foundDevice(QDBusPendingCallWatcher*))
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
index 672dcf0d..ea9f354b 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
@@ -58,16 +58,6 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
-static inline void convertAddress(quint64 from, quint8 (&to)[6])
-{
- to[0] = (from >> 0) & 0xff;
- to[1] = (from >> 8) & 0xff;
- to[2] = (from >> 16) & 0xff;
- to[3] = (from >> 24) & 0xff;
- to[4] = (from >> 32) & 0xff;
- to[5] = (from >> 40) & 0xff;
-}
-
QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
: error(QBluetoothServiceDiscoveryAgent::NoError), m_deviceAdapterAddress(deviceAdapter), state(Inactive), deviceDiscoveryAgent(0),
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
index 47231d8f..9a8da5e3 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -59,7 +59,7 @@
#include <QStack>
#include <QStringList>
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
class OrgBluezManagerInterface;
class OrgBluezAdapterInterface;
class OrgBluezDeviceInterface;
@@ -72,6 +72,10 @@ class QXmlStreamReader;
QT_END_NAMESPACE
#endif
+#ifdef QT_WINRT_BLUETOOTH
+class QWinRTBluetoothServiceDiscoveryWorker;
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothDeviceDiscoveryAgent;
@@ -83,7 +87,13 @@ class LocalDeviceBroadcastReceiver;
#endif
class QBluetoothServiceDiscoveryAgentPrivate
+#if defined QT_WINRT_BLUETOOTH
+ : public QObject
{
+ Q_OBJECT
+#else
+{
+#endif
Q_DECLARE_PUBLIC(QBluetoothServiceDiscoveryAgent)
public:
@@ -112,7 +122,7 @@ public:
void _q_deviceDiscovered(const QBluetoothDeviceInfo &info);
void _q_serviceDiscoveryFinished();
void _q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error);
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
void _q_discoveredServices(QDBusPendingCallWatcher *watcher);
void _q_createdDevice(QDBusPendingCallWatcher *watcher);
void _q_foundDevice(QDBusPendingCallWatcher *watcher);
@@ -141,7 +151,7 @@ private:
void stop();
bool isDuplicatedService(const QBluetoothServiceInfo &serviceInfo) const;
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
void startBluez5(const QBluetoothAddress &address);
void runExternalSdpScan(const QBluetoothAddress &remoteAddress,
const QBluetoothAddress &localAddress);
@@ -169,7 +179,7 @@ private:
QBluetoothServiceDiscoveryAgent::DiscoveryMode mode;
bool singleDevice;
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
QString foundHostAdapterPath;
OrgBluezManagerInterface *manager;
OrgFreedesktopDBusObjectManagerInterface *managerBluez5;
@@ -186,6 +196,17 @@ private:
QMap<QBluetoothAddress,QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > > sdpCache;
#endif
+#ifdef QT_WINRT_BLUETOOTH
+private slots:
+ void processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info);
+ void onScanFinished(quint64 deviceAddress);
+ void onScanCanceled();
+ void onError();
+
+private:
+ QPointer<QWinRTBluetoothServiceDiscoveryWorker> worker;
+#endif
+
protected:
QBluetoothServiceDiscoveryAgent *q_ptr;
};
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp
new file mode 100644
index 00000000..29ccb290
--- /dev/null
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp
@@ -0,0 +1,607 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothservicediscoveryagent.h"
+#include "qbluetoothservicediscoveryagent_p.h"
+
+#include <qfunctions_winrt.h>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/private/qeventdispatcher_winrt_p.h>
+
+#include <functional>
+#include <robuffer.h>
+#include <windows.devices.enumeration.h>
+#include <windows.devices.bluetooth.h>
+#include <windows.foundation.collections.h>
+#include <windows.storage.streams.h>
+#include <wrl.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Devices;
+using namespace ABI::Windows::Devices::Bluetooth;
+using namespace ABI::Windows::Devices::Bluetooth::Rfcomm;
+using namespace ABI::Windows::Devices::Enumeration;
+using namespace ABI::Windows::Storage::Streams;
+
+typedef Collections::IKeyValuePair<UINT32, IBuffer *> ValueItem;
+typedef Collections::IIterable<ValueItem *> ValueIterable;
+typedef Collections::IIterator<ValueItem *> ValueIterator;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+
+#define TYPE_UINT8 8
+#define TYPE_UINT16 9
+#define TYPE_UINT32 10
+#define TYPE_SHORT_UUID 25
+#define TYPE_LONG_UUID 28
+#define TYPE_STRING 37
+#define TYPE_SEQUENCE 53
+
+static QByteArray byteArrayFromBuffer(const ComPtr<IBuffer> &buffer, bool isWCharString = false)
+{
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
+ HRESULT hr = buffer.As(&byteAccess);
+ Q_ASSERT_SUCCEEDED(hr);
+ char *data;
+ hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data));
+ Q_ASSERT_SUCCEEDED(hr);
+ UINT32 size;
+ hr = buffer->get_Length(&size);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (isWCharString) {
+ QString valueString = QString::fromUtf16(reinterpret_cast<ushort *>(data)).left(size / 2);
+ return valueString.toUtf8();
+ }
+ return QByteArray(data, size);
+}
+
+class QWinRTBluetoothServiceDiscoveryWorker : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QWinRTBluetoothServiceDiscoveryWorker(quint64 targetAddress,
+ QBluetoothServiceDiscoveryAgent::DiscoveryMode mode);
+ ~QWinRTBluetoothServiceDiscoveryWorker();
+ void start();
+
+Q_SIGNALS:
+ void serviceFound(quint64 deviceAddress, const QBluetoothServiceInfo &info);
+ void scanFinished(quint64 deviceAddress);
+ void scanCanceled();
+ void errorOccured();
+
+private:
+ HRESULT onBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status);
+
+ void processServiceSearchResult(quint64 address, ComPtr<IVectorView<RfcommDeviceService*>> services);
+ QBluetoothServiceInfo::Sequence readSequence(ComPtr<IDataReader> dataReader, bool *ok, quint8 *bytesRead);
+
+private:
+ quint64 m_targetAddress;
+ QBluetoothServiceDiscoveryAgent::DiscoveryMode m_mode;
+};
+
+QWinRTBluetoothServiceDiscoveryWorker::QWinRTBluetoothServiceDiscoveryWorker(quint64 targetAddress,
+ QBluetoothServiceDiscoveryAgent::DiscoveryMode mode)
+ : m_targetAddress(targetAddress)
+ , m_mode(mode)
+{
+}
+
+QWinRTBluetoothServiceDiscoveryWorker::~QWinRTBluetoothServiceDiscoveryWorker()
+{
+}
+
+void QWinRTBluetoothServiceDiscoveryWorker::start()
+{
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
+ ComPtr<IBluetoothDeviceStatics> deviceStatics;
+ HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &deviceStatics);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IAsyncOperation<BluetoothDevice *>> deviceFromAddressOperation;
+ hr = deviceStatics->FromBluetoothAddressAsync(m_targetAddress, &deviceFromAddressOperation);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = deviceFromAddressOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothDevice *>>
+ (this, &QWinRTBluetoothServiceDiscoveryWorker::onBluetoothDeviceFoundAsync).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+}
+
+HRESULT QWinRTBluetoothServiceDiscoveryWorker::onBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status)
+{
+ if (status != Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not find device";
+ emit errorOccured();
+ return S_OK;
+ }
+
+ ComPtr<IBluetoothDevice> device;
+ HRESULT hr;
+ hr = op->GetResults(&device);
+ Q_ASSERT_SUCCEEDED(hr);
+ quint64 address;
+ device->get_BluetoothAddress(&address);
+
+#ifdef QT_WINRT_LIMITED_SERVICEDISCOVERY
+ if (m_mode != QBluetoothServiceDiscoveryAgent::MinimalDiscovery) {
+ qWarning() << "Used Windows SDK version (" << QString::number(QT_UCRTVERSION) << ") does not "
+ "support full service discovery. Consider updating to a more recent Windows 10 "
+ "SDK (14393 or above).";
+ }
+ ComPtr<IVectorView<RfcommDeviceService*>> commServices;
+ hr = device->get_RfcommServices(&commServices);
+ Q_ASSERT_SUCCEEDED(hr);
+ processServiceSearchResult(address, commServices);
+#else // !QT_WINRT_LIMITED_SERVICEDISOVERY
+ ComPtr<IBluetoothDevice3> device3;
+ hr = device.As(&device3);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IAsyncOperation<RfcommDeviceServicesResult *>> serviceOp;
+ const BluetoothCacheMode cacheMode = m_mode == QBluetoothServiceDiscoveryAgent::MinimalDiscovery
+ ? BluetoothCacheMode_Cached : BluetoothCacheMode_Uncached;
+ hr = device3->GetRfcommServicesWithCacheModeAsync(cacheMode, &serviceOp);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = serviceOp->put_Completed(Callback<IAsyncOperationCompletedHandler<RfcommDeviceServicesResult *>>
+ ([address, this](IAsyncOperation<RfcommDeviceServicesResult *> *op, AsyncStatus status)
+ {
+ if (status != Completed) {
+ qCDebug(QT_BT_WINRT) << "Could not obtain service list";
+ emit errorOccured();
+ return S_OK;
+ }
+
+ ComPtr<IRfcommDeviceServicesResult> result;
+ HRESULT hr = op->GetResults(&result);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IVectorView<RfcommDeviceService*>> commServices;
+ hr = result->get_Services(&commServices);
+ Q_ASSERT_SUCCEEDED(hr);
+ processServiceSearchResult(address, commServices);
+ return S_OK;
+ }).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+#endif // !QT_WINRT_LIMITED_SERVICEDISOVERY
+
+ return S_OK;
+}
+
+void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 address, ComPtr<IVectorView<RfcommDeviceService*>> services)
+{
+ quint32 size;
+ HRESULT hr;
+ hr = services->get_Size(&size);
+ Q_ASSERT_SUCCEEDED(hr);
+ for (quint32 i = 0; i < size; ++i) {
+ ComPtr<IRfcommDeviceService> service;
+ hr = services->GetAt(i, &service);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString name;
+ hr = service->get_ConnectionServiceName(name.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ const QString serviceName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
+ ComPtr<IRfcommServiceId> id;
+ hr = service->get_ServiceId(&id);
+ Q_ASSERT_SUCCEEDED(hr);
+ GUID guid;
+ hr = id->get_Uuid(&guid);
+ const QBluetoothUuid uuid(guid);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ QBluetoothServiceInfo info;
+ info.setServiceName(serviceName);
+ info.setServiceUuid(uuid);
+ ComPtr<IAsyncOperation<IMapView<UINT32, IBuffer *> *>> op;
+ hr = service->GetSdpRawAttributesAsync(op.GetAddressOf());
+ if (FAILED(hr)) {
+ emit errorOccured();
+ qDebug() << "Check manifest capabilities";
+ continue;
+ }
+ ComPtr<IMapView<UINT32, IBuffer *>> mapView;
+ hr = QWinRTFunctions::await(op, mapView.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ // TODO timeout
+ ComPtr<ValueIterable> iterable;
+ ComPtr<ValueIterator> iterator;
+
+ hr = mapView.As(&iterable);
+ if (FAILED(hr))
+ continue;
+
+ boolean current = false;
+ hr = iterable->First(&iterator);
+ if (FAILED(hr))
+ continue;
+ hr = iterator->get_HasCurrent(&current);
+ if (FAILED(hr))
+ continue;
+
+ while (SUCCEEDED(hr) && current) {
+ ComPtr<ValueItem> item;
+ hr = iterator->get_Current(&item);
+ if (FAILED(hr))
+ continue;
+
+ UINT32 key;
+ hr = item->get_Key(&key);
+ if (FAILED(hr))
+ continue;
+
+ ComPtr<IBuffer> buffer;
+ hr = item->get_Value(&buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IDataReader> dataReader;
+ ComPtr<IDataReaderStatics> dataReaderStatics;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), &dataReaderStatics);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = dataReaderStatics->FromBuffer(buffer.Get(), dataReader.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ BYTE type;
+ hr = dataReader->ReadByte(&type);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (type == TYPE_UINT8) {
+ quint8 value;
+ hr = dataReader->ReadByte(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ info.setAttribute(key, value);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "UINT8" << hex << value;
+ } else if (type == TYPE_UINT16) {
+ quint16 value;
+ hr = dataReader->ReadUInt16(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ info.setAttribute(key, value);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "UINT16" << hex << value;
+ } else if (type == TYPE_UINT32) {
+ quint32 value;
+ hr = dataReader->ReadUInt32(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ info.setAttribute(key, value);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "UINT32" << hex << value;
+ } else if (type == TYPE_SHORT_UUID) {
+ quint16 value;
+ hr = dataReader->ReadUInt16(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ const QBluetoothUuid uuid(value);
+ info.setAttribute(key, uuid);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "UUID" << hex << uuid;
+ } else if (type == TYPE_LONG_UUID) {
+ GUID value;
+ hr = dataReader->ReadGuid(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ const QBluetoothUuid uuid(value);
+ info.setAttribute(key, uuid);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "UUID" << hex << uuid;
+ } else if (type == TYPE_STRING) {
+ BYTE length;
+ hr = dataReader->ReadByte(&length);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString value;
+ hr = dataReader->ReadString(length, value.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ const QString str = QString::fromWCharArray(WindowsGetStringRawBuffer(value.Get(), nullptr));
+ info.setAttribute(key, str);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "STRING" << str;
+ } else if (type == TYPE_SEQUENCE) {
+ bool ok;
+ QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, &ok, nullptr);
+ if (ok) {
+ info.setAttribute(key, sequence);
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "SEQUENCE" << sequence;
+ } else {
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type << "SEQUENCE ERROR";
+ }
+ } else {
+ qCDebug(QT_BT_WINRT) << "UUID" << uuid << "KEY" << hex << key << "TYPE" << dec << type;
+ }
+ hr = iterator->MoveNext(&current);
+ }
+ emit serviceFound(address, info);
+ }
+ emit scanFinished(address);
+ deleteLater();
+}
+
+QBluetoothServiceInfo::Sequence QWinRTBluetoothServiceDiscoveryWorker::readSequence(ComPtr<IDataReader> dataReader, bool *ok, quint8 *bytesRead)
+{
+ if (ok)
+ *ok = false;
+ if (bytesRead)
+ *bytesRead = 0;
+ QBluetoothServiceInfo::Sequence result;
+ if (!dataReader)
+ return result;
+
+ quint8 remainingLength;
+ HRESULT hr = dataReader->ReadByte(&remainingLength);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (bytesRead)
+ *bytesRead += 1;
+ BYTE type;
+ hr = dataReader->ReadByte(&type);
+ remainingLength -= 1;
+ if (bytesRead)
+ *bytesRead += 1;
+ Q_ASSERT_SUCCEEDED(hr);
+ while (true) {
+ switch (type) {
+ case TYPE_UINT8: {
+ quint8 value;
+ hr = dataReader->ReadByte(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ result.append(QVariant::fromValue(value));
+ remainingLength -= 1;
+ if (bytesRead)
+ *bytesRead += 1;
+ break;
+ }
+ case TYPE_UINT16: {
+ quint16 value;
+ hr = dataReader->ReadUInt16(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ result.append(QVariant::fromValue(value));
+ remainingLength -= 2;
+ if (bytesRead)
+ *bytesRead += 2;
+ break;
+ }
+ case TYPE_UINT32: {
+ quint32 value;
+ hr = dataReader->ReadUInt32(&value);
+ Q_ASSERT_SUCCEEDED(hr);
+ result.append(QVariant::fromValue(value));
+ remainingLength -= 4;
+ if (bytesRead)
+ *bytesRead += 4;
+ break;
+ }
+ case TYPE_SHORT_UUID: {
+ quint16 b;
+ hr = dataReader->ReadUInt16(&b);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ const QBluetoothUuid uuid(b);
+ result.append(QVariant::fromValue(uuid));
+ remainingLength -= 2;
+ if (bytesRead)
+ *bytesRead += 2;
+ break;
+ }
+ case TYPE_LONG_UUID: {
+ GUID b;
+ hr = dataReader->ReadGuid(&b);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ const QBluetoothUuid uuid(b);
+ result.append(QVariant::fromValue(uuid));
+ remainingLength -= sizeof(GUID);
+ if (bytesRead)
+ *bytesRead += sizeof(GUID);
+ break;
+ }
+ case TYPE_STRING: {
+ BYTE length;
+ hr = dataReader->ReadByte(&length);
+ Q_ASSERT_SUCCEEDED(hr);
+ remainingLength -= 1;
+ if (bytesRead)
+ *bytesRead += 1;
+ HString value;
+ hr = dataReader->ReadString(length, value.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ const QString str = QString::fromWCharArray(WindowsGetStringRawBuffer(value.Get(), nullptr));
+ result.append(QVariant::fromValue(str));
+ remainingLength -= length;
+ if (bytesRead)
+ *bytesRead += length;
+ break;
+ }
+ case TYPE_SEQUENCE: {
+ quint8 bytesR;
+ const QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, ok, &bytesR);
+ if (*ok)
+ result.append(QVariant::fromValue(sequence));
+ else
+ return result;
+ remainingLength -= bytesR;
+ if (bytesRead)
+ *bytesRead += bytesR;
+ break;
+ }
+ default:
+ qCDebug(QT_BT_WINRT) << "SEQUENCE ERROR" << type;
+ result.clear();
+ return result;
+ }
+ if (remainingLength == 0)
+ break;
+
+ hr = dataReader->ReadByte(&type);
+ Q_ASSERT_SUCCEEDED(hr);
+ remainingLength -= 1;
+ if (bytesRead)
+ *bytesRead += 1;
+ }
+
+ if (ok)
+ *ok = true;
+ return result;
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
+ : error(QBluetoothServiceDiscoveryAgent::NoError),
+ state(Inactive),
+ deviceDiscoveryAgent(0),
+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
+ singleDevice(false),
+ q_ptr(qp)
+{
+ // TODO: use local adapter for discovery. Possible?
+ Q_UNUSED(deviceAdapter);
+}
+
+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
+{
+ stop();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
+{
+ if (worker)
+ return;
+
+ worker = new QWinRTBluetoothServiceDiscoveryWorker(address.toUInt64(), mode);
+
+ connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::serviceFound,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::processFoundService, Qt::QueuedConnection);
+ connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::scanFinished,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::onScanFinished, Qt::QueuedConnection);
+ connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::scanCanceled,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::onScanCanceled, Qt::QueuedConnection);
+ connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::errorOccured,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::onError, Qt::QueuedConnection);
+ worker->start();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::stop()
+{
+ if (!worker)
+ return;
+
+ disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::serviceFound,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::processFoundService);
+ disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::scanFinished,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::onScanFinished);
+ disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::scanCanceled,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::onScanCanceled);
+ disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::errorOccured,
+ this, &QBluetoothServiceDiscoveryAgentPrivate::onError);
+ // mWorker will delete itself as soon as it is done with its discovery
+ worker = nullptr;
+ setDiscoveryState(Inactive);
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info)
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ //apply uuidFilter
+ if (!uuidFilter.isEmpty()) {
+ bool serviceNameMatched = uuidFilter.contains(info.serviceUuid());
+ bool serviceClassMatched = false;
+ for (const QBluetoothUuid &id : info.serviceClassUuids()) {
+ if (uuidFilter.contains(id)) {
+ serviceClassMatched = true;
+ break;
+ }
+ }
+
+ if (!serviceNameMatched && !serviceClassMatched)
+ return;
+ }
+
+ if (!info.isValid())
+ return;
+
+ QBluetoothServiceInfo returnInfo(info);
+ bool deviceFound;
+ for (const QBluetoothDeviceInfo &deviceInfo : discoveredDevices) {
+ if (deviceInfo.address().toUInt64() == deviceAddress) {
+ deviceFound = true;
+ returnInfo.setDevice(deviceInfo);
+ break;
+ }
+ }
+ Q_ASSERT(deviceFound);
+
+ if (!isDuplicatedService(returnInfo)) {
+ discoveredServices.append(returnInfo);
+ qCDebug(QT_BT_WINRT) << "Discovered services" << discoveredDevices.at(0).address().toString()
+ << returnInfo.serviceName() << returnInfo.serviceUuid()
+ << ">>>" << returnInfo.serviceClassUuids();
+
+ emit q->serviceDiscovered(returnInfo);
+ }
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::onScanFinished(quint64 deviceAddress)
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ bool deviceFound;
+ for (const QBluetoothDeviceInfo &deviceInfo : discoveredDevices) {
+ if (deviceInfo.address().toUInt64() == deviceAddress) {
+ deviceFound = true;
+ discoveredDevices.removeOne(deviceInfo);
+ if (discoveredDevices.isEmpty())
+ setDiscoveryState(Inactive);
+ break;
+ }
+ }
+ Q_ASSERT(deviceFound);
+ stop();
+ emit q->finished();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::onScanCanceled()
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ emit q->canceled();
+}
+
+void QBluetoothServiceDiscoveryAgentPrivate::onError()
+{
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ discoveredDevices.clear();
+ error = QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = "errorDescription";
+ emit q->error(error);
+}
+
+QT_END_NAMESPACE
+
+#include <qbluetoothservicediscoveryagent_winrt.moc>
diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h
index f1f3b669..ea695b56 100644
--- a/src/bluetooth/qbluetoothserviceinfo_p.h
+++ b/src/bluetooth/qbluetoothserviceinfo_p.h
@@ -62,6 +62,22 @@
class OrgBluezServiceInterface;
class OrgBluezProfileManager1Interface;
+#ifdef QT_WINRT_BLUETOOTH
+#include <wrl.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace Devices {
+ namespace Bluetooth {
+ namespace Rfcomm {
+ struct IRfcommServiceProvider;
+ }
+ }
+ }
+ }
+}
+#endif
+
QT_BEGIN_NAMESPACE
class QBluetoothServiceInfo;
@@ -88,7 +104,7 @@ public:
QBluetoothServiceInfo::Sequence protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const;
int serverChannel() const;
private:
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
bool ensureSdpConnection(const QBluetoothAddress &localAdapter = QBluetoothAddress());
OrgBluezServiceInterface *service;
@@ -98,6 +114,12 @@ private:
QString profilePath;
#endif
+#ifdef QT_WINRT_BLUETOOTH
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::Rfcomm::IRfcommServiceProvider> serviceProvider;
+
+ bool writeSdpAttributes();
+#endif
+
mutable bool registered;
};
diff --git a/src/bluetooth/qbluetoothserviceinfo_winrt.cpp b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp
new file mode 100644
index 00000000..d72056b7
--- /dev/null
+++ b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp
@@ -0,0 +1,450 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothserviceinfo.h"
+#include "qbluetoothserviceinfo_p.h"
+#include "qbluetoothserver_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <qfunctions_winrt.h>
+
+#include <wrl.h>
+#include <windows.devices.bluetooth.h>
+#include <windows.devices.bluetooth.rfcomm.h>
+#include <windows.foundation.h>
+#include <windows.networking.sockets.h>
+#include <windows.storage.streams.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Devices::Bluetooth;
+using namespace ABI::Windows::Devices::Bluetooth::Rfcomm;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Networking::Sockets;
+using namespace ABI::Windows::Storage::Streams;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+
+#define TYPE_UINT8 8
+#define TYPE_UINT16 9
+#define TYPE_UINT32 10
+#define TYPE_SHORT_UUID 25
+#define TYPE_LONG_UUID 28
+#define TYPE_STRING 37
+#define TYPE_SEQUENCE 53
+
+extern QHash<QBluetoothServerPrivate *, int> __fakeServerPorts;
+
+bool repairProfileDescriptorListIfNeeded(ComPtr<IBuffer> &buffer)
+{
+ ComPtr<IDataReaderStatics> dataReaderStatics;
+ HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(),
+ &dataReaderStatics);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IDataReader> reader;
+ hr = dataReaderStatics->FromBuffer(buffer.Get(), reader.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ BYTE type;
+ hr = reader->ReadByte(&type);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (type != TYPE_SEQUENCE) {
+ qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Malformed profile descriptor list read";
+ return false;
+ }
+
+ quint8 length;
+ hr = reader->ReadByte(&length);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = reader->ReadByte(&type);
+ Q_ASSERT_SUCCEEDED(hr);
+ // We have to "repair" the structure if the outer sequence contains a uuid directly
+ if (type == TYPE_SHORT_UUID && length == 4) {
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Repairing profile descriptor list";
+ quint16 uuid;
+ hr = reader->ReadUInt16(&uuid);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IDataWriter> writer;
+ hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(),
+ &writer);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = writer->WriteByte(TYPE_SEQUENCE);
+ Q_ASSERT_SUCCEEDED(hr);
+ // 8 == length of nested sequence (outer sequence -> inner sequence -> uuid and version)
+ hr = writer->WriteByte(8);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteByte(TYPE_SEQUENCE);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteByte(7);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteByte(TYPE_SHORT_UUID);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteUInt16(uuid);
+ Q_ASSERT_SUCCEEDED(hr);
+ // Write default version to make WinRT happy
+ hr = writer->WriteByte(TYPE_UINT16);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteUInt16(0x100);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = writer->DetachBuffer(&buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ return true;
+}
+
+static ComPtr<IBuffer> bufferFromAttribute(const QVariant &attribute)
+{
+ ComPtr<IDataWriter> writer;
+ HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(),
+ &writer);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ switch (int(attribute.type())) {
+ case QMetaType::Void:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Void";
+ return nullptr;
+ case QMetaType::UChar:
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::UChar";
+ hr = writer->WriteByte(TYPE_UINT8);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteByte(attribute.value<quint8>());
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ case QMetaType::UShort:
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::UShort";
+ hr = writer->WriteByte(TYPE_UINT16);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteUInt16(attribute.value<quint16>());
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ case QMetaType::UInt:
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::UInt";
+ hr = writer->WriteByte(TYPE_UINT32);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteByte(attribute.value<quint32>());
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ case QMetaType::Char:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Char";
+ return nullptr;
+ break;
+ case QMetaType::Short:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Short";
+ return nullptr;
+ break;
+ case QMetaType::Int:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Int";
+ return nullptr;
+ break;
+ case QMetaType::QString: {
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::QString";
+ hr = writer->WriteByte(TYPE_STRING);
+ Q_ASSERT_SUCCEEDED(hr);
+ const QString stringValue = attribute.value<QString>();
+ hr = writer->WriteByte(stringValue.length());
+ Q_ASSERT_SUCCEEDED(hr);
+ HStringReference stringRef(reinterpret_cast<LPCWSTR>(stringValue.utf16()));
+ quint32 bytesWritten;
+ hr = writer->WriteString(stringRef.Get(), &bytesWritten);
+ if (bytesWritten != stringValue.length()) {
+ qCWarning(QT_BT_WINRT) << "Did not write full value to buffer";
+ return nullptr;
+ }
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ }
+ case QMetaType::Bool:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Bool";
+ return nullptr;
+ break;
+ case QMetaType::QUrl:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::QUrl";
+ return nullptr;
+ break;
+ case QVariant::UserType:
+ if (attribute.userType() == qMetaTypeId<QBluetoothUuid>()) {
+ QBluetoothUuid uuid = attribute.value<QBluetoothUuid>();
+ const int minimumSize = uuid.minimumSize();
+ switch (uuid.minimumSize()) {
+ case 0:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register Uuid of length 0";
+ return nullptr;
+ break;
+ case 2:
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering Uuid attribute with length 2" << uuid;
+ hr = writer->WriteByte(TYPE_SHORT_UUID);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteUInt16(uuid.toUInt16());
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ case 4:
+ qCWarning(QT_BT_WINRT) << "Don't know how to register Uuid of length 4";
+ return nullptr;
+ break;
+ case 16:
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering Uuid attribute with length 16";
+ hr = writer->WriteByte(TYPE_LONG_UUID);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteGuid(uuid);
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ default:
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering Uuid attribute";
+ hr = writer->WriteByte(TYPE_LONG_UUID);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteGuid(uuid);
+ Q_ASSERT_SUCCEEDED(hr);
+ break;
+ }
+ } else if (attribute.userType() == qMetaTypeId<QBluetoothServiceInfo::Sequence>()) {
+ qCDebug(QT_BT_WINRT) << "Registering sequence attribute";
+ hr = writer->WriteByte(TYPE_SEQUENCE);
+ Q_ASSERT_SUCCEEDED(hr);
+ const QBluetoothServiceInfo::Sequence *sequence =
+ static_cast<const QBluetoothServiceInfo::Sequence *>(attribute.data());
+ ComPtr<IDataWriter> tmpWriter;
+ HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(),
+ &tmpWriter);
+ Q_ASSERT_SUCCEEDED(hr);
+ foreach (const QVariant &v, *sequence) {
+ ComPtr<IBuffer> tmpBuffer = bufferFromAttribute(v);
+ if (!tmpBuffer) {
+ qCWarning(QT_BT_WINRT) << "Could not create buffer from attribute in sequence";
+ return nullptr;
+ }
+ quint32 l;
+ hr = tmpBuffer->get_Length(&l);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = tmpWriter->WriteBuffer(tmpBuffer.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ ComPtr<IBuffer> tmpBuffer;
+ hr = tmpWriter->DetachBuffer(&tmpBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ // write sequence length
+ quint32 length;
+ tmpBuffer->get_Length(&length);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = writer->WriteByte(length + 1);
+ Q_ASSERT_SUCCEEDED(hr);
+ // write sequence data
+ hr = writer->WriteBuffer(tmpBuffer.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registered sequence attribute with length" << length;
+ } else if (attribute.userType() == qMetaTypeId<QBluetoothServiceInfo::Alternative>()) {
+ qCWarning(QT_BT_WINRT) << "Don't know how to register user type Alternative";
+ return false;
+ }
+ break;
+ default:
+ qCWarning(QT_BT_WINRT) << "Unknown variant type", attribute.userType();
+ return nullptr;
+ }
+ ComPtr<IBuffer> buffer;
+ hr = writer->DetachBuffer(&buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ return buffer;
+}
+
+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
+ : registered(false)
+{
+}
+
+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate()
+{
+}
+
+bool QBluetoothServiceInfoPrivate::isRegistered() const
+{
+ return registered;
+}
+
+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter)
+{
+ Q_UNUSED(localAdapter);
+ if (registered)
+ return false;
+
+ if (protocolDescriptor(QBluetoothUuid::Rfcomm).isEmpty()) {
+ qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Only RFCOMM services can be registered on WinRT";
+ return false;
+ }
+
+ QBluetoothServerPrivate *sPriv = __fakeServerPorts.key(serverChannel());
+ if (!sPriv)
+ return false;
+
+ HRESULT hr;
+ QBluetoothUuid uuid = attributes.value(QBluetoothServiceInfo::ServiceId).value<QBluetoothUuid>();
+ ComPtr<IRfcommServiceIdStatics> serviceIdStatics;
+ hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Rfcomm_RfcommServiceId).Get(),
+ IID_PPV_ARGS(&serviceIdStatics));
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IRfcommServiceId> serviceId;
+ hr = serviceIdStatics->FromUuid(uuid, &serviceId);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IRfcommServiceProviderStatics> providerStatics;
+ hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Rfcomm_RfcommServiceProvider).Get(),
+ IID_PPV_ARGS(&providerStatics));
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IAsyncOperation<RfcommServiceProvider *>> op;
+ hr = providerStatics->CreateAsync(serviceId.Get(), &op);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = QWinRTFunctions::await(op, serviceProvider.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IStreamSocketListener> listener = sPriv->listener();
+ if (!listener) {
+ qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Could not obtain listener from server.";
+ return false;
+ }
+
+
+ HString serviceIdHString;
+ serviceId->AsString(serviceIdHString.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ const QString serviceIdString = QString::fromWCharArray(WindowsGetStringRawBuffer(serviceIdHString.Get(), nullptr));
+
+ //tell the server what service name our listener should have
+ //and start the real listener
+ bool result = sPriv->initiateActiveListening(serviceIdString);
+ if (!result) {
+ return false;
+ }
+
+ result = writeSdpAttributes();
+ if (!result) {
+ return false;
+ }
+
+ hr = serviceProvider->StartAdvertising(listener.Get());
+ if (FAILED(hr)) {
+ qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Could not start advertising. Check your SDP data.";
+ return false;
+ }
+
+ registered = true;
+ return true;
+}
+
+bool QBluetoothServiceInfoPrivate::unregisterService()
+{
+ if (!registered)
+ return false;
+
+ QBluetoothServerPrivate *sPriv = __fakeServerPorts.key(serverChannel());
+ if (!sPriv) {
+ //QBluetoothServer::close() was called without prior call to unregisterService().
+ //Now it is unregistered anyway.
+ registered = false;
+ return true;
+ }
+
+ bool result = sPriv->deactivateActiveListening();
+ if (!result)
+ return false;
+
+ HRESULT hr;
+ hr = serviceProvider->StopAdvertising();
+ Q_ASSERT_SUCCEEDED(hr);
+
+ registered = false;
+ return true;
+}
+
+bool QBluetoothServiceInfoPrivate::writeSdpAttributes()
+{
+ if (!serviceProvider)
+ return false;
+
+ ComPtr<IDataWriter> writer;
+ HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(),
+ &writer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IMap<UINT32, IBuffer *>> rawAttributes;
+ hr = serviceProvider->get_SdpRawAttributes(&rawAttributes);
+ Q_ASSERT_SUCCEEDED(hr);
+ for (quint16 key : attributes.keys()) {
+ // The SDP Class Id List and RFCOMM and L2CAP protocol descriptors are automatically
+ // generated by the RfcommServiceProvider. Do not specify it in the SDP raw attribute map.
+ if (key == QBluetoothServiceInfo::ServiceClassIds
+ || key == QBluetoothServiceInfo::ProtocolDescriptorList)
+ continue;
+ const QVariant attribute = attributes.value(key);
+ HRESULT hr;
+ ComPtr<IBuffer> buffer = bufferFromAttribute(attribute);
+ if (!buffer) {
+ qCWarning(QT_BT_WINRT) << "Could not create buffer from attribute with id:" << key;
+ return false;
+ }
+
+ // Other backends support a wrong structure in profile descriptor list. In order to make
+ // WinRT accept the list without breaking existing apps we have to repair this structure.
+ if (key == QBluetoothServiceInfo::BluetoothProfileDescriptorList) {
+ if (!repairProfileDescriptorListIfNeeded(buffer)) {
+ qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Error while checking/repairing structure of profile descriptor list";
+ return false;
+ }
+ }
+
+ hr = writer->WriteBuffer(buffer.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = writer->DetachBuffer(&buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ boolean replaced;
+ hr = rawAttributes->Insert(key, buffer.Get(), &replaced);
+ Q_ASSERT_SUCCEEDED(hr);
+ Q_ASSERT(!replaced);
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp
index d396ca84..3e961142 100644
--- a/src/bluetooth/qbluetoothsocket.cpp
+++ b/src/bluetooth/qbluetoothsocket.cpp
@@ -338,7 +338,14 @@ void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, Op
}
d->connectToService(service.device().address(), service.serviceUuid(), openMode);
#else
- // Report this problem early:
+#if defined(QT_WINRT_BLUETOOTH)
+ // Report these problems early:
+ if (socketType() != QBluetoothServiceInfo::RfcommProtocol) {
+ d->errorString = tr("Socket type not supported");
+ setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+#endif // QT_WINRT_BLUETOOTH
if (socketType() == QBluetoothServiceInfo::UnknownProtocol) {
qCWarning(QT_BT) << "QBluetoothSocket::connectToService cannot "
"connect with 'UnknownProtocol' type";
@@ -416,7 +423,14 @@ void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const
}
d->connectToService(address, uuid, openMode);
#else
- // Report this problem early, prevent device discovery:
+#if defined(QT_WINRT_BLUETOOTH)
+ // Report these problems early, prevent device discovery:
+ if (socketType() != QBluetoothServiceInfo::RfcommProtocol) {
+ d->errorString = tr("Socket type not supported");
+ setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+#endif // QT_WINRT_BLUETOOTH
if (socketType() == QBluetoothServiceInfo::UnknownProtocol) {
qCWarning(QT_BT) << "QBluetoothSocket::connectToService cannot "
"connect with 'UnknownProtocol' type";
@@ -462,7 +476,14 @@ void QBluetoothSocket::connectToService(const QBluetoothAddress &address, quint1
setSocketError(QBluetoothSocket::ServiceNotFoundError);
qCWarning(QT_BT) << "Connecting to port is not supported";
#else
- // Report this problem early:
+#if defined(QT_WINRT_BLUETOOTH)
+ // Report these problems early
+ if (socketType() != QBluetoothServiceInfo::RfcommProtocol) {
+ d->errorString = tr("Socket type not supported");
+ setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+#endif // QT_WINRT_BLUETOOTH
if (socketType() == QBluetoothServiceInfo::UnknownProtocol) {
qCWarning(QT_BT) << "QBluetoothSocket::connectToService cannot "
"connect with 'UnknownProtocol' type";
diff --git a/src/bluetooth/qbluetoothsocket_bluez.cpp b/src/bluetooth/qbluetoothsocket_bluez.cpp
index 162a1a0b..42c5503b 100644
--- a/src/bluetooth/qbluetoothsocket_bluez.cpp
+++ b/src/bluetooth/qbluetoothsocket_bluez.cpp
@@ -185,7 +185,7 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address,
// only Linux (of all platforms supported by this library) supports this type
// of socket.
-#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
if (lowEnergySocketType) {
addr.l2_cid = htobs(port);
addr.l2_bdaddr_type = lowEnergySocketType;
diff --git a/src/bluetooth/qbluetoothsocket_p.h b/src/bluetooth/qbluetoothsocket_p.h
index 147427fd..956f8f02 100644
--- a/src/bluetooth/qbluetoothsocket_p.h
+++ b/src/bluetooth/qbluetoothsocket_p.h
@@ -61,6 +61,26 @@
class WorkerThread;
#endif
+#ifdef QT_WINRT_BLUETOOTH
+#include <wrl.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace Networking {
+ namespace Sockets {
+ struct IStreamSocket;
+ }
+ }
+ namespace Foundation {
+ struct IAsyncAction;
+ enum class AsyncStatus;
+ }
+ }
+}
+
+class SocketWorker;
+#endif // QT_WINRT_BLUETOOTH
+
#ifndef QPRIVATELINEARBUFFER_BUFFERSIZE
#define QPRIVATELINEARBUFFER_BUFFERSIZE Q_INT64_C(16384)
#endif
@@ -134,6 +154,11 @@ public:
bool setSocketDescriptor(const QAndroidJniObject &socket, QBluetoothServiceInfo::Protocol socketType,
QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState,
QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite);
+#elif defined(QT_WINRT_BLUETOOTH)
+ bool setSocketDescriptor(Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket> socket,
+ QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState,
+ QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite);
#endif
bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState,
@@ -183,7 +208,25 @@ signals:
#endif
-#if defined(QT_BLUEZ_BLUETOOTH)
+#ifdef QT_WINRT_BLUETOOTH
+ SocketWorker *m_worker;
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket> m_socketObject;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> m_connectOp;
+
+ QMutex m_readMutex;
+
+ // Protected by m_readMutex. Written in addToPendingData (native callback)
+ QVector<QByteArray> m_pendingData;
+
+ Q_INVOKABLE void addToPendingData(const QVector<QByteArray> &data);
+
+private slots:
+ void handleNewData(const QVector<QByteArray> &data);
+ void handleError(QBluetoothSocket::SocketError error);
+#endif // QT_WINRT_BLUETOOTH
+
+#if QT_CONFIG(bluez)
private slots:
void _q_readNotify();
void _q_writeNotify();
@@ -194,7 +237,11 @@ protected:
private:
-#ifdef QT_BLUEZ_BLUETOOTH
+#ifdef QT_WINRT_BLUETOOTH
+ HRESULT handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status);
+#endif
+
+#if QT_CONFIG(bluez)
public:
quint8 lowEnergySocketType;
#endif
diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp
new file mode 100644
index 00000000..1f4e6233
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_winrt.cpp
@@ -0,0 +1,668 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbluetoothsocket.h"
+#include "qbluetoothsocket_p.h"
+
+#include <qfunctions_winrt.h>
+
+#include <private/qeventdispatcher_winrt_p.h>
+
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/qloggingcategory.h>
+
+#include <robuffer.h>
+#include <windows.devices.bluetooth.h>
+#include <windows.networking.sockets.h>
+#include <windows.storage.streams.h>
+#include <wrl.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Devices::Bluetooth;
+using namespace ABI::Windows::Devices::Bluetooth::Rfcomm;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Networking;
+using namespace ABI::Windows::Networking::Sockets;
+using namespace ABI::Windows::Storage::Streams;
+
+typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> SocketReadCompletedHandler;
+typedef IAsyncOperationWithProgress<IBuffer *, UINT32> IAsyncBufferOperation;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+
+struct SocketGlobal
+{
+ SocketGlobal()
+ {
+ HRESULT hr;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
+ &bufferFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ ComPtr<IBufferFactory> bufferFactory;
+};
+Q_GLOBAL_STATIC(SocketGlobal, g)
+
+#define READ_BUFFER_SIZE 65536
+
+static inline QString qt_QStringFromHString(const HString &string)
+{
+ UINT32 length;
+ PCWSTR rawString = string.GetRawBuffer(&length);
+ return QString::fromWCharArray(rawString, length);
+}
+
+static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
+{
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = g->bufferFactory->Create(len, &buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = buffer->put_Length(len);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
+ hr = buffer.As(&byteArrayAccess);
+ Q_ASSERT_SUCCEEDED(hr);
+ byte *bytes;
+ hr = byteArrayAccess->Buffer(&bytes);
+ Q_ASSERT_SUCCEEDED(hr);
+ memcpy(bytes, data, len);
+ ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
+ hr = stream->WriteAsync(buffer.Get(), &op);
+ RETURN_IF_FAILED("Failed to write to stream", return -1);
+ UINT32 bytesWritten;
+ hr = QWinRTFunctions::await(op, &bytesWritten);
+ RETURN_IF_FAILED("Failed to write to stream", return -1);
+ return bytesWritten;
+}
+
+class SocketWorker : public QObject
+{
+ Q_OBJECT
+public:
+ SocketWorker()
+ {
+ }
+
+ ~SocketWorker()
+ {
+ if (Q_UNLIKELY(m_initialReadOp)) {
+ ComPtr<IAsyncInfo> info;
+ HRESULT hr = m_initialReadOp.As(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (info) {
+ hr = info->Cancel();
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->Close();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ }
+
+ if (m_readOp) {
+ ComPtr<IAsyncInfo> info;
+ HRESULT hr = m_readOp.As(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (info) {
+ hr = info->Cancel();
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->Close();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ }
+ }
+
+signals:
+ void newDataReceived(const QVector<QByteArray> &data);
+ void socketErrorOccured(QBluetoothSocket::SocketError error);
+
+public slots:
+ Q_INVOKABLE void notifyAboutNewData()
+ {
+ QMutexLocker locker(&m_mutex);
+ const QVector<QByteArray> newData = std::move(m_pendingData);
+ m_pendingData.clear();
+ emit newDataReceived(newData);
+ }
+
+public:
+ void startReading()
+ {
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([this]()
+ {
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IInputStream> stream;
+ hr = m_socket->get_InputStream(&stream);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, m_initialReadOp.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = m_initialReadOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketWorker::onReadyRead).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
+ {
+ if (asyncInfo == m_initialReadOp.Get()) {
+ m_initialReadOp.Reset();
+ } else if (asyncInfo == m_readOp.Get()) {
+ m_readOp.Reset();
+ } else {
+ Q_ASSERT(false);
+ }
+
+ // A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
+ // that the connection was closed. The socket cannot be closed here, as the subsequent read
+ // might fail then.
+ if (status == Error || status == Canceled) {
+ emit socketErrorOccured(QBluetoothSocket::NetworkError);
+ return S_OK;
+ }
+
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = asyncInfo->GetResults(&buffer);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get read results buffer");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ UINT32 bufferLength;
+ hr = buffer->get_Length(&bufferLength);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get buffer length");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ // A zero sized buffer length signals, that the remote host closed the connection. The socket
+ // cannot be closed though, as the following read might have socket descriptor -1 and thus and
+ // the closing of the socket won't be communicated to the caller. So only the error is set. The
+ // actual socket close happens inside of read.
+ if (!bufferLength) {
+ emit socketErrorOccured(QBluetoothSocket::NetworkError);
+ return S_OK;
+ }
+
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
+ hr = buffer.As(&byteArrayAccess);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get cast buffer");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ byte *data;
+ hr = byteArrayAccess->Buffer(&data);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to access buffer data");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ QByteArray newData(reinterpret_cast<const char*>(data), qint64(bufferLength));
+ QMutexLocker readLocker(&m_mutex);
+ if (m_pendingData.isEmpty())
+ QMetaObject::invokeMethod(this, "notifyAboutNewData", Qt::QueuedConnection);
+ m_pendingData << newData;
+ readLocker.unlock();
+
+ hr = QEventDispatcherWinRT::runOnXamlThread([buffer, this]() {
+ UINT32 readBufferLength;
+ ComPtr<IInputStream> stream;
+ HRESULT hr = m_socket->get_InputStream(&stream);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to obtain input stream");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ // Reuse the stream buffer
+ hr = buffer->get_Capacity(&readBufferLength);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get buffer capacity");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ hr = buffer->put_Length(0);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to set buffer length");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &m_readOp);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ hr = m_readOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketWorker::onReadyRead).Get());
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ }
+
+ void setSocket(ComPtr<IStreamSocket> socket) { m_socket = socket; }
+
+private:
+ ComPtr<IStreamSocket> m_socket;
+ QVector<QByteArray> m_pendingData;
+
+ // Protects pendingData/pendingDatagrams which are accessed from native callbacks
+ QMutex m_mutex;
+
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_initialReadOp;
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
+};
+
+QBluetoothSocketPrivate::QBluetoothSocketPrivate()
+ : socket(-1),
+ socketType(QBluetoothServiceInfo::UnknownProtocol),
+ state(QBluetoothSocket::UnconnectedState),
+ socketError(QBluetoothSocket::NoSocketError),
+ secFlags(QBluetooth::NoSecurity),
+ m_worker(new SocketWorker())
+{
+ connect(m_worker, &SocketWorker::newDataReceived,
+ this, &QBluetoothSocketPrivate::handleNewData, Qt::QueuedConnection);
+ connect(m_worker, &SocketWorker::socketErrorOccured,
+ this, &QBluetoothSocketPrivate::handleError, Qt::QueuedConnection);
+}
+
+QBluetoothSocketPrivate::~QBluetoothSocketPrivate()
+{
+ abort();
+}
+
+bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ if (socket != -1) {
+ if (type == socketType)
+ return true;
+ m_socketObject = nullptr;
+ socket = -1;
+ }
+ socketType = type;
+ if (socketType != QBluetoothServiceInfo::RfcommProtocol)
+ return false;
+
+ HRESULT hr;
+ hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &m_socketObject);
+ if (FAILED(hr) || !m_socketObject) {
+ qErrnoWarning(hr, "ensureNativeSocket: Could not create socket instance");
+ return false;
+ }
+ socket = qintptr(m_socketObject.Get());
+ m_worker->setSocket(m_socketObject);
+
+ return true;
+}
+
+void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+ Q_UNUSED(openMode);
+
+ if (socket == -1 && !ensureNativeSocket(socketType)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return;
+ }
+
+ const QString addressString = address.toString();
+ HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
+ ComPtr<IHostNameFactory> hostNameFactory;
+ HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
+ &hostNameFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
+ RETURN_VOID_IF_FAILED("QBluetoothSocketPrivate::connectToService: Could not create hostname.");
+
+ const QString portString = QString::number(port);
+ HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
+
+ hr = m_socketObject->ConnectAsync(remoteHost.Get(), portReference.Get(), &m_connectOp);
+ if (hr == E_ACCESSDENIED) {
+ qErrnoWarning(hr, "QBluetoothSocketPrivate::connectToService: Unable to connect to bluetooth socket."
+ "Please check your manifest capabilities.");
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return;
+ }
+ Q_ASSERT_SUCCEEDED(hr);
+
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+ q->setOpenMode(openMode);
+ QEventDispatcherWinRT::runOnXamlThread([this]() {
+ HRESULT hr;
+ hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
+ this, &QBluetoothSocketPrivate::handleConnectOpFinished).Get());
+ RETURN_HR_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
+ return S_OK;
+ });
+}
+
+void QBluetoothSocketPrivate::abort()
+{
+ Q_Q(QBluetoothSocket);
+ if (state == QBluetoothSocket::UnconnectedState)
+ return;
+
+ disconnect(m_worker, &SocketWorker::newDataReceived,
+ this, &QBluetoothSocketPrivate::handleNewData);
+ disconnect(m_worker, &SocketWorker::socketErrorOccured,
+ this, &QBluetoothSocketPrivate::handleError);
+ m_worker->deleteLater();
+
+ if (socket != -1) {
+ m_socketObject = nullptr;
+ socket = -1;
+ }
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+}
+
+QString QBluetoothSocketPrivate::localName() const
+{
+ const QBluetoothAddress address = localAddress();
+ if (address.isNull())
+ return QString();
+
+ QBluetoothLocalDevice device(address);
+ return device.name();
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::localAddress() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> localHost;
+ hr = info->get_LocalAddress(&localHost);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString localAddress;
+ hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return QBluetoothAddress(qt_QStringFromHString(localAddress));
+}
+
+quint16 QBluetoothSocketPrivate::localPort() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString localPortString;
+ hr = info->get_LocalPort(localPortString.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return qt_QStringFromHString(localPortString).toInt();
+}
+
+QString QBluetoothSocketPrivate::peerName() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = info->get_RemoteHostName(&remoteHost);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString remoteHostName;
+ hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return qt_QStringFromHString(remoteHostName);
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = info->get_RemoteAddress(&remoteHost);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString remoteAddress;
+ hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return QBluetoothAddress(qt_QStringFromHString(remoteAddress));
+}
+
+quint16 QBluetoothSocketPrivate::peerPort() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString remotePortString;
+ hr = info->get_LocalPort(remotePortString.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return qt_QStringFromHString(remotePortString).toInt();
+}
+
+qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ ComPtr<IOutputStream> stream;
+ HRESULT hr;
+ hr = m_socketObject->get_OutputStream(&stream);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ qint64 bytesWritten = writeIOStream(stream, data, maxSize);
+ if (bytesWritten < 0) {
+ qCWarning(QT_BT_WINRT) << "Socket::writeData: " << state;
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ }
+
+ emit q->bytesWritten(bytesWritten);
+ return bytesWritten;
+}
+
+qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ if (!buffer.isEmpty())
+ return buffer.read(data, maxSize);
+
+ return 0;
+}
+
+void QBluetoothSocketPrivate::close()
+{
+ abort();
+}
+
+bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
+{
+ Q_UNUSED(socketDescriptor);
+ Q_UNUSED(socketType)
+ Q_UNUSED(socketState);
+ Q_UNUSED(openMode);
+ qCWarning(QT_BT_WINRT) << "No socket descriptor support on WinRT.";
+ return false;
+}
+
+bool QBluetoothSocketPrivate::setSocketDescriptor(ComPtr<IStreamSocket> socketPtr, QBluetoothServiceInfo::Protocol socketType,
+ QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+ if (socketType != QBluetoothServiceInfo::RfcommProtocol || !socketPtr)
+ return false;
+
+ m_socketObject = socketPtr;
+ socket = qintptr(m_socketObject.Get());
+ m_worker->setSocket(m_socketObject);
+ q->setSocketState(socketState);
+ if (socketState == QBluetoothSocket::ConnectedState)
+ m_worker->startReading();
+ q->setOpenMode(openMode);
+ return true;
+}
+
+qint64 QBluetoothSocketPrivate::bytesAvailable() const
+{
+ return buffer.size();
+}
+
+void QBluetoothSocketPrivate::handleNewData(const QVector<QByteArray> &data)
+{
+ // Defer putting the data into the list until the next event loop iteration
+ // (where the readyRead signal is emitted as well)
+ QMetaObject::invokeMethod(this, "addToPendingData", Qt::QueuedConnection,
+ Q_ARG(QVector<QByteArray>, data));
+}
+
+void QBluetoothSocketPrivate::handleError(QBluetoothSocket::SocketError error)
+{
+ Q_Q(QBluetoothSocket);
+ switch (error) {
+ case QBluetoothSocket::NetworkError:
+ errorString = QBluetoothSocket::tr("Network error");
+ break;
+ default:
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ }
+
+ q->setSocketError(error);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+}
+
+void QBluetoothSocketPrivate::addToPendingData(const QVector<QByteArray> &data)
+{
+ Q_Q(QBluetoothSocket);
+ QMutexLocker locker(&m_readMutex);
+ m_pendingData.append(data);
+ for (const QByteArray &newData : data) {
+ char *writePointer = buffer.reserve(newData.length());
+ memcpy(writePointer, newData.data(), newData.length());
+ }
+ locker.unlock();
+ emit q->readyRead();
+}
+
+HRESULT QBluetoothSocketPrivate::handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status)
+{
+ Q_Q(QBluetoothSocket);
+ if (status != Completed || !m_connectOp) { // Protect against a late callback
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ }
+
+ HRESULT hr = action->GetResults();
+ switch (hr) {
+ case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
+ errorString = QBluetoothSocket::tr("Connection timed out");
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ case 0x80072751: // A socket operation was attempted to an unreachable host.
+ errorString = QBluetoothSocket::tr("Host not reachable");
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ case 0x8007274d: // No connection could be made because the target machine actively refused it.
+ errorString = QBluetoothSocket::tr("Host refused connection");
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ default:
+ if (FAILED(hr)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ }
+ }
+
+ // The callback might be triggered several times if we do not cancel/reset it here
+ if (m_connectOp) {
+ ComPtr<IAsyncInfo> info;
+ hr = m_connectOp.As(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (info) {
+ hr = info->Cancel();
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->Close();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ hr = m_connectOp.Reset();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ m_worker->startReading();
+ emit q->connected();
+
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#include "qbluetoothsocket_winrt.moc"
diff --git a/src/bluetooth/qbluetoothtransfermanager.cpp b/src/bluetooth/qbluetoothtransfermanager.cpp
index 7206dce6..165faceb 100644
--- a/src/bluetooth/qbluetoothtransfermanager.cpp
+++ b/src/bluetooth/qbluetoothtransfermanager.cpp
@@ -40,7 +40,7 @@
#include "qbluetoothtransfermanager.h"
#include "qbluetoothtransferrequest.h"
#include "qbluetoothtransferreply.h"
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
#include "qbluetoothtransferreply_bluez_p.h"
#elif QT_OSX_BLUETOOTH
#include "qbluetoothtransferreply_osx_p.h"
@@ -112,7 +112,7 @@ QBluetoothTransferManager::~QBluetoothTransferManager()
QBluetoothTransferReply *QBluetoothTransferManager::put(const QBluetoothTransferRequest &request,
QIODevice *data)
{
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
QBluetoothTransferReplyBluez *rep = new QBluetoothTransferReplyBluez(data, request, this);
connect(rep, SIGNAL(finished(QBluetoothTransferReply*)), this, SIGNAL(finished(QBluetoothTransferReply*)));
return rep;
@@ -121,8 +121,8 @@ QBluetoothTransferReply *QBluetoothTransferManager::put(const QBluetoothTransfer
connect(reply, SIGNAL(finished(QBluetoothTransferReply*)), this, SIGNAL(finished(QBluetoothTransferReply*)));
return reply;
#else
- // Android and iOS have no implementation
-#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH)
+ // Android, iOS, and WinRT have no implementation
+#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH)
printDummyWarning();
#endif
Q_UNUSED(request);
diff --git a/src/bluetooth/qleadvertiser_p.h b/src/bluetooth/qleadvertiser_p.h
index 8e71844e..922fdf64 100644
--- a/src/bluetooth/qleadvertiser_p.h
+++ b/src/bluetooth/qleadvertiser_p.h
@@ -54,7 +54,7 @@
#include "qlowenergyadvertisingdata.h"
#include "qlowenergyadvertisingparameters.h"
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
#include "bluez/bluez_data_p.h"
#endif
@@ -94,7 +94,7 @@ private:
};
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
struct AdvData;
struct AdvParams;
class HciManager;
@@ -147,7 +147,7 @@ private:
bool m_sendPowerLevel;
bool m_disableCommandFinished;
};
-#endif // QT_BLUEZ_BLUETOOTH
+#endif // QT_CONFIG(bluez)
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergyadvertisingdata.cpp b/src/bluetooth/qlowenergyadvertisingdata.cpp
index bf41c051..3837025b 100644
--- a/src/bluetooth/qlowenergyadvertisingdata.cpp
+++ b/src/bluetooth/qlowenergyadvertisingdata.cpp
@@ -129,6 +129,13 @@ QLowEnergyAdvertisingData &QLowEnergyAdvertisingData::operator=(const QLowEnergy
Specifies that \a name should be broadcast as the name of the device. If the full name does not
fit into the advertising data packet, an abbreviated name is sent, as described by the
Bluetooth Low Energy specification.
+
+ On Android, the local name cannot be changed. Android always uses the device name.
+ If this local name is not empty, the Android implementation includes the device name
+ in the advertisement packet; otherwise the device name is omitted from the advertisement
+ packet.
+
+ \sa localName()
*/
void QLowEnergyAdvertisingData::setLocalName(const QString &name)
{
@@ -137,6 +144,8 @@ void QLowEnergyAdvertisingData::setLocalName(const QString &name)
/*!
Returns the name of the local device that is to be advertised.
+
+ \sa setLocalName()
*/
QString QLowEnergyAdvertisingData::localName() const
{
diff --git a/src/bluetooth/qlowenergyconnectionparameters.cpp b/src/bluetooth/qlowenergyconnectionparameters.cpp
index af4502dd..95256402 100644
--- a/src/bluetooth/qlowenergyconnectionparameters.cpp
+++ b/src/bluetooth/qlowenergyconnectionparameters.cpp
@@ -68,6 +68,32 @@ public:
with each other. In general, a lower connection interval and latency means faster communication,
but also higher power consumption. How these criteria should be weighed against each other
is highly dependent on the concrete use case.
+
+ Android only indirectly permits the adjustment of this parameter set.
+ The platform separates the connection parameters into three categories (hight, low & balanced
+ priority). Each category implies a predefined set of values for \l minimumInterval(),
+ \l maximumInterval() and \l latency(). Additionally, the value ranges of each category can vary
+ from one Android device to the next. Qt uses the \l minimumInterval() to determine the target
+ category as follows:
+
+ \table
+ \header
+ \li minimumInterval()
+ \li Android priority
+ \row
+ \li interval < 30
+ \li CONNECTION_PRIORITY_HIGH
+ \row
+ \li 30 <= interval <= 100
+ \li CONNECTION_PRIORITY_BALANCED
+ \row
+ \li interval > 100
+ \li CONNECTION_PRIORITY_LOW_POWER
+ \endtable
+
+ The \l supervisionTimeout() cannot be changed on Android and is therefore ignored.
+
+
\inmodule QtBluetooth
\ingroup shared
@@ -163,6 +189,9 @@ int QLowEnergyConnectionParameters::latency() const
Sets the link supervision timeout to \a timeout milliseconds.
There are several constraints on this value: It must be in the range [100,32000] and it must be
larger than (1 + \l latency()) * 2 * \l maximumInterval().
+
+ On Android, this timeout is not adjustable and therefore ignored.
+
\sa supervisionTimeout()
*/
void QLowEnergyConnectionParameters::setSupervisionTimeout(int timeout)
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index 03278276..a3aad282 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -359,7 +359,12 @@ void QLowEnergyControllerPrivate::invalidateServices()
QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivate::serviceForHandle(
QLowEnergyHandle handle)
{
- foreach (QSharedPointer<QLowEnergyServicePrivate> service, serviceList.values())
+ ServiceDataMap &currentList = serviceList;
+ if (role == QLowEnergyController::PeripheralRole)
+ currentList = localServices;
+
+ const QList<QSharedPointer<QLowEnergyServicePrivate>> values = currentList.values();
+ for (auto service: values)
if (service->startHandle <= handle && handle <= service->endHandle)
return service;
@@ -963,19 +968,35 @@ QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData
/*!
Requests the controller to update the connection according to \a parameters.
If the request is successful, the \l connectionUpdated() signal will be emitted
- with the actual new parameters.
- See the \l QLowEnergyConnectionParameters class for more information on connection parameters.
- \note Currently, this functionality is only implemented on Linux.
-
+ with the actual new parameters. See the \l QLowEnergyConnectionParameters class for more
+ information on connection parameters.
+
+ Android only indirectly permits the adjustment of this parameter set.
+ The connection parameters are separated into three categories (high, low & balanced priority).
+ Each category implies a pre-configured set of values for
+ \l QLowEnergyConnectionParameters::minimumInterval(),
+ \l QLowEnergyConnectionParameters::maximumInterval() and
+ \l QLowEnergyConnectionParameters::latency(). Although the connection request is an asynchronous
+ operation, Android does not provide a callback stating the result of the request. This is
+ an acknowledged Android bug. Due to this bug Android does not emit the \l connectionUpdated()
+ signal.
+
+ \note Currently, this functionality is only implemented on Linux and Android.
+
+ \sa connectionUpdated()
\since 5.7
*/
void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters &parameters)
{
- if (state() != ConnectedState) {
+ switch (state()) {
+ case ConnectedState:
+ case DiscoveredState:
+ case DiscoveringState:
+ d_ptr->requestConnectionUpdate(parameters);
+ break;
+ default:
qCWarning(QT_BT) << "Connection update request only possible in connected state";
- return;
}
- d_ptr->requestConnectionUpdate(parameters);
}
/*!
diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp
index cd6603a3..a1decd96 100644
--- a/src/bluetooth/qlowenergycontroller_android.cpp
+++ b/src/bluetooth/qlowenergycontroller_android.cpp
@@ -40,11 +40,35 @@
#include "qlowenergycontroller_p.h"
#include <QtCore/QLoggingCategory>
#include <QtAndroidExtras/QAndroidJniEnvironment>
+#include <QtBluetooth/QLowEnergyServiceData>
+#include <QtBluetooth/QLowEnergyCharacteristicData>
+#include <QtBluetooth/QLowEnergyDescriptorData>
+#include <QtBluetooth/QLowEnergyAdvertisingData>
+#include <QtBluetooth/QLowEnergyAdvertisingParameters>
+#include <QtBluetooth/QLowEnergyConnectionParameters>
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
+Q_DECLARE_METATYPE(QAndroidJniObject)
+
+// Conversion: QBluetoothUuid -> java.util.UUID
+static QAndroidJniObject javaUuidfromQtUuid(const QBluetoothUuid& uuid)
+{
+ QString output = uuid.toString();
+ // cut off leading and trailing brackets
+ output = output.mid(1, output.size()-2);
+
+ QAndroidJniObject javaString = QAndroidJniObject::fromString(output);
+ QAndroidJniObject javaUuid = QAndroidJniObject::callStaticObjectMethod(
+ "java/util/UUID", "fromString", "(Ljava/lang/String;)Ljava/util/UUID;",
+ javaString.object());
+
+ return javaUuid;
+}
+
QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
: QObject(),
state(QLowEnergyController::UnconnectedState),
@@ -56,25 +80,48 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
{
+ if (role == QLowEnergyController::PeripheralRole) {
+ if (hub)
+ hub->javaObject().callMethod<void>("disconnectServer");
+ }
}
void QLowEnergyControllerPrivate::init()
{
-}
+ // Android Central/Client support starts with v18
+ // Peripheral/Server support requires Android API v21
+ const bool isPeripheral = (role == QLowEnergyController::PeripheralRole);
+ const jint version = QtAndroidPrivate::androidSdkVersion();
+
+ if (isPeripheral) {
+ if (version < 21) {
+ qWarning() << "Qt Bluetooth LE Peripheral support not available"
+ "on Android devices below version 21";
+ return;
+ }
-void QLowEnergyControllerPrivate::connectToDevice()
-{
- // required to pass unit test on default backend
- if (remoteDevice.isNull()) {
- qWarning() << "Invalid/null remote device address";
- setError(QLowEnergyController::UnknownRemoteDeviceError);
- return;
- }
+ qRegisterMetaType<QAndroidJniObject>();
- setState(QLowEnergyController::ConnectingState);
+ hub = new LowEnergyNotificationHub(remoteDevice, isPeripheral, this);
+ // we only connect to the peripheral role specific signals
+ // TODO add connections as they get added later on
+ connect(hub, &LowEnergyNotificationHub::connectionUpdated,
+ this, &QLowEnergyControllerPrivate::connectionUpdated);
+ connect(hub, &LowEnergyNotificationHub::advertisementError,
+ this, &QLowEnergyControllerPrivate::advertisementError);
+ connect(hub, &LowEnergyNotificationHub::serverCharacteristicChanged,
+ this, &QLowEnergyControllerPrivate::serverCharacteristicChanged);
+ connect(hub, &LowEnergyNotificationHub::serverDescriptorWritten,
+ this, &QLowEnergyControllerPrivate::serverDescriptorWritten);
+ } else {
+ if (version < 18) {
+ qWarning() << "Qt Bluetooth LE Central/Client support not available"
+ "on Android devices below version 18";
+ return;
+ }
- if (!hub) {
- hub = new LowEnergyNotificationHub(remoteDevice, this);
+ hub = new LowEnergyNotificationHub(remoteDevice, isPeripheral, this);
+ // we only connect to the central role specific signals
connect(hub, &LowEnergyNotificationHub::connectionUpdated,
this, &QLowEnergyControllerPrivate::connectionUpdated);
connect(hub, &LowEnergyNotificationHub::servicesDiscovered,
@@ -91,9 +138,22 @@ void QLowEnergyControllerPrivate::connectToDevice()
this, &QLowEnergyControllerPrivate::descriptorWritten);
connect(hub, &LowEnergyNotificationHub::characteristicChanged,
this, &QLowEnergyControllerPrivate::characteristicChanged);
- connect(hub, &LowEnergyNotificationHub::serviceError,
- this, &QLowEnergyControllerPrivate::serviceError);
}
+}
+
+void QLowEnergyControllerPrivate::connectToDevice()
+{
+ if (!hub)
+ return; // Android version below v18
+
+ // required to pass unit test on default backend
+ if (remoteDevice.isNull()) {
+ qWarning() << "Invalid/null remote device address";
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ return;
+ }
+
+ setState(QLowEnergyController::ConnectingState);
if (!hub->javaObject().isValid()) {
qCWarning(QT_BT_ANDROID) << "Cannot initiate QtBluetoothLE";
@@ -194,12 +254,26 @@ void QLowEnergyControllerPrivate::writeCharacteristic(
bool result = false;
if (hub) {
- qCDebug(QT_BT_ANDROID) << "Write characteristic with handle " << charHandle
- << newValue.toHex() << "(service:" << service->uuid
- << ", writeWithResponse:" << (mode == QLowEnergyService::WriteWithResponse)
- << ", signed:" << (mode == QLowEnergyService::WriteSigned) << ")";
- result = hub->javaObject().callMethod<jboolean>("writeCharacteristic", "(I[BI)Z",
- charHandle, payload, mode);
+ if (role == QLowEnergyController::CentralRole) {
+ qCDebug(QT_BT_ANDROID) << "Write characteristic with handle " << charHandle
+ << newValue.toHex() << "(service:" << service->uuid
+ << ", writeWithResponse:" << (mode == QLowEnergyService::WriteWithResponse)
+ << ", signed:" << (mode == QLowEnergyService::WriteSigned) << ")";
+ result = hub->javaObject().callMethod<jboolean>("writeCharacteristic", "(I[BI)Z",
+ charHandle, payload, mode);
+ } else { // peripheral mode
+ qCDebug(QT_BT_ANDROID) << "Write server characteristic with handle " << charHandle
+ << newValue.toHex() << "(service:" << service->uuid;
+
+ const auto &characteristic = characteristicForHandle(charHandle);
+ if (characteristic.isValid()) {
+ const QAndroidJniObject charUuid = javaUuidfromQtUuid(characteristic.uuid());
+ result = hub->javaObject().callMethod<jboolean>(
+ "writeCharacteristic",
+ "(Landroid/bluetooth/BluetoothGattService;Ljava/util/UUID;[B)Z",
+ service->androidService.object(), charUuid.object(), payload);
+ }
+ }
}
if (env->ExceptionOccurred()) {
@@ -216,7 +290,7 @@ void QLowEnergyControllerPrivate::writeCharacteristic(
void QLowEnergyControllerPrivate::writeDescriptor(
const QSharedPointer<QLowEnergyServicePrivate> service,
- const QLowEnergyHandle /*charHandle*/,
+ const QLowEnergyHandle charHandle,
const QLowEnergyHandle descHandle,
const QByteArray &newValue)
{
@@ -230,10 +304,27 @@ void QLowEnergyControllerPrivate::writeDescriptor(
bool result = false;
if (hub) {
- qCDebug(QT_BT_ANDROID) << "Write descriptor with handle " << descHandle
- << newValue.toHex() << "(service:" << service->uuid << ")";
- result = hub->javaObject().callMethod<jboolean>("writeDescriptor", "(I[B)Z",
- descHandle, payload);
+ if (role == QLowEnergyController::CentralRole) {
+ qCDebug(QT_BT_ANDROID) << "Write descriptor with handle " << descHandle
+ << newValue.toHex() << "(service:" << service->uuid << ")";
+ result = hub->javaObject().callMethod<jboolean>("writeDescriptor", "(I[B)Z",
+ descHandle, payload);
+ } else {
+ const auto &characteristic = characteristicForHandle(charHandle);
+ const auto &descriptor = descriptorForHandle(descHandle);
+ if (characteristic.isValid() && descriptor.isValid()) {
+ qCDebug(QT_BT_ANDROID) << "Write descriptor" << descriptor.uuid()
+ << "(service:" << service->uuid
+ << "char: " << characteristic.uuid() << ")";
+ const QAndroidJniObject charUuid = javaUuidfromQtUuid(characteristic.uuid());
+ const QAndroidJniObject descUuid = javaUuidfromQtUuid(descriptor.uuid());
+ result = hub->javaObject().callMethod<jboolean>(
+ "writeDescriptor",
+ "(Landroid/bluetooth/BluetoothGattService;Ljava/util/UUID;Ljava/util/UUID;[B)Z",
+ service->androidService.object(), charUuid.object(),
+ descUuid.object(), payload);
+ }
+ }
}
if (env->ExceptionOccurred()) {
@@ -306,14 +397,56 @@ void QLowEnergyControllerPrivate::connectionUpdated(
QLowEnergyController::ControllerState newState,
QLowEnergyController::Error errorCode)
{
- Q_Q(QLowEnergyController);
-
- const QLowEnergyController::ControllerState oldState = state;
qCDebug(QT_BT_ANDROID) << "Connection updated:"
<< "error:" << errorCode
- << "oldState:" << oldState
+ << "oldState:" << state
<< "newState:" << newState;
+ if (role == QLowEnergyController::PeripheralRole)
+ peripheralConnectionUpdated(newState, errorCode);
+ else
+ centralConnectionUpdated(newState, errorCode);
+}
+
+// called if server/peripheral
+void QLowEnergyControllerPrivate::peripheralConnectionUpdated(
+ QLowEnergyController::ControllerState newState,
+ QLowEnergyController::Error errorCode)
+{
+ // Java errorCode can be larger than max QLowEnergyController::Error
+ if (errorCode > QLowEnergyController::AdvertisingError)
+ errorCode = QLowEnergyController::UnknownError;
+
+ if (errorCode != QLowEnergyController::NoError)
+ setError(errorCode);
+
+ const QLowEnergyController::ControllerState oldState = state;
+ setState(newState);
+
+ // disconnect implies stop of advertisement
+ if (newState == QLowEnergyController::UnconnectedState)
+ stopAdvertising();
+
+
+ Q_Q(QLowEnergyController);
+ if (oldState == QLowEnergyController::ConnectedState
+ && newState != QLowEnergyController::ConnectedState) {
+ emit q->disconnected();
+ } else if (newState == QLowEnergyController::ConnectedState
+ && oldState != QLowEnergyController::ConnectedState) {
+ emit q->connected();
+ }
+}
+
+// called if client/central
+void QLowEnergyControllerPrivate::centralConnectionUpdated(
+ QLowEnergyController::ControllerState newState,
+ QLowEnergyController::Error errorCode)
+{
+ Q_Q(QLowEnergyController);
+
+ const QLowEnergyController::ControllerState oldState = state;
+
if (errorCode != QLowEnergyController::NoError) {
// ConnectionError if transition from Connecting to Connected
if (oldState == QLowEnergyController::ConnectingState) {
@@ -558,6 +691,71 @@ void QLowEnergyControllerPrivate::descriptorWritten(
emit service->descriptorWritten(descriptor, data);
}
+void QLowEnergyControllerPrivate::serverDescriptorWritten(
+ const QAndroidJniObject &jniDesc, const QByteArray &newValue)
+{
+ qCDebug(QT_BT_ANDROID) << "Server descriptor change notification" << newValue.toHex();
+
+ // retrieve service, char and desc uuids
+ QAndroidJniObject jniChar = jniDesc.callObjectMethod(
+ "getCharacteristic", "()Landroid/bluetooth/BluetoothGattCharacteristic;");
+ if (!jniChar.isValid())
+ return;
+
+ QAndroidJniObject jniService = jniChar.callObjectMethod(
+ "getService", "()Landroid/bluetooth/BluetoothGattService;");
+ if (!jniService.isValid())
+ return;
+
+ QAndroidJniObject jniUuid = jniService.callObjectMethod("getUuid", "()Ljava/util/UUID;");
+ const QBluetoothUuid serviceUuid(jniUuid.toString());
+ if (serviceUuid.isNull())
+ return;
+
+ // TODO test if two service with same uuid exist
+ if (!localServices.contains(serviceUuid))
+ return;
+
+ jniUuid = jniChar.callObjectMethod("getUuid", "()Ljava/util/UUID;");
+ const QBluetoothUuid characteristicUuid(jniUuid.toString());
+ if (characteristicUuid.isNull())
+ return;
+
+ jniUuid = jniDesc.callObjectMethod("getUuid", "()Ljava/util/UUID;");
+ const QBluetoothUuid descriptorUuid(jniUuid.toString());
+ if (descriptorUuid.isNull())
+ return;
+
+ // find matching QLEDescriptor
+ auto servicePrivate = localServices.value(serviceUuid);
+ // TODO test if service contains two characteristics with same uuid
+ // or characteristic contains two descriptors with same uuid
+ const auto handleList = servicePrivate->characteristicList.keys();
+ for (const auto charHandle: handleList) {
+ const auto &charData = servicePrivate->characteristicList.value(charHandle);
+ if (charData.uuid != characteristicUuid)
+ continue;
+
+ const auto &descHandleList = charData.descriptorList.keys();
+ for (const auto descHandle: descHandleList) {
+ const auto &descData = charData.descriptorList.value(descHandle);
+ if (descData.uuid != descriptorUuid)
+ continue;
+
+ qCDebug(QT_BT_ANDROID) << "serverDescriptorChanged: Matching descriptor"
+ << descriptorUuid << "in char" << characteristicUuid
+ << "of service" << serviceUuid;
+
+ servicePrivate->characteristicList[charHandle].descriptorList[descHandle].value = newValue;
+
+ emit servicePrivate->descriptorWritten(
+ QLowEnergyDescriptor(servicePrivate, charHandle, descHandle),
+ newValue);
+ return;
+ }
+ }
+}
+
void QLowEnergyControllerPrivate::characteristicChanged(
int charHandle, const QByteArray &data)
{
@@ -583,6 +781,55 @@ void QLowEnergyControllerPrivate::characteristicChanged(
emit service->characteristicChanged(characteristic, data);
}
+void QLowEnergyControllerPrivate::serverCharacteristicChanged(
+ const QAndroidJniObject &characteristic, const QByteArray &newValue)
+{
+ qCDebug(QT_BT_ANDROID) << "Server characteristic change notification" << newValue.toHex();
+
+ // match characteristic to servicePrivate
+ QAndroidJniObject service = characteristic.callObjectMethod(
+ "getService", "()Landroid/bluetooth/BluetoothGattService;");
+ if (!service.isValid())
+ return;
+
+ QAndroidJniObject jniUuid = service.callObjectMethod("getUuid", "()Ljava/util/UUID;");
+ QBluetoothUuid serviceUuid(jniUuid.toString());
+ if (serviceUuid.isNull())
+ return;
+
+ // TODO test if two service with same uuid exist
+ if (!localServices.contains(serviceUuid))
+ return;
+
+ auto servicePrivate = localServices.value(serviceUuid);
+
+ jniUuid = characteristic.callObjectMethod("getUuid", "()Ljava/util/UUID;");
+ QBluetoothUuid characteristicUuid(jniUuid.toString());
+ if (characteristicUuid.isNull())
+ return;
+
+ QLowEnergyHandle foundHandle = 0;
+ const auto handleList = servicePrivate->characteristicList.keys();
+ // TODO test if service contains two characteristics with same uuid
+ for (const auto handle: handleList) {
+ QLowEnergyServicePrivate::CharData &charData = servicePrivate->characteristicList[handle];
+ if (charData.uuid != characteristicUuid)
+ continue;
+
+ qCDebug(QT_BT_ANDROID) << "serverCharacteristicChanged: Matching characteristic"
+ << characteristicUuid << " on " << serviceUuid;
+ charData.value = newValue;
+ foundHandle = handle;
+ break;
+ }
+
+ if (!foundHandle)
+ return;
+
+ emit servicePrivate->characteristicChanged(
+ QLowEnergyCharacteristic(servicePrivate, foundHandle), newValue);
+}
+
void QLowEnergyControllerPrivate::serviceError(
int attributeHandle, QLowEnergyService::ServiceError errorCode)
{
@@ -599,32 +846,418 @@ void QLowEnergyControllerPrivate::serviceError(
service->setError(errorCode);
}
+void QLowEnergyControllerPrivate::advertisementError(int errorCode)
+{
+ Q_Q(QLowEnergyController);
+
+ switch (errorCode)
+ {
+ case 1: // AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE
+ errorString = QLowEnergyController::tr("Advertisement data is larger than 31 bytes");
+ break;
+ case 2: // AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED
+ errorString = QLowEnergyController::tr("Advertisement feature not supported on the platform");
+ break;
+ case 3: // AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR
+ errorString = QLowEnergyController::tr("Error occurred trying to start advertising");
+ break;
+ case 4: // AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS
+ errorString = QLowEnergyController::tr("Failed due to too many advertisers");
+ break;
+ default:
+ errorString = QLowEnergyController::tr("Unknown advertisement error");
+ break;
+ }
+
+ error = QLowEnergyController::AdvertisingError;
+ emit q->error(error);
+
+ // not relevant states in peripheral mode
+ Q_ASSERT(state != QLowEnergyController::DiscoveredState);
+ Q_ASSERT(state != QLowEnergyController::DiscoveringState);
+
+ switch (state)
+ {
+ case QLowEnergyController::UnconnectedState:
+ case QLowEnergyController::ConnectingState:
+ case QLowEnergyController::ConnectedState:
+ case QLowEnergyController::ClosingState:
+ // noop as remote is already connected or about to disconnect.
+ // when connection drops we reset to unconnected anyway
+ break;
+
+ case QLowEnergyController::AdvertisingState:
+ setState(QLowEnergyController::UnconnectedState);
+ break;
+ default:
+ break;
+ }
+}
+
+static QAndroidJniObject javaParcelUuidfromQtUuid(const QBluetoothUuid &uuid)
+{
+ QString output = uuid.toString();
+ // cut off leading and trailing brackets
+ output = output.mid(1, output.size()-2);
+
+ QAndroidJniObject javaString = QAndroidJniObject::fromString(output);
+ QAndroidJniObject parcelUuid = QAndroidJniObject::callStaticObjectMethod(
+ "android/os/ParcelUuid", "fromString",
+ "(Ljava/lang/String;)Landroid/os/ParcelUuid;", javaString.object());
+
+ return parcelUuid;
+}
+
+static QAndroidJniObject createJavaAdvertiseData(const QLowEnergyAdvertisingData &data)
+{
+ QAndroidJniObject builder = QAndroidJniObject("android/bluetooth/le/AdvertiseData$Builder");
+
+ // device name cannot be set but there is choice to show it or not
+ builder = builder.callObjectMethod("setIncludeDeviceName", "(Z)Landroid/bluetooth/le/AdvertiseData$Builder;",
+ !data.localName().isEmpty());
+ builder = builder.callObjectMethod("setIncludeTxPowerLevel", "(Z)Landroid/bluetooth/le/AdvertiseData$Builder;",
+ data.includePowerLevel());
+ for (const auto service: data.services())
+ {
+ builder = builder.callObjectMethod("addServiceUuid",
+ "(Landroid/os/ParcelUuid;)Landroid/bluetooth/le/AdvertiseData$Builder;",
+ javaParcelUuidfromQtUuid(service).object());
+ }
+
+ if (!data.manufacturerData().isEmpty()) {
+ QAndroidJniEnvironment env;
+ const qint32 nativeSize = data.manufacturerData().size();
+ jbyteArray nativeData = env->NewByteArray(nativeSize);
+ env->SetByteArrayRegion(nativeData, 0, nativeSize,
+ reinterpret_cast<const jbyte*>(data.manufacturerData().constData()));
+ builder = builder.callObjectMethod("addManufacturerData",
+ "(I[B])Landroid/bluetooth/le/AdvertiseData$Builder;",
+ data.manufacturerId(), nativeData);
+ env->DeleteLocalRef(nativeData);
+
+ if (env->ExceptionCheck()) {
+ qCWarning(QT_BT_ANDROID) << "Cannot set manufacturer id/data";
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ }
+
+ /*// TODO Qt vs Java API mismatch
+ -> Qt assumes rawData() is a global field
+ -> Android pairs rawData() per service uuid
+ if (!data.rawData().isEmpty()) {
+ QAndroidJniEnvironment env;
+ qint32 nativeSize = data.rawData().size();
+ jbyteArray nativeData = env->NewByteArray(nativeSize);
+ env->SetByteArrayRegion(nativeData, 0, nativeSize,
+ reinterpret_cast<const jbyte*>(data.rawData().constData()));
+ builder = builder.callObjectMethod("addServiceData",
+ "(Landroid/os/ParcelUuid;[B])Landroid/bluetooth/le/AdvertiseData$Builder;",
+ data.rawData().object(), nativeData);
+ env->DeleteLocalRef(nativeData);
+
+ if (env->ExceptionCheck()) {
+ qCWarning(QT_BT_ANDROID) << "Cannot set advertisement raw data";
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ }*/
+
+ QAndroidJniObject javaAdvertiseData = builder.callObjectMethod("build",
+ "()Landroid/bluetooth/le/AdvertiseData;");
+ return javaAdvertiseData;
+}
+
+static QAndroidJniObject createJavaAdvertiseSettings(const QLowEnergyAdvertisingParameters &params)
+{
+ QAndroidJniObject builder = QAndroidJniObject("android/bluetooth/le/AdvertiseSettings$Builder");
+
+ bool connectable = false;
+ switch (params.mode())
+ {
+ case QLowEnergyAdvertisingParameters::AdvInd:
+ connectable = true;
+ break;
+ case QLowEnergyAdvertisingParameters::AdvScanInd:
+ case QLowEnergyAdvertisingParameters::AdvNonConnInd:
+ connectable = false;
+ break;
+ // intentionally no default case
+ }
+ builder = builder.callObjectMethod("setConnectable", "(Z)Landroid/bluetooth/le/AdvertiseSettings$Builder;",
+ connectable);
+
+ /* TODO No Android API for further QLowEnergyAdvertisingParameters options
+ * Android TxPowerLevel, AdvertiseMode and Timeout not mappable to Qt
+ */
+
+ QAndroidJniObject javaAdvertiseSettings = builder.callObjectMethod("build",
+ "()Landroid/bluetooth/le/AdvertiseSettings;");
+ return javaAdvertiseSettings;
+}
+
+
void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters &params,
const QLowEnergyAdvertisingData &advertisingData,
const QLowEnergyAdvertisingData &scanResponseData)
{
- Q_UNUSED(params);
- Q_UNUSED(advertisingData);
- Q_UNUSED(scanResponseData);
- qCWarning(QT_BT_ANDROID) << "LE advertising not implemented for Android";
+ setState(QLowEnergyController::AdvertisingState);
+
+ if (!hub->javaObject().isValid()) {
+ qCWarning(QT_BT_ANDROID) << "Cannot initiate QtBluetoothLEServer";
+ setError(QLowEnergyController::AdvertisingError);
+ setState(QLowEnergyController::UnconnectedState);
+ return;
+ }
+
+ // Pass on advertisingData, scanResponse & AdvertiseSettings
+ QAndroidJniObject jAdvertiseData = createJavaAdvertiseData(advertisingData);
+ QAndroidJniObject jScanResponse = createJavaAdvertiseData(scanResponseData);
+ QAndroidJniObject jAdvertiseSettings = createJavaAdvertiseSettings(params);
+
+ const bool result = hub->javaObject().callMethod<jboolean>("startAdvertising",
+ "(Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseSettings;)Z",
+ jAdvertiseData.object(), jScanResponse.object(), jAdvertiseSettings.object());
+ if (!result) {
+ setError(QLowEnergyController::AdvertisingError);
+ setState(QLowEnergyController::UnconnectedState);
+ }
}
void QLowEnergyControllerPrivate::stopAdvertising()
{
- qCWarning(QT_BT_ANDROID) << "LE advertising not implemented for Android";
+ setState(QLowEnergyController::UnconnectedState);
+ hub->javaObject().callMethod<void>("stopAdvertising");
}
void QLowEnergyControllerPrivate::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
{
- Q_UNUSED(params);
- qCWarning(QT_BT_ANDROID) << "Connection update not implemented for Android";
+ // Possible since Android v21
+ // Android does not permit specification of specific latency or min/max
+ // connection intervals (see BluetoothGatt.requestConnectionPriority()
+ // In fact, each device manufacturer is permitted to change those values via a config
+ // file too. Therefore we can only make an approximated guess (see implementation below)
+ // In addition there is no feedback signal (known bug) from the hardware layer as per v24.
+
+ // TODO recheck in later Android releases whether callback for
+ // BluetoothGatt.requestConnectionPriority() was added
+
+ if (role != QLowEnergyController::CentralRole) {
+ qCWarning(QT_BT_ANDROID) << "On Android, connection requests only work for central role";
+ return;
+ }
+
+ const bool result = hub->javaObject().callMethod<jboolean>("requestConnectionUpdatePriority",
+ "(D)Z", params.minimumInterval());
+ if (!result)
+ qCWarning(QT_BT_ANDROID) << "Cannot set connection update priority";
}
-void QLowEnergyControllerPrivate::addToGenericAttributeList(const QLowEnergyServiceData &service,
+/*
+ * Returns the Java char permissions based on the given characteristic data.
+ */
+static int setupCharPermissions(const QLowEnergyCharacteristicData &charData)
+{
+ int permission = 0;
+ if (charData.properties() & QLowEnergyCharacteristic::Read) {
+ if (int(charData.readConstraints()) == 0 // nothing is equivalent to simple read
+ || (charData.readConstraints() & QBluetooth::AttAuthorizationRequired)) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_READ");
+ }
+
+ if (charData.readConstraints() & QBluetooth::AttAuthenticationRequired) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_READ_ENCRYPTED");
+ }
+
+ if (charData.readConstraints() & QBluetooth::AttEncryptionRequired) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_READ_ENCRYPTED_MITM");
+ }
+ }
+
+ if (charData.properties() &
+ (QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse) ) {
+ if (int(charData.writeConstraints()) == 0 // no flag is equivalent ti simple write
+ || (charData.writeConstraints() & QBluetooth::AttAuthorizationRequired)) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_WRITE");
+ }
+
+ if (charData.writeConstraints() & QBluetooth::AttAuthenticationRequired) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_WRITE_ENCRYPTED");
+ }
+
+ if (charData.writeConstraints() & QBluetooth::AttEncryptionRequired) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_WRITE_ENCRYPTED_MITM");
+ }
+ }
+
+ if (charData.properties() & QLowEnergyCharacteristic::WriteSigned) {
+ if (charData.writeConstraints() & QBluetooth::AttEncryptionRequired) {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_WRITE_SIGNED_MITM");
+ } else {
+ permission |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattCharacteristic",
+ "PERMISSION_WRITE_SIGNED");
+ }
+ }
+
+ return permission;
+}
+
+/*
+ * Returns the Java desc permissions based on the given descriptor data.
+ */
+static int setupDescPermissions(const QLowEnergyDescriptorData &descData)
+{
+ int permissions = 0;
+
+ if (descData.isReadable()) {
+ if (int(descData.readConstraints()) == 0 // empty is equivalent to simple read
+ || (descData.readConstraints() & QBluetooth::AttAuthorizationRequired)) {
+ permissions |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattDescriptor",
+ "PERMISSION_READ");
+ }
+
+ if (descData.readConstraints() & QBluetooth::AttAuthenticationRequired) {
+ permissions |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattDescriptor",
+ "PERMISSION_READ_ENCRYPTED");
+ }
+
+ if (descData.readConstraints() & QBluetooth::AttEncryptionRequired) {
+ permissions |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattDescriptor",
+ "PERMISSION_READ_ENCRYPTED_MITM");
+ }
+ }
+
+ if (descData.isWritable()) {
+ if (int(descData.readConstraints()) == 0 // empty is equivalent to simple read
+ || (descData.readConstraints() & QBluetooth::AttAuthorizationRequired)) {
+ permissions |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattDescriptor",
+ "PERMISSION_WRITE");
+ }
+
+ if (descData.readConstraints() & QBluetooth::AttAuthenticationRequired) {
+ permissions |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattDescriptor",
+ "PERMISSION_WRITE_ENCRYPTED");
+ }
+
+ if (descData.readConstraints() & QBluetooth::AttEncryptionRequired) {
+ permissions |= QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattDescriptor",
+ "PERMISSION_WRITE_ENCRYPTED_MITM");
+ }
+ }
+
+ return permissions;
+}
+
+void QLowEnergyControllerPrivate::addToGenericAttributeList(const QLowEnergyServiceData &serviceData,
QLowEnergyHandle startHandle)
{
- Q_UNUSED(service);
- Q_UNUSED(startHandle);
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(startHandle);
+ if (service.isNull())
+ return;
+
+ // create BluetoothGattService object
+ jint sType = QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattService", "SERVICE_TYPE_PRIMARY");
+ if (serviceData.type() == QLowEnergyServiceData::ServiceTypeSecondary)
+ sType = QAndroidJniObject::getStaticField<jint>(
+ "android/bluetooth/BluetoothGattService", "SERVICE_TYPE_SECONDARY");
+
+ service->androidService = QAndroidJniObject("android/bluetooth/BluetoothGattService",
+ "(Ljava/util/UUID;I)V",
+ javaUuidfromQtUuid(service->uuid).object(), sType);
+
+ // add included services, which must have been added earlier already
+ const QList<QLowEnergyService*> includedServices = serviceData.includedServices();
+ for (const auto includedServiceEntry: includedServices) {
+ //TODO test this end-to-end
+ const jboolean result = service->androidService.callMethod<jboolean>(
+ "addService", "(Landroid/bluetooth/BluetoothGattService;)Z",
+ includedServiceEntry->d_ptr->androidService.object());
+ if (!result)
+ qWarning(QT_BT_ANDROID) << "Cannot add included service " << includedServiceEntry->serviceUuid()
+ << "to current service" << service->uuid;
+ }
+
+ // add characteristics
+ const QList<QLowEnergyCharacteristicData> serviceCharsData = serviceData.characteristics();
+ for (const auto &charData: serviceCharsData) {
+ QAndroidJniObject javaChar = QAndroidJniObject("android/bluetooth/BluetoothGattCharacteristic",
+ "(Ljava/util/UUID;II)V",
+ javaUuidfromQtUuid(charData.uuid()).object(),
+ int(charData.properties()),
+ setupCharPermissions(charData));
+
+ QAndroidJniEnvironment env;
+ jbyteArray jb = env->NewByteArray(charData.value().size());
+ env->SetByteArrayRegion(jb, 0, charData.value().size(), (jbyte*)charData.value().data());
+ jboolean success = javaChar.callMethod<jboolean>("setValue", "([B)Z", jb);
+ if (!success)
+ qCWarning(QT_BT_ANDROID) << "Cannot setup initial characteristic value for " << charData.uuid();
+
+ env->DeleteLocalRef(jb);
+
+ const QList<QLowEnergyDescriptorData> descriptorList = charData.descriptors();
+ for (const auto &descData: descriptorList) {
+ QAndroidJniObject javaDesc = QAndroidJniObject("android/bluetooth/BluetoothGattDescriptor",
+ "(Ljava/util/UUID;I)V",
+ javaUuidfromQtUuid(descData.uuid()).object(),
+ setupDescPermissions(descData));
+
+ jb = env->NewByteArray(descData.value().size());
+ env->SetByteArrayRegion(jb, 0, descData.value().size(), (jbyte*)descData.value().data());
+ success = javaDesc.callMethod<jboolean>("setValue", "([B)Z", jb);
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Cannot setup initial descriptor value for "
+ << descData.uuid() << "(char" << charData.uuid()
+ << "on service " << service->uuid << ")";
+ }
+
+ env->DeleteLocalRef(jb);
+
+
+ success = javaChar.callMethod<jboolean>("addDescriptor",
+ "(Landroid/bluetooth/BluetoothGattDescriptor;)Z",
+ javaDesc.object());
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Cannot add descriptor" << descData.uuid()
+ << "to service" << service->uuid << "(char:"
+ << charData.uuid() << ")";
+ }
+ }
+
+ success = service->androidService.callMethod<jboolean>(
+ "addCharacteristic",
+ "(Landroid/bluetooth/BluetoothGattCharacteristic;)Z", javaChar.object());
+ if (!success) {
+ qCWarning(QT_BT_ANDROID) << "Cannot add characteristic" << charData.uuid()
+ << "to service" << service->uuid;
+ }
+ }
+
+ hub->javaObject().callMethod<void>("addService",
+ "(Landroid/bluetooth/BluetoothGattService;)V",
+ service->androidService.object());
}
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp
index f8d2ba2a..e5a3d8de 100644
--- a/src/bluetooth/qlowenergycontroller_bluez.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluez.cpp
@@ -2889,7 +2889,10 @@ void QLowEnergyControllerPrivate::handleConnectionRequest()
QBluetoothSocket::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered);
restoreClientConfigurations();
loadSigningDataIfNecessary(RemoteSigningKey);
+
+ Q_Q(QLowEnergyController);
setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
}
void QLowEnergyControllerPrivate::closeServerSocket()
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
index 19d10567..5fcc46a4 100644
--- a/src/bluetooth/qlowenergycontroller_p.h
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -77,7 +77,7 @@ QT_END_NAMESPACE
#include "qlowenergycontroller.h"
#include "qlowenergyserviceprivate_p.h"
-#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
#include <QtBluetooth/QBluetoothSocket>
#elif defined(QT_ANDROID_BLUETOOTH)
#include <QtAndroidExtras/QAndroidJniObject>
@@ -96,7 +96,7 @@ QT_BEGIN_NAMESPACE
class QLowEnergyServiceData;
class QTimer;
-#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
class HciManager;
class LeCmacCalculator;
class QSocketNotifier;
@@ -184,10 +184,11 @@ public:
QLowEnergyController::Error error;
QString errorString;
- // list of all found service uuids
+ // list of all found service uuids on remote device
ServiceDataMap serviceList;
QLowEnergyHandle lastLocalHandle;
+ // list of all service uuids on local peripheral device
ServiceDataMap localServices;
struct Attribute {
@@ -208,7 +209,7 @@ public:
QLowEnergyController::RemoteAddressType addressType;
private:
-#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE)
quint16 connectionHandle = 0;
QBluetoothSocket *l2cpSocket;
struct Request {
@@ -439,8 +440,18 @@ private slots:
QLowEnergyService::ServiceError errorCode);
void descriptorWritten(int descHandle, const QByteArray &data,
QLowEnergyService::ServiceError errorCode);
+ void serverDescriptorWritten(const QAndroidJniObject &jniDesc, const QByteArray &newValue);
void characteristicChanged(int charHandle, const QByteArray &data);
+ void serverCharacteristicChanged(const QAndroidJniObject &jniChar, const QByteArray &newValue);
void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode);
+ void advertisementError(int errorCode);
+
+private:
+ void peripheralConnectionUpdated(QLowEnergyController::ControllerState newState,
+ QLowEnergyController::Error errorCode);
+ void centralConnectionUpdated(QLowEnergyController::ControllerState newState,
+ QLowEnergyController::Error errorCode);
+
#elif defined(QT_WINRT_BLUETOOTH)
private slots:
void characteristicChanged(int charHandle, const QByteArray &data);
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
index bde099ba..7727b583 100644
--- a/src/bluetooth/qlowenergyserviceprivate_p.h
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -57,6 +57,10 @@
#include <QtBluetooth/QLowEnergyService>
#include <QtBluetooth/QLowEnergyCharacteristic>
+#if defined(QT_ANDROID_BLUETOOTH)
+#include <QtAndroidExtras/QAndroidJniObject>
+#endif
+
QT_BEGIN_NAMESPACE
class QLowEnergyControllerPrivate;
@@ -119,6 +123,12 @@ public:
QHash<QLowEnergyHandle, CharData> characteristicList;
QPointer<QLowEnergyControllerPrivate> controller;
+
+#if defined(QT_ANDROID_BLUETOOTH)
+ // reference to the BluetoothGattService object
+ QAndroidJniObject androidService;
+#endif
+
};
typedef QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> CharacteristicDataMap;
diff --git a/src/imports/bluetooth/plugin.cpp b/src/imports/bluetooth/plugin.cpp
index f77a4d95..2a152e32 100644
--- a/src/imports/bluetooth/plugin.cpp
+++ b/src/imports/bluetooth/plugin.cpp
@@ -81,9 +81,9 @@ public:
qmlRegisterType<QDeclarativeBluetoothService >(uri, major, minor, "BluetoothService");
qmlRegisterType<QDeclarativeBluetoothSocket >(uri, major, minor, "BluetoothSocket");
- // Register the 5.8 types
- // introduces 5.8 version, other existing 5.2 exports become automatically available under 5.2-5.7
- minor = 8;
+ // Register the 5.9 types
+ // introduces 5.9 version, other existing 5.2 exports become automatically available under 5.2-5.8
+ minor = 9;
qmlRegisterType<QDeclarativeBluetoothDiscoveryModel >(uri, major, minor, "BluetoothDiscoveryModel");
}
};
diff --git a/src/imports/bluetooth/plugins.qmltypes b/src/imports/bluetooth/plugins.qmltypes
index 0ffe3223..e23f8740 100644
--- a/src/imports/bluetooth/plugins.qmltypes
+++ b/src/imports/bluetooth/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtBluetooth 5.8'
+// 'qmlplugindump -nonrelocatable QtBluetooth 5.9'
Module {
dependencies: ["QtQuick 2.8"]
@@ -14,7 +14,7 @@ Module {
exports: [
"QtBluetooth/BluetoothDiscoveryModel 5.0",
"QtBluetooth/BluetoothDiscoveryModel 5.2",
- "QtBluetooth/BluetoothDiscoveryModel 5.8"
+ "QtBluetooth/BluetoothDiscoveryModel 5.9"
]
exportMetaObjectRevisions: [0, 0, 0]
Enum {
diff --git a/src/imports/nfc/plugin.cpp b/src/imports/nfc/plugin.cpp
index d003f531..53febda2 100644
--- a/src/imports/nfc/plugin.cpp
+++ b/src/imports/nfc/plugin.cpp
@@ -98,8 +98,8 @@ public:
minor = 5;
qmlRegisterType<QDeclarativeNearField, 1>(uri, major, minor, "NearField");
- // Register the 5.6 - 5.8 types
- minor = 8;
+ // Register the 5.6 - 5.9 types
+ minor = 9;
qmlRegisterType<QDeclarativeNearField, 1>(uri, major, minor, "NearField");
}
diff --git a/src/imports/nfc/plugins.qmltypes b/src/imports/nfc/plugins.qmltypes
index c9fae196..45cf53b8 100644
--- a/src/imports/nfc/plugins.qmltypes
+++ b/src/imports/nfc/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtNfc 5.8'
+// 'qmlplugindump -nonrelocatable QtNfc 5.9'
Module {
dependencies: ["QtQuick 2.8"]
@@ -58,7 +58,7 @@ Module {
"QtNfc/NearField 5.2",
"QtNfc/NearField 5.4",
"QtNfc/NearField 5.5",
- "QtNfc/NearField 5.8"
+ "QtNfc/NearField 5.9"
]
exportMetaObjectRevisions: [0, 0, 0, 1, 1]
Property { name: "messageRecords"; type: "QQmlNdefRecord"; isList: true; isReadonly: true }
diff --git a/src/nfc/android/androidjninfc.cpp b/src/nfc/android/androidjninfc.cpp
index 4adb6f21..f21d3b08 100644
--- a/src/nfc/android/androidjninfc.cpp
+++ b/src/nfc/android/androidjninfc.cpp
@@ -88,7 +88,6 @@ 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;
}
diff --git a/src/nfc/nfc.pro b/src/nfc/nfc.pro
index 0819cc4f..ce193efa 100644
--- a/src/nfc/nfc.pro
+++ b/src/nfc/nfc.pro
@@ -74,7 +74,8 @@ linux:!android:qtHaveModule(dbus) {
qllcpserver_p.cpp \
qnearfieldsharemanagerimpl_p.cpp \
qnearfieldsharetargetimpl_p.cpp \
- qnearfieldmanager_neard.cpp
+ qnearfieldmanager_neard.cpp \
+ qnearfieldtarget_neard_p.cpp
include(neard/neard.pri)
@@ -107,6 +108,7 @@ linux:!android:qtHaveModule(dbus) {
android/androidjninfc.cpp \
qnearfieldmanager_android.cpp \
qnearfieldtarget_android.cpp \
+ qnearfieldtarget_android_p.cpp \
qnearfieldsharemanagerimpl_p.cpp \
qnearfieldsharetargetimpl_p.cpp \
android/androidmainnewintentlistener.cpp
@@ -127,7 +129,8 @@ isEmpty(NFC_BACKEND_AVAILABLE) {
qllcpserver_p.cpp \
qnearfieldmanagerimpl_p.cpp \
qnearfieldsharemanagerimpl_p.cpp \
- qnearfieldsharetargetimpl_p.cpp
+ qnearfieldsharetargetimpl_p.cpp \
+ qnearfieldtarget_p.cpp
}
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
diff --git a/src/nfc/qnearfieldmanager_android.cpp b/src/nfc/qnearfieldmanager_android.cpp
index d7db6ecf..a5693218 100644
--- a/src/nfc/qnearfieldmanager_android.cpp
+++ b/src/nfc/qnearfieldmanager_android.cpp
@@ -269,6 +269,10 @@ QByteArray QNearFieldManagerPrivateImpl::getUid(const QAndroidJniObject &intent)
void QNearFieldManagerPrivateImpl::onTargetDiscovered(QAndroidJniObject intent)
{
+ // Only intents with a tag are relevant
+ if (!AndroidNfc::getTag(intent).isValid())
+ return;
+
// Getting UID
QByteArray uid = getUid(intent);
diff --git a/src/nfc/qnearfieldtarget.cpp b/src/nfc/qnearfieldtarget.cpp
index 509160c1..e642824b 100644
--- a/src/nfc/qnearfieldtarget.cpp
+++ b/src/nfc/qnearfieldtarget.cpp
@@ -111,7 +111,7 @@ QT_BEGIN_NAMESPACE
/*!
\enum QNearFieldTarget::Error
- This enum describes the error codes that that a near field target reports.
+ This enum describes the error codes that a near field target reports.
\value NoError No error has occurred.
\value UnknownError An unidentified error occurred.
@@ -123,17 +123,10 @@ QT_BEGIN_NAMESPACE
\value InvalidParametersError Invalid parameters were passed to a tag type specific function.
\value NdefReadError Failed to read NDEF messages from the target.
\value NdefWriteError Failed to write NDEF messages to the target.
+ \value CommandError Failed to send a command to the target.
*/
-// Copied from qbytearray.cpp
-// Modified to initialize the crc with 0x6363 instead of 0xffff and to not invert the final result.
-static const quint16 crc_tbl[16] = {
- 0x0000, 0x1081, 0x2102, 0x3183,
- 0x4204, 0x5285, 0x6306, 0x7387,
- 0x8408, 0x9489, 0xa50a, 0xb58b,
- 0xc60c, 0xd68d, 0xe70e, 0xf78f
-};
-
+#if QT_DEPRECATED_SINCE(5, 9)
/*!
\relates QNearFieldTarget
@@ -141,17 +134,9 @@ static const quint16 crc_tbl[16] = {
*/
quint16 qNfcChecksum(const char *data, uint len)
{
- quint16 crc = 0x6363;
- uchar c;
- const uchar *p = reinterpret_cast<const uchar *>(data);
- while (len--) {
- c = *p++;
- crc = ((crc >> 4) & 0x0fff) ^ crc_tbl[((crc ^ c) & 15)];
- c >>= 4;
- crc = ((crc >> 4) & 0x0fff) ^ crc_tbl[((crc ^ c) & 15)];
- }
- return crc;
+ return qChecksum(data, len, Qt::ChecksumItuV41);
}
+#endif
/*!
\fn void QNearFieldTarget::disconnected()
@@ -276,7 +261,7 @@ QNearFieldTarget::RequestId &QNearFieldTarget::RequestId::operator=(const Reques
Constructs a new near field target with \a parent.
*/
QNearFieldTarget::QNearFieldTarget(QObject *parent)
-: QObject(parent), d_ptr(new QNearFieldTargetPrivate)
+: QObject(parent), d_ptr(new QNearFieldTargetPrivate(this))
{
qRegisterMetaType<QNearFieldTarget::RequestId>();
qRegisterMetaType<QNearFieldTarget::Error>();
@@ -318,6 +303,53 @@ QUrl QNearFieldTarget::url() const
*/
/*!
+ \since 5.9
+
+ Returns true if this feature is enabled.
+
+ \sa setKeepConnection(), disconnect()
+*/
+bool QNearFieldTarget::keepConnection() const
+{
+ return d_ptr->keepConnection();
+}
+
+/*!
+ \since 5.9
+
+ Preserves the connection to the target device after processing a command or
+ reading/writing NDEF messages if \a isPersistent is \c true.
+ By default, this behavior is not enabled.
+
+ Returns \c true if enabling this feature was successful. A possible
+ reason for a failure is the lack of support on the used platform.
+
+ Enabling this feature requires to use the disconnect() function too, to close the
+ connection manually and enable communication with the target from a different instance.
+ Disabling this feature will also close an open connection.
+
+ \sa keepConnection(), disconnect()
+*/
+bool QNearFieldTarget::setKeepConnection(bool isPersistent)
+{
+ return d_ptr->setKeepConnection(isPersistent);
+}
+
+/*!
+ \since 5.9
+
+ Closes the connection to the target.
+
+ Returns true only if an existing connection was successfully closed.
+
+ \sa keepConnection(), setKeepConnection()
+*/
+bool QNearFieldTarget::disconnect()
+{
+ return d_ptr->disconnect();
+}
+
+/*!
Returns true if the target is processing commands; otherwise returns false.
*/
bool QNearFieldTarget::isProcessingCommand() const
@@ -364,6 +396,19 @@ QNearFieldTarget::RequestId QNearFieldTarget::writeNdefMessages(const QList<QNde
}
/*!
+ \since 5.9
+
+ Returns the maximum number of bytes that can be sent with sendCommand. 0 will
+ be returned if the target does not support sending tag type specific commands.
+
+ \sa sendCommand(), sendCommands()
+*/
+int QNearFieldTarget::maxCommandLength() const
+{
+ return d_ptr->maxCommandLength();
+}
+
+/*!
Sends \a command to the near field target. Returns a request id which can be used to track the
completion status of the request. An invalid request id will be returned if the target does not
support sending tag type specific commands.
diff --git a/src/nfc/qnearfieldtarget.h b/src/nfc/qnearfieldtarget.h
index dc081f5e..641645c3 100644
--- a/src/nfc/qnearfieldtarget.h
+++ b/src/nfc/qnearfieldtarget.h
@@ -40,9 +40,10 @@
#ifndef QNEARFIELDTARGET_H
#define QNEARFIELDTARGET_H
-#include <QtCore/QObject>
+#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMetaType>
+#include <QtCore/QObject>
#include <QtCore/QSharedDataPointer>
#include <QtNfc/qnfcglobal.h>
@@ -91,7 +92,8 @@ public:
ChecksumMismatchError,
InvalidParametersError,
NdefReadError,
- NdefWriteError
+ NdefWriteError,
+ CommandError
};
Q_ENUM(Error)
@@ -125,6 +127,10 @@ public:
virtual Type type() const = 0;
virtual AccessMethods accessMethods() const = 0;
+ bool keepConnection() const;
+ bool setKeepConnection(bool isPersistent);
+ bool disconnect();
+
bool isProcessingCommand() const;
// NdefAccess
@@ -133,6 +139,7 @@ public:
virtual RequestId writeNdefMessages(const QList<QNdefMessage> &messages);
// TagTypeSpecificAccess
+ int maxCommandLength() const;
virtual RequestId sendCommand(const QByteArray &command);
virtual RequestId sendCommands(const QList<QByteArray> &commands);
@@ -160,7 +167,9 @@ private:
QNearFieldTargetPrivate *d_ptr;
};
+#if QT_DEPRECATED_SINCE(5, 9)
Q_NFC_EXPORT quint16 qNfcChecksum(const char * data, uint len);
+#endif
Q_DECLARE_OPERATORS_FOR_FLAGS(QNearFieldTarget::AccessMethods)
diff --git a/src/nfc/qnearfieldtarget_android.cpp b/src/nfc/qnearfieldtarget_android.cpp
index e0c1616d..50c5c1b3 100644
--- a/src/nfc/qnearfieldtarget_android.cpp
+++ b/src/nfc/qnearfieldtarget_android.cpp
@@ -41,25 +41,27 @@
#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"
+#define NDEFTECHNOLOGY QStringLiteral("android.nfc.tech.Ndef")
+#define NDEFFORMATABLETECHNOLOGY QStringLiteral("android.nfc.tech.NdefFormatable")
+#define ISODEPTECHNOLOGY QStringLiteral("android.nfc.tech.IsoDep")
+#define NFCATECHNOLOGY QStringLiteral("android.nfc.tech.NfcA")
+#define NFCBTECHNOLOGY QStringLiteral("android.nfc.tech.NfcB")
+#define NFCFTECHNOLOGY QStringLiteral("android.nfc.tech.NfcF")
+#define NFCVTECHNOLOGY QStringLiteral("android.nfc.tech.NfcV")
+#define MIFARECLASSICTECHNOLOGY QStringLiteral("android.nfc.tech.MifareClassic")
+#define MIFARECULTRALIGHTTECHNOLOGY QStringLiteral("android.nfc.tech.MifareUltralight")
+
+#define MIFARETAG QStringLiteral("com.nxp.ndef.mifareclassic")
+#define NFCTAGTYPE1 QStringLiteral("org.nfcforum.ndef.type1")
+#define NFCTAGTYPE2 QStringLiteral("org.nfcforum.ndef.type2")
+#define NFCTAGTYPE3 QStringLiteral("org.nfcforum.ndef.type3")
+#define NFCTAGTYPE4 QStringLiteral("org.nfcforum.ndef.type4")
NearFieldTarget::NearFieldTarget(QAndroidJniObject intent, const QByteArray uid, QObject *parent) :
QNearFieldTarget(parent),
m_intent(intent),
- m_uid(uid)
+ m_uid(uid),
+ m_keepConnection(false)
{
updateTechList();
updateType();
@@ -84,13 +86,56 @@ QNearFieldTarget::Type NearFieldTarget::type() const
QNearFieldTarget::AccessMethods NearFieldTarget::accessMethods() const
{
- AccessMethods result = NdefAccess;
+ AccessMethods result = UnknownAccess;
+
+ if (m_techList.contains(NDEFTECHNOLOGY)
+ || m_techList.contains(NDEFFORMATABLETECHNOLOGY))
+ result |= NdefAccess;
+
+ if (m_techList.contains(ISODEPTECHNOLOGY)
+ || m_techList.contains(NFCATECHNOLOGY)
+ || m_techList.contains(NFCBTECHNOLOGY)
+ || m_techList.contains(NFCFTECHNOLOGY)
+ || m_techList.contains(NFCVTECHNOLOGY))
+ result |= TagTypeSpecificAccess;
+
return result;
}
+bool NearFieldTarget::keepConnection() const
+{
+ return m_keepConnection;
+}
+
+bool NearFieldTarget::setKeepConnection(bool isPersistent)
+{
+ m_keepConnection = isPersistent;
+
+ if (!m_keepConnection)
+ disconnect();
+
+ return true;
+}
+
+bool NearFieldTarget::disconnect()
+{
+ if (!m_tagTech.isValid())
+ return false;
+
+ bool connected = m_tagTech.callMethod<jboolean>("isConnected");
+ if (catchJavaExceptions())
+ return false;
+
+ if (!connected)
+ return false;
+
+ m_tagTech.callMethod<void>("close");
+ return !catchJavaExceptions();
+}
+
bool NearFieldTarget::hasNdefMessage()
{
- return m_techList.contains(QStringLiteral(NDEFTECHNOLOGY));
+ return m_techList.contains(NDEFTECHNOLOGY);
}
QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
@@ -109,8 +154,7 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
}
// Getting Ndef technology object
- QAndroidJniObject ndef = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY));
- if (!ndef.isValid()) {
+ if (!setTagTechnology({NDEFTECHNOLOGY})) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::UnsupportedError),
Q_ARG(const QNearFieldTarget::RequestId&, requestId));
@@ -118,8 +162,7 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
}
// Connect
- ndef.callMethod<void>("connect");
- if (catchJavaExceptions()) {
+ if (!connect()) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError),
Q_ARG(const QNearFieldTarget::RequestId&, requestId));
@@ -127,7 +170,7 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
}
// Get NdefMessage object
- QAndroidJniObject ndefMessage = ndef.callObjectMethod("getNdefMessage", "()Landroid/nfc/NdefMessage;");
+ QAndroidJniObject ndefMessage = m_tagTech.callObjectMethod("getNdefMessage", "()Landroid/nfc/NdefMessage;");
if (catchJavaExceptions())
ndefMessage = QAndroidJniObject();
if (!ndefMessage.isValid()) {
@@ -141,9 +184,10 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
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.
+ if (!m_keepConnection) {
+ // Closing connection
+ disconnect(); // IOException at this point does not matter anymore.
+ }
// Sending QNdefMessage, requestCompleted and exit.
QNdefMessage qNdefMessage = QNdefMessage::fromByteArray(ndefMessageQBA);
@@ -157,68 +201,89 @@ QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages()
return requestId;
}
+int NearFieldTarget::maxCommandLength() const
+{
+ QAndroidJniObject tagTech;
+ if (m_techList.contains(ISODEPTECHNOLOGY))
+ tagTech = getTagTechnology(ISODEPTECHNOLOGY);
+ else if (m_techList.contains(NFCATECHNOLOGY))
+ tagTech = getTagTechnology(NFCATECHNOLOGY);
+ else if (m_techList.contains(NFCBTECHNOLOGY))
+ tagTech = getTagTechnology(NFCBTECHNOLOGY);
+ else if (m_techList.contains(NFCFTECHNOLOGY))
+ tagTech = getTagTechnology(NFCFTECHNOLOGY);
+ else if (m_techList.contains(NFCVTECHNOLOGY))
+ tagTech = getTagTechnology(NFCVTECHNOLOGY);
+ else
+ return 0;
+
+ int returnVal = tagTech.callMethod<jint>("getMaxTransceiveLength");
+ if (catchJavaExceptions())
+ return 0;
+
+ return returnVal;
+}
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) {
+ if (command.size() == 0 || command.size() > maxCommandLength()) {
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 {
+ // Making sure that target has commands
+ if (!(accessMethods() & TagTypeSpecificAccess))
+ return QNearFieldTarget::RequestId();
+
+ QAndroidJniEnvironment env;
+
+ if (!setTagTechnology({ISODEPTECHNOLOGY, NFCATECHNOLOGY, NFCBTECHNOLOGY, NFCFTECHNOLOGY, NFCVTECHNOLOGY})) {
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");
+ // Connecting
+ QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
+ if (!connect()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+ // Making QByteArray
+ QByteArray ba(command);
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();
-
-
+ // Writing
+ QAndroidJniObject myNewVal = m_tagTech.callObjectMethod("transceive", "([B)[B", jba);
+ if (catchJavaExceptions()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::CommandError),
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+ return requestId;
+ }
+ QByteArray result = jbyteArrayToQByteArray(myNewVal.object<jbyteArray>());
env->DeleteLocalRef(jba);
+ handleResponse(requestId, result);
- return QNearFieldTarget::RequestId();*/
+ if (!m_keepConnection) {
+ // Closing connection
+ disconnect(); // IOException at this point does not matter anymore.
+ }
+ QMetaObject::invokeMethod(this, "requestCompleted", Qt::QueuedConnection,
+ Q_ARG(const QNearFieldTarget::RequestId&, requestId));
+
+ return requestId;
}
QNearFieldTarget::RequestId NearFieldTarget::sendCommands(const QList<QByteArray> &commands)
{
QNearFieldTarget::RequestId requestId;
- for (int i=0; i < commands.size(); i++){
+ for (int i=0; i < commands.size(); i++)
requestId = sendCommand(commands.at(i));
- }
return requestId;
}
@@ -234,22 +299,18 @@ QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QList<QNdef
const char *writeMethod;
QAndroidJniObject tagTechnology;
+ if (!setTagTechnology({NDEFFORMATABLETECHNOLOGY, NDEFTECHNOLOGY}))
+ return QNearFieldTarget::RequestId();
+
// Getting write method
- if (m_techList.contains(QStringLiteral(NDEFFORMATABLETECHNOLOGY))) {
- tagTechnology = getTagTechnology(QStringLiteral(NDEFFORMATABLETECHNOLOGY));
+ if (m_tech == NDEFFORMATABLETECHNOLOGY)
writeMethod = "format";
- } else if (m_techList.contains(QStringLiteral(NDEFTECHNOLOGY))) {
- tagTechnology = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY));
+ else
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()) {
+ if (!connect()) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNearFieldTarget::Error, QNearFieldTarget::TargetOutOfRangeError),
Q_ARG(const QNearFieldTarget::RequestId&, requestId));
@@ -278,9 +339,8 @@ QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QList<QNdef
return requestId;
}
- // Closing connection, sending signal and exit
- tagTechnology.callMethod<void>("close");
- catchJavaExceptions(); // IOException at this point does not matter anymore.
+ if (!m_keepConnection)
+ disconnect(); // IOException at this point does not matter anymore.
QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection);
return requestId;
}
@@ -302,19 +362,26 @@ void NearFieldTarget::setIntent(QAndroidJniObject intent)
void NearFieldTarget::checkIsTargetLost()
{
- if (!m_intent.isValid() || m_techList.isEmpty()) {
+ if (!m_intent.isValid() || !setTagTechnology(m_techList)) {
+ handleTargetLost();
+ return;
+ }
+
+ bool connected = m_tagTech.callMethod<jboolean>("isConnected");
+ if (catchJavaExceptions()) {
handleTargetLost();
return;
}
- // Using first available technology to check connection
- QString techStr = m_techList.first();
- QAndroidJniObject tagTech = getTagTechnology(techStr);
- tagTech.callMethod<void>("connect");
+
+ if (connected)
+ return;
+
+ m_tagTech.callMethod<void>("connect");
if (catchJavaExceptions(false)) {
handleTargetLost();
return;
}
- tagTech.callMethod<void>("close");
+ m_tagTech.callMethod<void>("close");
if (catchJavaExceptions(false))
handleTargetLost();
}
@@ -334,6 +401,8 @@ void NearFieldTarget::updateTechList()
// Getting tech list
QAndroidJniEnvironment env;
QAndroidJniObject tag = AndroidNfc::getTag(m_intent);
+ Q_ASSERT_X(tag.isValid(), "updateTechList", "could not get Tag object");
+
QAndroidJniObject techListArray = tag.callObjectMethod("getTechList", "()[Ljava/lang/String;");
if (!techListArray.isValid()) {
handleTargetLost();
@@ -358,28 +427,28 @@ QNearFieldTarget::Type NearFieldTarget::getTagType() const
{
QAndroidJniEnvironment env;
- if (m_techList.contains(QStringLiteral(NDEFTECHNOLOGY))) {
- QAndroidJniObject ndef = getTagTechnology(QStringLiteral(NDEFTECHNOLOGY));
+ if (m_techList.contains(NDEFTECHNOLOGY)) {
+ QAndroidJniObject ndef = getTagTechnology(NDEFTECHNOLOGY);
QString qtype = ndef.callObjectMethod("getType", "()Ljava/lang/String;").toString();
- if (qtype.compare(QStringLiteral(MIFARETAG)) == 0)
+ if (qtype.compare(MIFARETAG) == 0)
return MifareTag;
- if (qtype.compare(QStringLiteral(NFCTAGTYPE1)) == 0)
+ if (qtype.compare(NFCTAGTYPE1) == 0)
return NfcTagType1;
- if (qtype.compare(QStringLiteral(NFCTAGTYPE2)) == 0)
+ if (qtype.compare(NFCTAGTYPE2) == 0)
return NfcTagType2;
- if (qtype.compare(QStringLiteral(NFCTAGTYPE3)) == 0)
+ if (qtype.compare(NFCTAGTYPE3) == 0)
return NfcTagType3;
- if (qtype.compare(QStringLiteral(NFCTAGTYPE4)) == 0)
+ if (qtype.compare(NFCTAGTYPE4) == 0)
return NfcTagType4;
return ProprietaryTag;
- } else if (m_techList.contains(QStringLiteral(NFCATECHNOLOGY))) {
- if (m_techList.contains(QStringLiteral(MIFARECLASSICTECHNOLOGY)))
+ } else if (m_techList.contains(NFCATECHNOLOGY)) {
+ if (m_techList.contains(MIFARECLASSICTECHNOLOGY))
return MifareTag;
// Checking ATQA/SENS_RES
// xxx0 0000 xxxx xxxx: Identifies tag Type 1 platform
- QAndroidJniObject nfca = getTagTechnology(QStringLiteral(NFCATECHNOLOGY));
+ QAndroidJniObject nfca = getTagTechnology(NFCATECHNOLOGY);
QAndroidJniObject atqaBA = nfca.callObjectMethod("getAtqa", "()[B");
QByteArray atqaQBA = jbyteArrayToQByteArray(atqaBA.object<jbyteArray>());
if (atqaQBA.isEmpty())
@@ -396,9 +465,9 @@ QNearFieldTarget::Type NearFieldTarget::getTagType() const
else if ((sakS & 0x0064) == 0x0020)
return NfcTagType4;
return ProprietaryTag;
- } else if (m_techList.contains(QStringLiteral(NFCBTECHNOLOGY))) {
+ } else if (m_techList.contains(NFCBTECHNOLOGY)) {
return NfcTagType4;
- } else if (m_techList.contains(QStringLiteral(NFCFTECHNOLOGY))) {
+ } else if (m_techList.contains(NFCFTECHNOLOGY)) {
return NfcTagType3;
}
@@ -409,7 +478,7 @@ void NearFieldTarget::setupTargetCheckTimer()
{
m_targetCheckTimer = new QTimer(this);
m_targetCheckTimer->setInterval(1000);
- connect(m_targetCheckTimer, SIGNAL(timeout()), this, SLOT(checkIsTargetLost()));
+ QObject::connect(m_targetCheckTimer, &QTimer::timeout, this, &NearFieldTarget::checkIsTargetLost);
m_targetCheckTimer->start();
}
@@ -426,10 +495,45 @@ QAndroidJniObject NearFieldTarget::getTagTechnology(const QString &tech) const
// Getting requested technology
QAndroidJniObject tag = AndroidNfc::getTag(m_intent);
+ Q_ASSERT_X(tag.isValid(), "getTagTechnology", "could not get Tag object");
+
const QString sig = QString::fromUtf8("(Landroid/nfc/Tag;)L%1;");
- QAndroidJniObject tagtech = QAndroidJniObject::callStaticObjectMethod(techClass.toUtf8().constData(), "get",
+ QAndroidJniObject tagTech = QAndroidJniObject::callStaticObjectMethod(techClass.toUtf8().constData(), "get",
sig.arg(techClass).toUtf8().constData(), tag.object<jobject>());
- return tagtech;
+
+ return tagTech;
+}
+
+bool NearFieldTarget::setTagTechnology(const QStringList &techList)
+{
+ for (const QString &tech : techList) {
+ if (m_techList.contains(tech)) {
+ if (m_tech == tech) {
+ return true;
+ }
+ m_tech = tech;
+ m_tagTech = getTagTechnology(tech);
+ return m_tagTech.isValid();
+ }
+ }
+
+ return false;
+}
+
+bool NearFieldTarget::connect()
+{
+ if (!m_tagTech.isValid())
+ return false;
+
+ bool connected = m_tagTech.callMethod<jboolean>("isConnected");
+ if (catchJavaExceptions())
+ return false;
+
+ if (connected)
+ return true;
+
+ m_tagTech.callMethod<void>("connect");
+ return !catchJavaExceptions();
}
QByteArray NearFieldTarget::jbyteArrayToQByteArray(const jbyteArray &byteArray) const
diff --git a/src/nfc/qnearfieldtarget_android_p.cpp b/src/nfc/qnearfieldtarget_android_p.cpp
new file mode 100644
index 00000000..de553ea9
--- /dev/null
+++ b/src/nfc/qnearfieldtarget_android_p.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+
+#include "qnearfieldtarget_p.h"
+#include "qnearfieldtarget_android_p.h"
+
+QT_BEGIN_NAMESPACE
+
+bool QNearFieldTargetPrivate::keepConnection() const
+{
+ NEARFIELDTARGET_Q();
+ return q->keepConnection();
+}
+
+bool QNearFieldTargetPrivate::setKeepConnection(bool isPersistent)
+{
+ NEARFIELDTARGET_Q();
+ return q->setKeepConnection(isPersistent);
+}
+
+bool QNearFieldTargetPrivate::disconnect()
+{
+ NEARFIELDTARGET_Q();
+ return q->disconnect();
+}
+
+int QNearFieldTargetPrivate::maxCommandLength() const
+{
+ NEARFIELDTARGET_Q();
+ return q->maxCommandLength();
+}
+
+QT_END_NAMESPACE
diff --git a/src/nfc/qnearfieldtarget_android_p.h b/src/nfc/qnearfieldtarget_android_p.h
index 94bb394d..0063e9a5 100644
--- a/src/nfc/qnearfieldtarget_android_p.h
+++ b/src/nfc/qnearfieldtarget_android_p.h
@@ -75,8 +75,12 @@ public:
virtual QByteArray uid() const;
virtual Type type() const;
virtual AccessMethods accessMethods() const;
+ bool keepConnection() const;
+ bool setKeepConnection(bool isPersistent);
+ bool disconnect();
virtual bool hasNdefMessage();
virtual RequestId readNdefMessages();
+ int maxCommandLength() const;
virtual RequestId sendCommand(const QByteArray &command);
virtual RequestId sendCommands(const QList<QByteArray> &commands);
virtual RequestId writeNdefMessages(const QList<QNdefMessage> &messages);
@@ -98,6 +102,8 @@ protected:
void setupTargetCheckTimer();
void handleTargetLost();
QAndroidJniObject getTagTechnology(const QString &tech) const;
+ bool setTagTechnology(const QStringList &techList);
+ bool connect();
QByteArray jbyteArrayToQByteArray(const jbyteArray &byteArray) const;
bool catchJavaExceptions(bool verbose = true) const;
@@ -107,6 +113,9 @@ protected:
QStringList m_techList;
Type m_type;
QTimer *m_targetCheckTimer;
+ QString m_tech;
+ QAndroidJniObject m_tagTech;
+ bool m_keepConnection;
};
QT_END_NAMESPACE
diff --git a/src/nfc/qnearfieldtarget_emulator.cpp b/src/nfc/qnearfieldtarget_emulator.cpp
index 96462b0d..29b1f74d 100644
--- a/src/nfc/qnearfieldtarget_emulator.cpp
+++ b/src/nfc/qnearfieldtarget_emulator.cpp
@@ -40,11 +40,12 @@
#include "qnearfieldtarget_emulator_p.h"
#include "qnearfieldtarget_p.h"
-#include <QtCore/QDirIterator>
-#include <QtCore/QSettings>
-#include <QtCore/QMutex>
+#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
+#include <QtCore/QDirIterator>
+#include <QtCore/QMutex>
+#include <QtCore/QSettings>
QT_BEGIN_NAMESPACE
@@ -87,7 +88,7 @@ QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command)
return id;
}
- quint16 crc = qNfcChecksum(command.constData(), command.length());
+ quint16 crc = qChecksum(command.constData(), command.length(), Qt::ChecksumItuV41);
QByteArray response = m_tag->processCommand(command + char(crc & 0xff) + char(crc >> 8));
@@ -99,7 +100,7 @@ QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command)
}
// check crc
- if (qNfcChecksum(response.constData(), response.length()) != 0) {
+ if (qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41) != 0) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNearFieldTarget::Error, ChecksumMismatchError),
Q_ARG(QNearFieldTarget::RequestId, id));
@@ -157,7 +158,7 @@ QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command)
return id;
}
- quint16 crc = qNfcChecksum(command.constData(), command.length());
+ quint16 crc = qChecksum(command.constData(), command.length(), Qt::ChecksumItuV41);
QByteArray response = m_tag->processCommand(command + char(crc & 0xff) + char(crc >> 8));
@@ -166,7 +167,7 @@ QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command)
if (response.length() > 1) {
// check crc
- if (qNfcChecksum(response.constData(), response.length()) != 0) {
+ if (qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41) != 0) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNearFieldTarget::Error, ChecksumMismatchError),
Q_ARG(QNearFieldTarget::RequestId, id));
diff --git a/src/nfc/qnearfieldtarget_neard_p.cpp b/src/nfc/qnearfieldtarget_neard_p.cpp
new file mode 100644
index 00000000..4ed17a15
--- /dev/null
+++ b/src/nfc/qnearfieldtarget_neard_p.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Governikus GmbH & Co. K
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+
+#include "qnearfieldtarget.h"
+#include "qnearfieldtarget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+bool QNearFieldTargetPrivate::keepConnection() const
+{
+ return false;
+}
+
+bool QNearFieldTargetPrivate::setKeepConnection(bool isPersistent)
+{
+ Q_UNUSED(isPersistent);
+ return false;
+}
+
+bool QNearFieldTargetPrivate::disconnect()
+{
+ return false;
+}
+
+int QNearFieldTargetPrivate::maxCommandLength() const
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/nfc/qnearfieldtarget_p.cpp b/src/nfc/qnearfieldtarget_p.cpp
new file mode 100644
index 00000000..4ed17a15
--- /dev/null
+++ b/src/nfc/qnearfieldtarget_p.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Governikus GmbH & Co. K
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+
+#include "qnearfieldtarget.h"
+#include "qnearfieldtarget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+bool QNearFieldTargetPrivate::keepConnection() const
+{
+ return false;
+}
+
+bool QNearFieldTargetPrivate::setKeepConnection(bool isPersistent)
+{
+ Q_UNUSED(isPersistent);
+ return false;
+}
+
+bool QNearFieldTargetPrivate::disconnect()
+{
+ return false;
+}
+
+int QNearFieldTargetPrivate::maxCommandLength() const
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/nfc/qnearfieldtarget_p.h b/src/nfc/qnearfieldtarget_p.h
index 7a787ace..9cef2f55 100644
--- a/src/nfc/qnearfieldtarget_p.h
+++ b/src/nfc/qnearfieldtarget_p.h
@@ -57,6 +57,9 @@
#include <QtCore/QMap>
#include <QtCore/QSharedData>
+#include <QtCore/QVariant>
+
+#define NEARFIELDTARGET_Q() NearFieldTarget * const q = reinterpret_cast<NearFieldTarget *>(q_ptr)
QT_BEGIN_NAMESPACE
@@ -66,8 +69,18 @@ class QNearFieldTarget::RequestIdPrivate : public QSharedData
class QNearFieldTargetPrivate
{
+ QNearFieldTarget *q_ptr;
+ Q_DECLARE_PUBLIC(QNearFieldTarget)
+
public:
+ QNearFieldTargetPrivate(QNearFieldTarget *q) : q_ptr(q) {}
+
QMap<QNearFieldTarget::RequestId, QVariant> m_decodedResponses;
+
+ bool keepConnection() const;
+ bool setKeepConnection(bool isPersistent);
+ bool disconnect();
+ int maxCommandLength() const;
};
QT_END_NAMESPACE
diff --git a/src/nfc/targetemulator.cpp b/src/nfc/targetemulator.cpp
index ffc2c1b5..80555593 100644
--- a/src/nfc/targetemulator.cpp
+++ b/src/nfc/targetemulator.cpp
@@ -44,9 +44,6 @@
#include <QtCore/QDebug>
-// Implementation of qNfcChecksum
-#include "qnearfieldtarget.h"
-
QT_BEGIN_NAMESPACE
TagBase::TagBase()
@@ -157,7 +154,7 @@ QByteArray NfcTagType1::processCommand(const QByteArray &command)
QByteArray uid = command.mid(3, 4);
// check checksum
- if (qNfcChecksum(command.constData(), command.length()) != 0)
+ if (qChecksum(command.constData(), command.length(), Qt::ChecksumItuV41) != 0)
return QByteArray();
// check UID
@@ -226,7 +223,7 @@ QByteArray NfcTagType1::processCommand(const QByteArray &command)
QByteArray uid = command.mid(10, 4);
// check checksum
- if (qNfcChecksum(command.constData(), command.length()) != 0)
+ if (qChecksum(command.constData(), command.length(), Qt::ChecksumItuV41) != 0)
return QByteArray();
// check UID
@@ -280,7 +277,7 @@ QByteArray NfcTagType1::processCommand(const QByteArray &command)
}
if (!response.isEmpty()) {
- quint16 crc = qNfcChecksum(response.constData(), response.length());
+ quint16 crc = qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41);
response.append(quint8(crc & 0xff));
response.append(quint8(crc >> 8));
}
@@ -324,7 +321,7 @@ QByteArray NfcTagType2::processCommand(const QByteArray &command)
QByteArray response;
// check checksum
- if (qNfcChecksum(command.constData(), command.length()) != 0)
+ if (qChecksum(command.constData(), command.length(), Qt::ChecksumItuV41) != 0)
return QByteArray();
if (expectPacket2) {
@@ -381,7 +378,7 @@ QByteArray NfcTagType2::processCommand(const QByteArray &command)
}
if (!response.isEmpty()) {
- quint16 crc = qNfcChecksum(response.constData(), response.length());
+ quint16 crc = qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41);
response.append(quint8(crc & 0xff));
response.append(quint8(crc >> 8));
}
diff --git a/src/src.pro b/src/src.pro
index 38be9a4f..dba9de4b 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -22,8 +22,8 @@ qtHaveModule(quick) {
}
include($$OUT_PWD/bluetooth/qtbluetooth-config.pri)
-QT_FOR_CONFIG += bluetooth-private
-qtConfig(bluez):qtHaveModule(dbus) {
+QT_FOR_CONFIG += bluetooth
+qtConfig(bluez) {
sdpscanner.subdir = tools/sdpscanner
sdpscanner.depends = bluetooth
SUBDIRS += sdpscanner
diff --git a/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro b/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro
index 96880930..900bb5e9 100644
--- a/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro
+++ b/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro
@@ -4,7 +4,3 @@ CONFIG += testcase
QT = core concurrent bluetooth-private testlib
osx:QT += widgets
-
-qtConfig(bluez):qtHaveModule(dbus) {
- DEFINES += QT_BLUEZ_BLUETOOTH
-}
diff --git a/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp b/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp
index efc4d8a6..c75004d6 100644
--- a/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp
+++ b/tests/auto/qbluetoothdevicediscoveryagent/tst_qbluetoothdevicediscoveryagent.cpp
@@ -96,7 +96,7 @@ tst_QBluetoothDeviceDiscoveryAgent::~tst_QBluetoothDeviceDiscoveryAgent()
{
}
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
// This section was adopted from tst_qloggingcategory.cpp
QString logMessage;
@@ -133,7 +133,7 @@ void tst_QBluetoothDeviceDiscoveryAgent::initTestCase()
qRegisterMetaType<QBluetoothDeviceInfo>();
qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::InquiryType>();
-#ifdef QT_BLUEZ_BLUETOOTH
+#if QT_CONFIG(bluez)
// To distinguish Bluez 4 and 5 we peek into the debug output
// of first Bluetooth ctor. It executes a runtime test and prints the result
// as logging output. This avoids more complex runtime detection logic within this unit test.
@@ -495,7 +495,7 @@ void tst_QBluetoothDeviceDiscoveryAgent::tst_discoveryTimeout()
QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000);
agent.setLowEnergyDiscoveryTimeout(20000);
QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000);
-#elif defined(QT_BLUEZ_BLUETOOTH)
+#elif QT_CONFIG(bluez)
if (isBluez5Runtime) {
QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000);
agent.setLowEnergyDiscoveryTimeout(-1); // negative ignored
diff --git a/tests/auto/qbluetoothsocket/qbluetoothsocket.pro b/tests/auto/qbluetoothsocket/qbluetoothsocket.pro
index 8c9b2acb..32fc6558 100644
--- a/tests/auto/qbluetoothsocket/qbluetoothsocket.pro
+++ b/tests/auto/qbluetoothsocket/qbluetoothsocket.pro
@@ -13,6 +13,4 @@ osx {
DEFINES += QT_OSX_BLUETOOTH
} else: android {
DEFINES += QT_ANDROID_BLUETOOTH
-} else: qtConfig(bluez):qtHaveModule(dbus) {
- DEFINES += QT_BLUEZ_BLUETOOTH
}
diff --git a/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp b/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp
index 566a5c5b..fa4629ac 100644
--- a/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp
+++ b/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp
@@ -505,7 +505,7 @@ void tst_QBluetoothSocket::tst_preferredSecurityFlags()
//test default values
#if defined(QT_ANDROID_BLUETOOTH) | defined(QT_OSX_BLUETOOTH)
QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::Secure);
-#elif defined(QT_BLUEZ_BLUETOOTH)
+#elif QT_CONFIG(bluez)
QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::Authorization);
#else
QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::NoSecurity);