1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 BlackBerry Limited. All rights reserved. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtBluetooth module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "qdeclarativebluetoothdiscoverymodel_p.h" |
42 | |
43 | #include <QPixmap> |
44 | |
45 | #include <QtCore/QLoggingCategory> |
46 | #include <QtBluetooth/QBluetoothDeviceInfo> |
47 | #include <QtBluetooth/QBluetoothAddress> |
48 | |
49 | #include "qdeclarativebluetoothservice_p.h" |
50 | |
51 | /*! |
52 | \qmltype BluetoothDiscoveryModel |
53 | \instantiates QDeclarativeBluetoothDiscoveryModel |
54 | \inqmlmodule QtBluetooth |
55 | \since 5.2 |
56 | \brief Enables searching for the Bluetooth devices and services in |
57 | range. |
58 | |
59 | BluetoothDiscoveryModel provides a model of connectable services. The |
60 | contents of the model can be filtered by UUID allowing discovery to be |
61 | limited to a single service such as a game. |
62 | |
63 | The model roles provided by BluetoothDiscoveryModel are |
64 | \c service, \c name, \c remoteAddress and \c deviceName. The meaning of the roles |
65 | changes based on the current \l discoveryMode. |
66 | |
67 | \table |
68 | \header |
69 | \li Model role |
70 | \li Device Discovery |
71 | \li Service Discovery |
72 | \row |
73 | \li \c name |
74 | \li The device's name and address. |
75 | \li The service name and the name of the device offering the service. If the device name is empty the devices address will be used. |
76 | \row |
77 | \li \c deviceName |
78 | \li The name of the device. |
79 | \li The name of the device offering the service. |
80 | \row |
81 | \li \c service |
82 | \li The role is undefined in this mode. |
83 | \li The \l BluetoothService object describing the discovered service. |
84 | \row |
85 | \li \c remoteAddress |
86 | \li The address of the found device. |
87 | \li The address of the device offering the service. |
88 | \endtable |
89 | |
90 | \sa QBluetoothServiceDiscoveryAgent |
91 | */ |
92 | |
93 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_QML) |
94 | |
95 | class QDeclarativeBluetoothDiscoveryModelPrivate |
96 | { |
97 | public: |
98 | QDeclarativeBluetoothDiscoveryModelPrivate() |
99 | : |
100 | m_error(QDeclarativeBluetoothDiscoveryModel::NoError), |
101 | m_discoveryMode(QDeclarativeBluetoothDiscoveryModel::MinimalServiceDiscovery), |
102 | m_running(false), |
103 | m_runningRequested(true), |
104 | m_componentCompleted(false), |
105 | m_currentState(QDeclarativeBluetoothDiscoveryModel::IdleAction), |
106 | m_nextState(QDeclarativeBluetoothDiscoveryModel::IdleAction), |
107 | m_wasDirectDeviceAgentCancel(false) |
108 | { |
109 | } |
110 | ~QDeclarativeBluetoothDiscoveryModelPrivate() |
111 | { |
112 | if (m_deviceAgent) |
113 | delete m_deviceAgent; |
114 | |
115 | if (m_serviceAgent) |
116 | delete m_serviceAgent; |
117 | |
118 | qDeleteAll(c: m_services); |
119 | } |
120 | |
121 | QBluetoothServiceDiscoveryAgent *m_serviceAgent = nullptr; |
122 | QBluetoothDeviceDiscoveryAgent *m_deviceAgent = nullptr; |
123 | |
124 | QDeclarativeBluetoothDiscoveryModel::Error m_error; |
125 | QList<QDeclarativeBluetoothService *> m_services; |
126 | QList<QBluetoothDeviceInfo> m_devices; |
127 | QDeclarativeBluetoothDiscoveryModel::DiscoveryMode m_discoveryMode; |
128 | QString m_uuid; |
129 | bool m_running; |
130 | bool m_runningRequested; |
131 | bool m_componentCompleted; |
132 | QString m_remoteAddress; |
133 | |
134 | QDeclarativeBluetoothDiscoveryModel::Action m_currentState; |
135 | QDeclarativeBluetoothDiscoveryModel::Action m_nextState; |
136 | bool m_wasDirectDeviceAgentCancel; |
137 | }; |
138 | |
139 | QDeclarativeBluetoothDiscoveryModel::QDeclarativeBluetoothDiscoveryModel(QObject *parent) : |
140 | QAbstractListModel(parent), |
141 | d(new QDeclarativeBluetoothDiscoveryModelPrivate) |
142 | { |
143 | d->m_deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); |
144 | connect(sender: d->m_deviceAgent, signal: &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, |
145 | receiver: this, slot: QOverload<const QBluetoothDeviceInfo&>::of(ptr: &QDeclarativeBluetoothDiscoveryModel::deviceDiscovered)); |
146 | connect(sender: d->m_deviceAgent, signal: &QBluetoothDeviceDiscoveryAgent::finished, |
147 | receiver: this, slot: &QDeclarativeBluetoothDiscoveryModel::finishedDiscovery); |
148 | connect(sender: d->m_deviceAgent, signal: &QBluetoothDeviceDiscoveryAgent::canceled, |
149 | receiver: this, slot: &QDeclarativeBluetoothDiscoveryModel::finishedDiscovery); |
150 | connect(sender: d->m_deviceAgent, |
151 | signal: QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(ptr: &QBluetoothDeviceDiscoveryAgent::error), |
152 | receiver: this, |
153 | slot: &QDeclarativeBluetoothDiscoveryModel::errorDeviceDiscovery); |
154 | d->m_deviceAgent->setObjectName(QStringLiteral("DeviceDiscoveryAgent" )); |
155 | |
156 | d->m_serviceAgent = new QBluetoothServiceDiscoveryAgent(this); |
157 | connect(sender: d->m_serviceAgent, signal: &QBluetoothServiceDiscoveryAgent::serviceDiscovered, |
158 | receiver: this, slot: QOverload<const QBluetoothServiceInfo&>::of(ptr: &QDeclarativeBluetoothDiscoveryModel::serviceDiscovered)); |
159 | connect(sender: d->m_serviceAgent, signal: &QBluetoothServiceDiscoveryAgent::finished, |
160 | receiver: this, slot: &QDeclarativeBluetoothDiscoveryModel::finishedDiscovery); |
161 | connect(sender: d->m_serviceAgent, signal: &QBluetoothServiceDiscoveryAgent::canceled, |
162 | receiver: this, slot: &QDeclarativeBluetoothDiscoveryModel::finishedDiscovery); |
163 | connect(sender: d->m_serviceAgent, |
164 | signal: QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(ptr: &QBluetoothServiceDiscoveryAgent::error), |
165 | receiver: this, |
166 | slot: &QDeclarativeBluetoothDiscoveryModel::errorDiscovery); |
167 | d->m_serviceAgent->setObjectName(QStringLiteral("ServiceDiscoveryAgent" )); |
168 | } |
169 | |
170 | QDeclarativeBluetoothDiscoveryModel::~QDeclarativeBluetoothDiscoveryModel() |
171 | { |
172 | delete d; |
173 | } |
174 | void QDeclarativeBluetoothDiscoveryModel::componentComplete() |
175 | { |
176 | d->m_componentCompleted = true; |
177 | if (d->m_runningRequested) |
178 | setRunning(true); |
179 | } |
180 | |
181 | void QDeclarativeBluetoothDiscoveryModel::errorDiscovery(QBluetoothServiceDiscoveryAgent::Error error) |
182 | { |
183 | switch (error) { |
184 | case QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError: |
185 | d->m_error = QDeclarativeBluetoothDiscoveryModel::InvalidBluetoothAdapterError; break; |
186 | case QBluetoothServiceDiscoveryAgent::NoError: |
187 | d->m_error = QDeclarativeBluetoothDiscoveryModel::NoError; break; |
188 | case QBluetoothServiceDiscoveryAgent::InputOutputError: |
189 | d->m_error = QDeclarativeBluetoothDiscoveryModel::InputOutputError; break; |
190 | case QBluetoothServiceDiscoveryAgent::PoweredOffError: |
191 | d->m_error = QDeclarativeBluetoothDiscoveryModel::PoweredOffError; break; |
192 | case QBluetoothServiceDiscoveryAgent::UnknownError: |
193 | d->m_error = QDeclarativeBluetoothDiscoveryModel::UnknownError; break; |
194 | } |
195 | |
196 | emit errorChanged(); |
197 | } |
198 | |
199 | void QDeclarativeBluetoothDiscoveryModel::errorDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::Error error) |
200 | { |
201 | d->m_error = static_cast<QDeclarativeBluetoothDiscoveryModel::Error>(error); |
202 | emit errorChanged(); |
203 | |
204 | //QBluetoothDeviceDiscoveryAgent::finished() signal is not emitted in case of an error |
205 | //Note that this behavior is different from QBluetoothServiceDiscoveryAgent. |
206 | //This resets the models running flag. |
207 | finishedDiscovery(); |
208 | } |
209 | |
210 | void QDeclarativeBluetoothDiscoveryModel::clearModel() |
211 | { |
212 | beginResetModel(); |
213 | qDeleteAll(c: d->m_services); |
214 | d->m_services.clear(); |
215 | d->m_devices.clear(); |
216 | endResetModel(); |
217 | } |
218 | |
219 | /*! |
220 | \qmlproperty enumeration BluetoothDiscoveryModel::error |
221 | |
222 | This property holds the last error reported during discovery. |
223 | \table |
224 | \header \li Property \li Description |
225 | \row \li \c BluetoothDiscoveryModel.NoError |
226 | \li No error occurred. |
227 | \row \li \c BluetoothDiscoveryModel.InputOutputError |
228 | \li An IO failure occurred during device discovery |
229 | \row \li \c BluetoothDiscoveryModel.PoweredOffError |
230 | \li The bluetooth device is not powered on. |
231 | \row \li \c BluetoothDiscoveryModel.InvalidBluetoothAdapterError |
232 | \li There is no default Bluetooth device to perform the |
233 | service discovery. The model always uses the local default adapter. |
234 | Specifying a default adapter is not possible. If that's required, |
235 | \l QBluetoothServiceDiscoveryAgent should be directly used. This |
236 | value was introduced by Qt 5.4. |
237 | \row \li \c BluetoothDiscoveryModel.UnknownError |
238 | \li An unknown error occurred. |
239 | \endtable |
240 | |
241 | This property is read-only. |
242 | */ |
243 | QDeclarativeBluetoothDiscoveryModel::Error QDeclarativeBluetoothDiscoveryModel::error() const |
244 | { |
245 | return d->m_error; |
246 | } |
247 | |
248 | int QDeclarativeBluetoothDiscoveryModel::rowCount(const QModelIndex &parent) const |
249 | { |
250 | Q_UNUSED(parent); |
251 | if (discoveryMode() == DeviceDiscovery) |
252 | return d->m_devices.count(); |
253 | else |
254 | return d->m_services.count(); |
255 | } |
256 | |
257 | QVariant QDeclarativeBluetoothDiscoveryModel::data(const QModelIndex &index, int role) const |
258 | { |
259 | if (!index.isValid() || index.row() < 0) |
260 | return QVariant(); |
261 | |
262 | if (discoveryMode() != DeviceDiscovery) { |
263 | if (index.row() >= d->m_services.count()){ |
264 | qCWarning(QT_BT_QML) << "index out of bounds" ; |
265 | return QVariant(); |
266 | } |
267 | |
268 | QDeclarativeBluetoothService *service = d->m_services.value(i: index.row()); |
269 | |
270 | switch (role) { |
271 | case Name: { |
272 | QString label = service->deviceName(); |
273 | if (label.isEmpty()) |
274 | label += service->deviceAddress(); |
275 | else |
276 | label+= QStringLiteral(":" ); |
277 | label += QStringLiteral(" " ) + service->serviceName(); |
278 | return label; |
279 | } |
280 | case ServiceRole: |
281 | return QVariant::fromValue(value: service); |
282 | case DeviceName: |
283 | return service->deviceName(); |
284 | case RemoteAddress: |
285 | return service->deviceAddress(); |
286 | } |
287 | } else { |
288 | if (index.row() >= d->m_devices.count()) { |
289 | qCWarning(QT_BT_QML) << "index out of bounds" ; |
290 | return QVariant(); |
291 | } |
292 | |
293 | QBluetoothDeviceInfo device = d->m_devices.value(i: index.row()); |
294 | switch (role) { |
295 | case Name: |
296 | return (device.name() + QStringLiteral(" (" ) + device.address().toString() + QStringLiteral(")" )); |
297 | case ServiceRole: |
298 | break; |
299 | case DeviceName: |
300 | return device.name(); |
301 | case RemoteAddress: |
302 | return device.address().toString(); |
303 | } |
304 | } |
305 | |
306 | return QVariant(); |
307 | } |
308 | |
309 | QHash<int,QByteArray> QDeclarativeBluetoothDiscoveryModel::roleNames() const |
310 | { |
311 | return {{Name, "name" }, |
312 | {ServiceRole, "service" }, |
313 | {RemoteAddress, "remoteAddress" }, |
314 | {DeviceName, "deviceName" }}; |
315 | } |
316 | |
317 | /*! |
318 | \qmlsignal BluetoothDiscoveryModel::serviceDiscovered(BluetoothService service) |
319 | |
320 | This signal is emitted when a new service is discovered. The \a service |
321 | parameter contains the service details. |
322 | |
323 | The corresponding handler is \c onServiceDiscovered. |
324 | |
325 | \sa BluetoothService |
326 | */ |
327 | |
328 | void QDeclarativeBluetoothDiscoveryModel::serviceDiscovered(const QBluetoothServiceInfo &service) |
329 | { |
330 | //qDebug() << "service discovered"; |
331 | |
332 | QDeclarativeBluetoothService *bs = new QDeclarativeBluetoothService(service, this); |
333 | QDeclarativeBluetoothService *current = nullptr; |
334 | for (int i = 0; i < d->m_services.count(); i++) { |
335 | current = d->m_services.at(i); |
336 | if (bs->deviceAddress() == current->deviceAddress() |
337 | && bs->serviceName() == current->serviceName() |
338 | && bs->serviceUuid() == current->serviceUuid()) { |
339 | delete bs; |
340 | return; |
341 | } |
342 | } |
343 | |
344 | beginInsertRows(parent: QModelIndex(),first: d->m_services.count(), last: d->m_services.count()); |
345 | d->m_services.append(t: bs); |
346 | endInsertRows(); |
347 | emit serviceDiscovered(service: bs); |
348 | } |
349 | |
350 | /*! |
351 | \qmlsignal BluetoothDiscoveryModel::deviceDiscovered(string device) |
352 | |
353 | This signal is emitted when a new device is discovered. \a device contains |
354 | the Bluetooth address of the discovered device. |
355 | |
356 | The corresponding handler is \c onDeviceDiscovered. |
357 | */ |
358 | |
359 | void QDeclarativeBluetoothDiscoveryModel::deviceDiscovered(const QBluetoothDeviceInfo &device) |
360 | { |
361 | //qDebug() << "Device discovered" << device.address().toString() << device.name(); |
362 | |
363 | beginInsertRows(parent: QModelIndex(),first: d->m_devices.count(), last: d->m_devices.count()); |
364 | d->m_devices.append(t: device); |
365 | endInsertRows(); |
366 | emit deviceDiscovered(device: device.address().toString()); |
367 | } |
368 | |
369 | void QDeclarativeBluetoothDiscoveryModel::finishedDiscovery() |
370 | { |
371 | QDeclarativeBluetoothDiscoveryModel::Action previous = d->m_currentState; |
372 | d->m_currentState = IdleAction; |
373 | |
374 | switch (previous) { |
375 | case IdleAction: |
376 | // last transition didn't even start |
377 | // can happen when start() or stop() immediately returned |
378 | // usually this happens within a current transitionToNextAction call |
379 | break; |
380 | case StopAction: |
381 | qCDebug(QT_BT_QML) << "Agent cancel detected" ; |
382 | transitionToNextAction(); |
383 | break; |
384 | default: // all other |
385 | qCDebug(QT_BT_QML) << "Discovery finished" << sender()->objectName(); |
386 | |
387 | //TODO Qt6 This hack below is once again due to the pendingCancel logic |
388 | // because QBluetoothDeviceDiscoveryAgent::isActive() is not reliable. |
389 | // In toggleStartStop() we need to know whether the stop() is delayed or immediate. |
390 | // isActive() cannot be used. Hence we have to wait for the canceled() signal. |
391 | // Android, WinRT and Bluez5 are immediate, Bluez4 is always delayed. |
392 | // The immediate case is what we catch here. |
393 | if (sender() == d->m_deviceAgent && d->m_nextState == StopAction) { |
394 | d->m_wasDirectDeviceAgentCancel = true; |
395 | return; |
396 | } |
397 | setRunning(false); |
398 | break; |
399 | } |
400 | } |
401 | |
402 | /*! |
403 | \qmlproperty enumeration BluetoothDiscoveryModel::discoveryMode |
404 | |
405 | Sets the discovery mode. The discovery mode has to be set before the discovery is started |
406 | \table |
407 | \header \li Property \li Description |
408 | \row \li \c BluetoothDiscoveryModel.FullServiceDiscovery |
409 | \li Starts a full discovery of all services of all devices in range. |
410 | \row \li \c BluetoothDiscoveryModel.MinimalServiceDiscovery |
411 | \li (Default) Starts a minimal discovery of all services of all devices in range. A minimal discovery is |
412 | faster but only guarantees the device and UUID information to be correct. |
413 | \row \li \c BluetoothDiscoveryModel.DeviceDiscovery |
414 | \li Discovers only devices in range. The service role will be 0 for any model item. |
415 | \endtable |
416 | */ |
417 | |
418 | QDeclarativeBluetoothDiscoveryModel::DiscoveryMode QDeclarativeBluetoothDiscoveryModel::discoveryMode() const |
419 | { |
420 | return d->m_discoveryMode; |
421 | } |
422 | |
423 | void QDeclarativeBluetoothDiscoveryModel::setDiscoveryMode(DiscoveryMode discovery) |
424 | { |
425 | d->m_discoveryMode = discovery; |
426 | emit discoveryModeChanged(); |
427 | } |
428 | |
429 | |
430 | void QDeclarativeBluetoothDiscoveryModel::updateNextAction(Action action) |
431 | { |
432 | qCDebug(QT_BT_QML) << "New action queue:" |
433 | << d->m_currentState << d->m_nextState << action; |
434 | |
435 | if (action == IdleAction) |
436 | return; |
437 | |
438 | switch (d->m_nextState) { |
439 | case IdleAction: |
440 | d->m_nextState = action; |
441 | return; |
442 | case StopAction: |
443 | qWarning() << "Invalid Stop state when processing new action" << action; |
444 | return; |
445 | case DeviceDiscoveryAction: |
446 | case MinimalServiceDiscoveryAction: |
447 | case FullServiceDiscoveryAction: |
448 | if (action == StopAction) // cancel out previous start call |
449 | d->m_nextState = IdleAction; |
450 | else |
451 | qWarning() << "Ignoring new DMF state while another DMF state is scheduled." ; |
452 | return; |
453 | } |
454 | } |
455 | |
456 | void QDeclarativeBluetoothDiscoveryModel::transitionToNextAction() |
457 | { |
458 | qCDebug(QT_BT_QML) << "Before transition change:" << d->m_currentState << d->m_nextState; |
459 | bool isRunning; |
460 | switch (d->m_currentState) { |
461 | case IdleAction: |
462 | switch (d->m_nextState) { |
463 | case IdleAction: break; // nothing to do |
464 | case StopAction: d->m_nextState = IdleAction; break; // clear, nothing to do |
465 | case DeviceDiscoveryAction: |
466 | case MinimalServiceDiscoveryAction: |
467 | case FullServiceDiscoveryAction: |
468 | Action temp = d->m_nextState; |
469 | clearModel(); |
470 | isRunning = toggleStartStop(action: d->m_nextState); |
471 | d->m_nextState = IdleAction; |
472 | if (isRunning) { |
473 | d->m_currentState = temp; |
474 | } else { |
475 | if (temp != DeviceDiscoveryAction ) |
476 | errorDiscovery(error: d->m_serviceAgent->error()); |
477 | d->m_running = false; |
478 | } |
479 | } |
480 | break; |
481 | case StopAction: |
482 | break; // do nothing, StopAction cleared by finished()/cancelled()/error() handlers |
483 | case DeviceDiscoveryAction: |
484 | case MinimalServiceDiscoveryAction: |
485 | case FullServiceDiscoveryAction: |
486 | switch (d->m_nextState) { |
487 | case IdleAction: break; |
488 | case StopAction: |
489 | isRunning = toggleStartStop(action: StopAction); |
490 | (isRunning) ? d->m_currentState = StopAction : d->m_currentState = IdleAction; |
491 | d->m_nextState = IdleAction; |
492 | break; |
493 | default: |
494 | Q_ASSERT(false); // should never happen |
495 | break; |
496 | } |
497 | |
498 | break; |
499 | } |
500 | |
501 | qCDebug(QT_BT_QML) << "After transition change:" << d->m_currentState << d->m_nextState; |
502 | } |
503 | |
504 | // Returns true if the agent is active |
505 | // this can be used to detect whether the agent still needs time to |
506 | // perform the requested action. |
507 | bool QDeclarativeBluetoothDiscoveryModel::toggleStartStop(Action action) |
508 | { |
509 | Q_ASSERT(action != IdleAction); |
510 | switch (action) { |
511 | case DeviceDiscoveryAction: |
512 | Q_ASSERT(!d->m_deviceAgent->isActive() && !d->m_serviceAgent->isActive()); |
513 | d->m_deviceAgent->start(); |
514 | return d->m_deviceAgent->isActive(); |
515 | case MinimalServiceDiscoveryAction: |
516 | case FullServiceDiscoveryAction: |
517 | Q_ASSERT(!d->m_deviceAgent->isActive() && !d->m_serviceAgent->isActive()); |
518 | d->m_serviceAgent->setRemoteAddress(QBluetoothAddress(d->m_remoteAddress)); |
519 | d->m_serviceAgent->clear(); |
520 | |
521 | if (!d->m_uuid.isEmpty()) |
522 | d->m_serviceAgent->setUuidFilter(QBluetoothUuid(d->m_uuid)); |
523 | |
524 | if (action == FullServiceDiscoveryAction) { |
525 | qCDebug(QT_BT_QML) << "Full Discovery" ; |
526 | d->m_serviceAgent->start(mode: QBluetoothServiceDiscoveryAgent::FullDiscovery); |
527 | } else { |
528 | qCDebug(QT_BT_QML) << "Minimal Discovery" ; |
529 | d->m_serviceAgent->start(mode: QBluetoothServiceDiscoveryAgent::MinimalDiscovery); |
530 | } |
531 | return d->m_serviceAgent->isActive(); |
532 | case StopAction: |
533 | Q_ASSERT(d->m_currentState != StopAction && d->m_currentState != IdleAction); |
534 | if (d->m_currentState == DeviceDiscoveryAction) { |
535 | d->m_deviceAgent->stop(); |
536 | |
537 | // TODO Qt6 Crude hack below |
538 | // cannot use isActive() below due to pendingCancel logic |
539 | // we always wait for canceled() signal coming through or check |
540 | // for directly invoked cancel() response caused by stop() above |
541 | bool stillActive = !d->m_wasDirectDeviceAgentCancel; |
542 | d->m_wasDirectDeviceAgentCancel = false; |
543 | return stillActive; |
544 | } else { |
545 | d->m_serviceAgent->stop(); |
546 | return d->m_serviceAgent->isActive(); |
547 | } |
548 | default: |
549 | return true; |
550 | } |
551 | } |
552 | |
553 | |
554 | /*! |
555 | \qmlproperty bool BluetoothDiscoveryModel::running |
556 | |
557 | This property starts or stops discovery. A restart of the discovery process |
558 | requires setting this property to \c false and subsequently to \c true again. |
559 | |
560 | */ |
561 | |
562 | bool QDeclarativeBluetoothDiscoveryModel::running() const |
563 | { |
564 | return d->m_running; |
565 | } |
566 | |
567 | void QDeclarativeBluetoothDiscoveryModel::setRunning(bool running) |
568 | { |
569 | if (!d->m_componentCompleted) { |
570 | d->m_runningRequested = running; |
571 | return; |
572 | } |
573 | |
574 | if (d->m_running == running) |
575 | return; |
576 | |
577 | d->m_running = running; |
578 | |
579 | Action nextAction = IdleAction; |
580 | if (running) { |
581 | if (discoveryMode() == MinimalServiceDiscovery) |
582 | nextAction = MinimalServiceDiscoveryAction; |
583 | else if (discoveryMode() == FullServiceDiscovery) |
584 | nextAction = FullServiceDiscoveryAction; |
585 | else |
586 | nextAction = DeviceDiscoveryAction; |
587 | } else { |
588 | nextAction = StopAction; |
589 | } |
590 | |
591 | Q_ASSERT(nextAction != IdleAction); |
592 | updateNextAction(action: nextAction); |
593 | transitionToNextAction(); |
594 | |
595 | qCDebug(QT_BT_QML) << "Running state:" << d->m_running; |
596 | emit runningChanged(); |
597 | } |
598 | |
599 | /*! |
600 | \qmlproperty string BluetoothDiscoveryModel::uuidFilter |
601 | |
602 | This property holds an optional UUID filter. A UUID can be used to return only |
603 | matching services. 16 bit, 32 bit or 128 bit UUIDs can be used. The string format |
604 | is same as the format of QUuid. |
605 | |
606 | \sa QBluetoothUuid |
607 | \sa QUuid |
608 | */ |
609 | |
610 | |
611 | QString QDeclarativeBluetoothDiscoveryModel::uuidFilter() const |
612 | { |
613 | return d->m_uuid; |
614 | } |
615 | |
616 | void QDeclarativeBluetoothDiscoveryModel::setUuidFilter(QString uuid) |
617 | { |
618 | if (uuid == d->m_uuid) |
619 | return; |
620 | |
621 | QBluetoothUuid qbuuid(uuid); |
622 | if (qbuuid.isNull()) { |
623 | qCWarning(QT_BT_QML) << "Invalid UUID providded " << uuid; |
624 | return; |
625 | } |
626 | d->m_uuid = uuid; |
627 | emit uuidFilterChanged(); |
628 | } |
629 | |
630 | /*! |
631 | \qmlproperty string BluetoothDiscoveryModel::remoteAddress |
632 | |
633 | This property holds an optional bluetooth address for a remote bluetooth device. |
634 | Only services on this remote device will be discovered. It has no effect if |
635 | an invalid bluetooth address was set or if the property was set after the discovery |
636 | was started. |
637 | |
638 | The property is ignored if device discovery is selected. |
639 | |
640 | */ |
641 | |
642 | QString QDeclarativeBluetoothDiscoveryModel::remoteAddress() |
643 | { |
644 | return d->m_remoteAddress; |
645 | } |
646 | |
647 | void QDeclarativeBluetoothDiscoveryModel::setRemoteAddress(QString address) |
648 | { |
649 | d->m_remoteAddress = address; |
650 | emit remoteAddressChanged(); |
651 | } |
652 | |