Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeoareamonitor_polling.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtPositioning/qgeocoordinate.h>
6#include <QtPositioning/qgeorectangle.h>
7#include <QtPositioning/qgeocircle.h>
8
9#include <QtCore/qhash.h>
10#include <QtCore/qmetaobject.h>
11#include <QtCore/qtimer.h>
12#include <QtCore/qdebug.h>
13#include <QtCore/qmutex.h>
14#include <QtCore/qset.h>
15
16#include <mutex>
17
18#define UPDATE_INTERVAL_5S 5000
19
20QT_BEGIN_NAMESPACE
21
22typedef QHash<QString, QGeoAreaMonitorInfo> MonitorTable;
23
25{
26 static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::areaEntered);
27 return signal;
28}
29
31{
32 static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::areaExited);
33 return signal;
34}
35
37{
38 static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::monitorExpired);
39 return signal;
40}
41
43{
45public:
53
64
75
87
103
112
138
144
146 {
148
149 return activeMonitorAreas;
150 }
151
153 {
155
156 bool signalsConnected = false;
158 if (client->hasConnections()) {
159 signalsConnected = true;
160 break;
161 }
162 }
163
165 if (source)
167 else
168 //translated to InsufficientPositionInfo
170 } else {
171 if (source)
173 }
174 }
175
176private:
178 {
182
183 const auto infos = activeMonitors();
184 for (const QGeoAreaMonitorInfo &info : infos) {
185 if (info.expiration().isValid()) {
186 if (!activeExpiry.first.isValid()) {
189 continue;
190 }
194 }
195 }
196 }
197
200 }
201
202
203 //returns true if areaEntered should be emitted
205 {
208 //this is the finishing singleshot event
212 } else {
214 }
215 return true;
216 }
217
218 return false;
219 }
220
221 //returns true if areaExited should be emitted
223 {
226 //this is the finishing singleShot event
230 } else {
232 }
233 return true;
234 }
235 return false;
236 }
237
238
239
242 void positionError(const QGeoPositionInfoSource::Error error);
243 void areaEventDetected(const QGeoAreaMonitorInfo &minfo,
244 const QGeoPositionInfo &pinfo, bool isEnteredEvent);
245private Q_SLOTS:
246 void timeout()
247 {
248 /*
249 * Don't block timer firing even if monitorExpiredSignal is not connected.
250 * This allows us to continue to remove the existing monitors as they expire.
251 **/
252 const QGeoAreaMonitorInfo info = activeMonitorAreas.take(activeExpiry.second);
253 setupNextExpiryTimeout();
254 emit timeout(info);
255
256 }
257
258 void positionUpdated(const QGeoPositionInfo &info)
259 {
260 const auto monInfos = activeMonitors();
261 for (const QGeoAreaMonitorInfo &monInfo : monInfos) {
262 const QString identifier = monInfo.identifier();
263 if (monInfo.area().contains(info.coordinate())) {
264 if (processInsideArea(identifier))
265 emit areaEventDetected(monInfo, info, true);
266 } else {
267 if (processOutsideArea(identifier))
268 emit areaEventDetected(monInfo, info, false);
269 }
270 }
271 }
272
273private:
274 QPair<QDateTime, QString> activeExpiry;
275 QHash<QString, int> singleShotTrigger;
276 QTimer* nextExpiryTimer;
277 QSet<QString> insideArea;
278
279 MonitorTable activeMonitorAreas;
280
281 QGeoPositionInfoSource* source = nullptr;
282 QList<QGeoAreaMonitorPolling*> registeredClients;
283 mutable QRecursiveMutex mutex;
284};
285
286Q_GLOBAL_STATIC(QGeoAreaMonitorPollingPrivate, pollingPrivate)
287
289{
290 d = pollingPrivate();
291 d->registerClient(this);
292 //hookup to default source if existing
293 if (!positionInfoSource())
294 setPositionInfoSource(QGeoPositionInfoSource::createDefaultSource(this));
295}
296
297QGeoAreaMonitorPolling::~QGeoAreaMonitorPolling()
298{
299 d->deregisterClient(this);
300}
301
303{
304 return d->positionSource();
305}
306
307void QGeoAreaMonitorPolling::setPositionInfoSource(QGeoPositionInfoSource *source)
308{
309 d->setPositionSource(source);
310}
311
313{
314 return lastError;
315}
316
317bool QGeoAreaMonitorPolling::startMonitoring(const QGeoAreaMonitorInfo &monitor)
318{
319 if (!monitor.isValid())
320 return false;
321
322 //reject an expiry in the past
323 if (monitor.expiration().isValid() &&
324 (monitor.expiration() < QDateTime::currentDateTime()))
325 return false;
326
327 //don't accept persistent monitor since we don't support it
328 if (monitor.isPersistent())
329 return false;
330
331 lastError = QGeoAreaMonitorSource::NoError;
332
333 //update or insert
334 d->startMonitoring(monitor);
335
336 return true;
337}
338
339int QGeoAreaMonitorPolling::idForSignal(const char *signal)
340{
341 if (qstrlen(signal) < 2)
342 return -1;
343
344 const QByteArray sig = QMetaObject::normalizedSignature(signal + 1);
345 const QMetaObject * const mo = metaObject();
346
347 return mo->indexOfSignal(sig.constData());
348}
349
350bool QGeoAreaMonitorPolling::hasConnections() const
351{
352 // This method is internal and requires the mutex to be already locked.
353 return signalConnections > 0;
354}
355
356bool QGeoAreaMonitorPolling::requestUpdate(const QGeoAreaMonitorInfo &monitor, const char *signal)
357{
358 if (!monitor.isValid())
359 return false;
360 //reject an expiry in the past
361 if (monitor.expiration().isValid() &&
362 (monitor.expiration() < QDateTime::currentDateTime()))
363 return false;
364
365 //don't accept persistent monitor since we don't support it
366 if (monitor.isPersistent())
367 return false;
368
369 // signal is validated in idForSignal()
370 const int signalId = idForSignal(signal);
371 if (signalId < 0)
372 return false;
373
374 //only accept area entered or exit signal
375 if (signalId != areaEnteredSignal().methodIndex() &&
376 signalId != areaExitedSignal().methodIndex())
377 {
378 return false;
379 }
380
381 lastError = QGeoAreaMonitorSource::NoError;
382
383 d->requestUpdate(monitor, signalId);
384
385 return true;
386}
387
388bool QGeoAreaMonitorPolling::stopMonitoring(const QGeoAreaMonitorInfo &monitor)
389{
390 QGeoAreaMonitorInfo info = d->stopMonitoring(monitor);
391
392 return info.isValid();
393}
394
396{
397 return d->activeMonitors().values();
398}
399
401{
402 QList<QGeoAreaMonitorInfo> results;
403 if (region.isEmpty())
404 return results;
405
406 const MonitorTable list = d->activeMonitors();
407 for (const QGeoAreaMonitorInfo &monitor : list) {
408 if (region.contains(monitor.area().center()))
409 results.append(monitor);
410 }
411
412 return results;
413}
414
419
420void QGeoAreaMonitorPolling::connectNotify(const QMetaMethod &signal)
421{
422 QMutexLocker locker(&connectionMutex);
423 if (signal == areaEnteredSignal() || signal == areaExitedSignal()) {
424 const bool alreadyConnected = hasConnections();
425 signalConnections++;
426 if (!alreadyConnected)
427 d->checkStartStop();
428 }
429}
430
431void QGeoAreaMonitorPolling::disconnectNotify(const QMetaMethod &signal)
432{
433 QMutexLocker locker(&connectionMutex);
434 if (signal == areaEnteredSignal() || signal == areaExitedSignal()) {
435 if (hasConnections())
436 signalConnections--;
437 if (!hasConnections())
438 d->checkStartStop();
439 }
440}
441
442void QGeoAreaMonitorPolling::positionError(const QGeoPositionInfoSource::Error error)
443{
444 switch (error) {
445 case QGeoPositionInfoSource::AccessError:
446 lastError = QGeoAreaMonitorSource::AccessError;
447 break;
448 case QGeoPositionInfoSource::UnknownSourceError:
449 lastError = QGeoAreaMonitorSource::UnknownSourceError;
450 break;
451 case QGeoPositionInfoSource::ClosedError:
452 case QGeoPositionInfoSource::UpdateTimeoutError:
453 lastError = QGeoAreaMonitorSource::InsufficientPositionInfo;
454 break;
455 case QGeoPositionInfoSource::NoError:
456 return;
457 }
458
459 emit QGeoAreaMonitorSource::errorOccurred(lastError);
460}
461
462void QGeoAreaMonitorPolling::timeout(const QGeoAreaMonitorInfo& monitor)
463{
464 if (isSignalConnected(monitorExpiredSignal()))
465 emit monitorExpired(monitor);
466}
467
468void QGeoAreaMonitorPolling::processAreaEvent(const QGeoAreaMonitorInfo &minfo,
469 const QGeoPositionInfo &pinfo, bool isEnteredEvent)
470{
471 if (isEnteredEvent)
472 emit areaEntered(minfo, pinfo);
473 else
474 emit areaExited(minfo, pinfo);
475}
476
477QT_END_NAMESPACE
478
479#include "qgeoareamonitor_polling.moc"
480#include "moc_qgeoareamonitor_polling.cpp"
void areaEventDetected(const QGeoAreaMonitorInfo &minfo, const QGeoPositionInfo &pinfo, bool isEnteredEvent)
void positionError(const QGeoPositionInfoSource::Error error)
Error error() const override
Returns the type of error that last occurred.
QGeoPositionInfoSource * positionInfoSource() const override
Returns the current QGeoPositionInfoSource used by this QGeoAreaMonitorSource object.
void connectNotify(const QMetaMethod &signal) override
QGeoAreaMonitorSource::AreaMonitorFeatures supportedAreaMonitorFeatures() const override
Returns the area monitoring features available to this source.
QList< QGeoAreaMonitorInfo > activeMonitors() const override
Returns the list of all active monitors known to the QGeoAreaMonitorSource object.
bool stopMonitoring(const QGeoAreaMonitorInfo &monitor) override
Returns true if monitor was successfully removed from the list of \l activeMonitors(); otherwise retu...
void setPositionInfoSource(QGeoPositionInfoSource *source) override
Sets the new \l QGeoPositionInfoSource to be used by this QGeoAreaMonitorSource object.
bool requestUpdate(const QGeoAreaMonitorInfo &monitor, const char *signal) override
Enables single shot area monitoring.
QList< QGeoAreaMonitorInfo > activeMonitors(const QGeoShape &region) const override
Returns the list of all active monitors known to the QGeoAreaMonitorSource object whose center lies w...
bool startMonitoring(const QGeoAreaMonitorInfo &monitor) override
Returns true if the monitoring of monitor could be successfully started; otherwise returns false.
void disconnectNotify(const QMetaMethod &signal) override
QObject * parent
Definition qobject.h:73
\inmodule QtCore
Definition qobject.h:105
static QMetaMethod areaEnteredSignal()
#define UPDATE_INTERVAL_5S
static QMetaMethod monitorExpiredSignal()
static QMetaMethod areaExitedSignal()