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// Qt-Security score:significant reason:default
4
6#include <QtPositioning/qgeocoordinate.h>
7#include <QtPositioning/qgeorectangle.h>
8#include <QtPositioning/qgeocircle.h>
9
10#include <QtCore/qhash.h>
11#include <QtCore/qmetaobject.h>
12#include <QtCore/qtimer.h>
13#include <QtCore/qdebug.h>
14#include <QtCore/qmutex.h>
15#include <QtCore/qset.h>
16
17#include <mutex>
18
19#define UPDATE_INTERVAL_5S 5000
20
21QT_BEGIN_NAMESPACE
22
23typedef QHash<QString, QGeoAreaMonitorInfo> MonitorTable;
24
26{
27 static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::areaEntered);
28 return signal;
29}
30
32{
33 static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::areaExited);
34 return signal;
35}
36
38{
39 static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::monitorExpired);
40 return signal;
41}
42
44{
46public:
54
65
76
88
104
113
139
145
147 {
149
150 return activeMonitorAreas;
151 }
152
154 {
156
157 bool signalsConnected = false;
159 if (client->hasConnections()) {
160 signalsConnected = true;
161 break;
162 }
163 }
164
166 if (source)
168 else
169 //translated to InsufficientPositionInfo
171 } else {
172 if (source)
174 }
175 }
176
177private:
179 {
183
184 const auto infos = activeMonitors();
185 for (const QGeoAreaMonitorInfo &info : infos) {
186 if (info.expiration().isValid()) {
187 if (!activeExpiry.first.isValid()) {
190 continue;
191 }
195 }
196 }
197 }
198
201 }
202
203
204 //returns true if areaEntered should be emitted
206 {
209 //this is the finishing singleshot event
213 } else {
215 }
216 return true;
217 }
218
219 return false;
220 }
221
222 //returns true if areaExited should be emitted
224 {
227 //this is the finishing singleShot event
231 } else {
233 }
234 return true;
235 }
236 return false;
237 }
238
239
240
243 void positionError(const QGeoPositionInfoSource::Error error);
244 void areaEventDetected(const QGeoAreaMonitorInfo &minfo,
245 const QGeoPositionInfo &pinfo, bool isEnteredEvent);
246private Q_SLOTS:
247 void timeout()
248 {
249 /*
250 * Don't block timer firing even if monitorExpiredSignal is not connected.
251 * This allows us to continue to remove the existing monitors as they expire.
252 **/
253 const QGeoAreaMonitorInfo info = activeMonitorAreas.take(activeExpiry.second);
254 setupNextExpiryTimeout();
255 emit timeout(info);
256
257 }
258
259 void positionUpdated(const QGeoPositionInfo &info)
260 {
261 const auto monInfos = activeMonitors();
262 for (const QGeoAreaMonitorInfo &monInfo : monInfos) {
263 const QString identifier = monInfo.identifier();
264 if (monInfo.area().contains(info.coordinate())) {
265 if (processInsideArea(identifier))
266 emit areaEventDetected(monInfo, info, true);
267 } else {
268 if (processOutsideArea(identifier))
269 emit areaEventDetected(monInfo, info, false);
270 }
271 }
272 }
273
274private:
275 QPair<QDateTime, QString> activeExpiry;
276 QHash<QString, int> singleShotTrigger;
277 QTimer* nextExpiryTimer;
278 QSet<QString> insideArea;
279
280 MonitorTable activeMonitorAreas;
281
282 QGeoPositionInfoSource* source = nullptr;
283 QList<QGeoAreaMonitorPolling*> registeredClients;
284 mutable QRecursiveMutex mutex;
285};
286
287Q_GLOBAL_STATIC(QGeoAreaMonitorPollingPrivate, pollingPrivate)
288
290{
291 d = pollingPrivate();
292 d->registerClient(this);
293 //hookup to default source if existing
294 if (!positionInfoSource())
295 setPositionInfoSource(QGeoPositionInfoSource::createDefaultSource(this));
296}
297
298QGeoAreaMonitorPolling::~QGeoAreaMonitorPolling()
299{
300 d->deregisterClient(this);
301}
302
304{
305 return d->positionSource();
306}
307
308void QGeoAreaMonitorPolling::setPositionInfoSource(QGeoPositionInfoSource *source)
309{
310 d->setPositionSource(source);
311}
312
314{
315 return lastError;
316}
317
318bool QGeoAreaMonitorPolling::startMonitoring(const QGeoAreaMonitorInfo &monitor)
319{
320 if (!monitor.isValid())
321 return false;
322
323 //reject an expiry in the past
324 if (monitor.expiration().isValid() &&
325 (monitor.expiration() < QDateTime::currentDateTime()))
326 return false;
327
328 //don't accept persistent monitor since we don't support it
329 if (monitor.isPersistent())
330 return false;
331
332 lastError = QGeoAreaMonitorSource::NoError;
333
334 //update or insert
335 d->startMonitoring(monitor);
336
337 return true;
338}
339
340int QGeoAreaMonitorPolling::idForSignal(const char *signal)
341{
342 if (qstrlen(signal) < 2)
343 return -1;
344
345 const QByteArray sig = QMetaObject::normalizedSignature(signal + 1);
346 const QMetaObject * const mo = metaObject();
347
348 return mo->indexOfSignal(sig.constData());
349}
350
351bool QGeoAreaMonitorPolling::hasConnections() const
352{
353 // This method is internal and requires the mutex to be already locked.
354 return signalConnections > 0;
355}
356
357bool QGeoAreaMonitorPolling::requestUpdate(const QGeoAreaMonitorInfo &monitor, const char *signal)
358{
359 if (!monitor.isValid())
360 return false;
361 //reject an expiry in the past
362 if (monitor.expiration().isValid() &&
363 (monitor.expiration() < QDateTime::currentDateTime()))
364 return false;
365
366 //don't accept persistent monitor since we don't support it
367 if (monitor.isPersistent())
368 return false;
369
370 // signal is validated in idForSignal()
371 const int signalId = idForSignal(signal);
372 if (signalId < 0)
373 return false;
374
375 //only accept area entered or exit signal
376 if (signalId != areaEnteredSignal().methodIndex() &&
377 signalId != areaExitedSignal().methodIndex())
378 {
379 return false;
380 }
381
382 lastError = QGeoAreaMonitorSource::NoError;
383
384 d->requestUpdate(monitor, signalId);
385
386 return true;
387}
388
389bool QGeoAreaMonitorPolling::stopMonitoring(const QGeoAreaMonitorInfo &monitor)
390{
391 QGeoAreaMonitorInfo info = d->stopMonitoring(monitor);
392
393 return info.isValid();
394}
395
397{
398 return d->activeMonitors().values();
399}
400
402{
403 QList<QGeoAreaMonitorInfo> results;
404 if (region.isEmpty())
405 return results;
406
407 const MonitorTable list = d->activeMonitors();
408 for (const QGeoAreaMonitorInfo &monitor : list) {
409 if (region.contains(monitor.area().center()))
410 results.append(monitor);
411 }
412
413 return results;
414}
415
420
421void QGeoAreaMonitorPolling::connectNotify(const QMetaMethod &signal)
422{
423 QMutexLocker locker(&connectionMutex);
424 if (signal == areaEnteredSignal() || signal == areaExitedSignal()) {
425 const bool alreadyConnected = hasConnections();
426 signalConnections++;
427 if (!alreadyConnected)
428 d->checkStartStop();
429 }
430}
431
432void QGeoAreaMonitorPolling::disconnectNotify(const QMetaMethod &signal)
433{
434 QMutexLocker locker(&connectionMutex);
435 if (signal == areaEnteredSignal() || signal == areaExitedSignal()) {
436 if (hasConnections())
437 signalConnections--;
438 if (!hasConnections())
439 d->checkStartStop();
440 }
441}
442
443void QGeoAreaMonitorPolling::positionError(const QGeoPositionInfoSource::Error error)
444{
445 switch (error) {
446 case QGeoPositionInfoSource::AccessError:
447 lastError = QGeoAreaMonitorSource::AccessError;
448 break;
449 case QGeoPositionInfoSource::UnknownSourceError:
450 lastError = QGeoAreaMonitorSource::UnknownSourceError;
451 break;
452 case QGeoPositionInfoSource::ClosedError:
453 case QGeoPositionInfoSource::UpdateTimeoutError:
454 lastError = QGeoAreaMonitorSource::InsufficientPositionInfo;
455 break;
456 case QGeoPositionInfoSource::NoError:
457 return;
458 }
459
460 emit QGeoAreaMonitorSource::errorOccurred(lastError);
461}
462
463void QGeoAreaMonitorPolling::timeout(const QGeoAreaMonitorInfo& monitor)
464{
465 if (isSignalConnected(monitorExpiredSignal()))
466 emit monitorExpired(monitor);
467}
468
469void QGeoAreaMonitorPolling::processAreaEvent(const QGeoAreaMonitorInfo &minfo,
470 const QGeoPositionInfo &pinfo, bool isEnteredEvent)
471{
472 if (isEnteredEvent)
473 emit areaEntered(minfo, pinfo);
474 else
475 emit areaExited(minfo, pinfo);
476}
477
478QT_END_NAMESPACE
479
480#include "qgeoareamonitor_polling.moc"
481#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:74
\inmodule QtCore
Definition qobject.h:106
static QMetaMethod areaEnteredSignal()
#define UPDATE_INTERVAL_5S
static QMetaMethod monitorExpiredSignal()
static QMetaMethod areaExitedSignal()