Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
20typedef QHash<QString, QGeoAreaMonitorInfo> MonitorTable;
21
22
28
34
40
42{
44public:
46 {
47 nextExpiryTimer = new QTimer(this);
48 nextExpiryTimer->setSingleShot(true);
49 connect(nextExpiryTimer, SIGNAL(timeout()),
50 this, SLOT(timeout()));
51 }
52
54 {
55 const std::lock_guard<QRecursiveMutex> locker(mutex);
56
57 activeMonitorAreas.insert(monitor.identifier(), monitor);
58 singleShotTrigger.remove(monitor.identifier());
59
61 setupNextExpiryTimeout();
62 }
63
64 void requestUpdate(const QGeoAreaMonitorInfo &monitor, int signalId)
65 {
66 const std::lock_guard<QRecursiveMutex> locker(mutex);
67
68 activeMonitorAreas.insert(monitor.identifier(), monitor);
69 singleShotTrigger.insert(monitor.identifier(), signalId);
70
72 setupNextExpiryTimeout();
73 }
74
76 {
77 const std::lock_guard<QRecursiveMutex> locker(mutex);
78
79 QGeoAreaMonitorInfo mon = activeMonitorAreas.take(monitor.identifier());
80
82 setupNextExpiryTimeout();
83
84 return mon;
85 }
86
88 {
89 const std::lock_guard<QRecursiveMutex> locker(mutex);
90
93
96
98 client, SLOT(processAreaEvent(QGeoAreaMonitorInfo,QGeoPositionInfo,bool)));
99
100 registeredClients.append(client);
101 }
102
104 {
105 const std::lock_guard<QRecursiveMutex> locker(mutex);
106
107 registeredClients.removeAll(client);
108 if (registeredClients.isEmpty())
110 }
111
113 {
114 const std::lock_guard<QRecursiveMutex> locker(mutex);
115
116 if (newSource == source)
117 return;
118
119 if (source)
120 delete source;
121
122 source = newSource;
123
124 if (source) {
125 source->setParent(this);
126 source->moveToThread(this->thread());
127 if (source->updateInterval() == 0)
128 source->setUpdateInterval(UPDATE_INTERVAL_5S);
129 disconnect(source, 0, 0, 0); //disconnect all
130 connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)),
131 this, SLOT(positionUpdated(QGeoPositionInfo)));
135 }
136 }
137
139 {
140 const std::lock_guard<QRecursiveMutex> locker(mutex);
141 return source;
142 }
143
145 {
146 const std::lock_guard<QRecursiveMutex> locker(mutex);
147
148 return activeMonitorAreas;
149 }
150
152 {
153 const std::lock_guard<QRecursiveMutex> locker(mutex);
154
155 bool signalsConnected = false;
156 for (const QGeoAreaMonitorPolling *client : std::as_const(registeredClients)) {
157 if (client->hasConnections()) {
158 signalsConnected = true;
159 break;
160 }
161 }
162
163 if (signalsConnected && !activeMonitorAreas.isEmpty()) {
164 if (source)
165 source->startUpdates();
166 else
167 //translated to InsufficientPositionInfo
169 } else {
170 if (source)
171 source->stopUpdates();
172 }
173 }
174
175private:
176 void setupNextExpiryTimeout()
177 {
178 nextExpiryTimer->stop();
179 activeExpiry.first = QDateTime();
180 activeExpiry.second = QString();
181
182 const auto infos = activeMonitors();
183 for (const QGeoAreaMonitorInfo &info : infos) {
184 if (info.expiration().isValid()) {
185 if (!activeExpiry.first.isValid()) {
186 activeExpiry.first = info.expiration();
187 activeExpiry.second = info.identifier();
188 continue;
189 }
190 if (info.expiration() < activeExpiry.first) {
191 activeExpiry.first = info.expiration();
192 activeExpiry.second = info.identifier();
193 }
194 }
195 }
196
197 if (activeExpiry.first.isValid())
198 nextExpiryTimer->start(QDateTime::currentDateTime().msecsTo(activeExpiry.first));
199 }
200
201
202 //returns true if areaEntered should be emitted
203 bool processInsideArea(const QString &monitorIdent)
204 {
205 if (!insideArea.contains(monitorIdent)) {
206 if (singleShotTrigger.value(monitorIdent, -1) == areaEnteredSignal().methodIndex()) {
207 //this is the finishing singleshot event
208 singleShotTrigger.remove(monitorIdent);
209 activeMonitorAreas.remove(monitorIdent);
210 setupNextExpiryTimeout();
211 } else {
212 insideArea.insert(monitorIdent);
213 }
214 return true;
215 }
216
217 return false;
218 }
219
220 //returns true if areaExited should be emitted
221 bool processOutsideArea(const QString &monitorIdent)
222 {
223 if (insideArea.contains(monitorIdent)) {
224 if (singleShotTrigger.value(monitorIdent, -1) == areaExitedSignal().methodIndex()) {
225 //this is the finishing singleShot event
226 singleShotTrigger.remove(monitorIdent);
227 activeMonitorAreas.remove(monitorIdent);
228 setupNextExpiryTimeout();
229 } else {
230 insideArea.remove(monitorIdent);
231 }
232 return true;
233 }
234 return false;
235 }
236
237
238
243 const QGeoPositionInfo &pinfo, bool isEnteredEvent);
244private Q_SLOTS:
245 void timeout()
246 {
247 /*
248 * Don't block timer firing even if monitorExpiredSignal is not connected.
249 * This allows us to continue to remove the existing monitors as they expire.
250 **/
251 const QGeoAreaMonitorInfo info = activeMonitorAreas.take(activeExpiry.second);
252 setupNextExpiryTimeout();
254
255 }
256
257 void positionUpdated(const QGeoPositionInfo &info)
258 {
259 const auto monInfos = activeMonitors();
260 for (const QGeoAreaMonitorInfo &monInfo : monInfos) {
261 const QString identifier = monInfo.identifier();
262 if (monInfo.area().contains(info.coordinate())) {
263 if (processInsideArea(identifier))
264 emit areaEventDetected(monInfo, info, true);
265 } else {
266 if (processOutsideArea(identifier))
267 emit areaEventDetected(monInfo, info, false);
268 }
269 }
270 }
271
272private:
273 QPair<QDateTime, QString> activeExpiry;
274 QHash<QString, int> singleShotTrigger;
275 QTimer* nextExpiryTimer;
276 QSet<QString> insideArea;
277
278 MonitorTable activeMonitorAreas;
279
281 QList<QGeoAreaMonitorPolling*> registeredClients;
282 mutable QRecursiveMutex mutex;
283};
284
286
288{
289 d = pollingPrivate();
290 d->registerClient(this);
291 //hookup to default source if existing
292 if (!positionInfoSource())
293 setPositionInfoSource(QGeoPositionInfoSource::createDefaultSource(this));
294}
295
300
305
310
312{
313 return lastError;
314}
315
317{
318 if (!monitor.isValid())
319 return false;
320
321 //reject an expiry in the past
322 if (monitor.expiration().isValid() &&
324 return false;
325
326 //don't accept persistent monitor since we don't support it
327 if (monitor.isPersistent())
328 return false;
329
331
332 //update or insert
333 d->startMonitoring(monitor);
334
335 return true;
336}
337
338int QGeoAreaMonitorPolling::idForSignal(const char *signal)
339{
341 const QMetaObject * const mo = metaObject();
342
343 return mo->indexOfSignal(sig.constData());
344}
345
346bool QGeoAreaMonitorPolling::hasConnections() const
347{
348 // This method is internal and requires the mutex to be already locked.
349 return signalConnections > 0;
350}
351
353{
354 if (!monitor.isValid())
355 return false;
356 //reject an expiry in the past
357 if (monitor.expiration().isValid() &&
359 return false;
360
361 //don't accept persistent monitor since we don't support it
362 if (monitor.isPersistent())
363 return false;
364
365 if (!signal)
366 return false;
367
368 const int signalId = idForSignal(signal);
369 if (signalId < 0)
370 return false;
371
372 //only accept area entered or exit signal
373 if (signalId != areaEnteredSignal().methodIndex() &&
374 signalId != areaExitedSignal().methodIndex())
375 {
376 return false;
377 }
378
380
381 d->requestUpdate(monitor, signalId);
382
383 return true;
384}
385
387{
389
390 return info.isValid();
391}
392
393QList<QGeoAreaMonitorInfo> QGeoAreaMonitorPolling::activeMonitors() const
394{
395 return d->activeMonitors().values();
396}
397
398QList<QGeoAreaMonitorInfo> QGeoAreaMonitorPolling::activeMonitors(const QGeoShape &region) const
399{
400 QList<QGeoAreaMonitorInfo> results;
401 if (region.isEmpty())
402 return results;
403
404 const MonitorTable list = d->activeMonitors();
405 for (const QGeoAreaMonitorInfo &monitor : list) {
406 if (region.contains(monitor.area().center()))
407 results.append(monitor);
408 }
409
410 return results;
411}
412
413QGeoAreaMonitorSource::AreaMonitorFeatures QGeoAreaMonitorPolling::supportedAreaMonitorFeatures() const
414{
415 return {};
416}
417
419{
420 QMutexLocker locker(&connectionMutex);
422 const bool alreadyConnected = hasConnections();
423 signalConnections++;
424 if (!alreadyConnected)
425 d->checkStartStop();
426 }
427}
428
430{
431 QMutexLocker locker(&connectionMutex);
433 if (hasConnections())
434 signalConnections--;
435 if (!hasConnections())
436 d->checkStartStop();
437 }
438}
439
440void QGeoAreaMonitorPolling::positionError(const QGeoPositionInfoSource::Error error)
441{
442 switch (error) {
445 break;
448 break;
452 break;
454 return;
455 }
456
458}
459
460void QGeoAreaMonitorPolling::timeout(const QGeoAreaMonitorInfo& monitor)
461{
463 emit monitorExpired(monitor);
464}
465
466void QGeoAreaMonitorPolling::processAreaEvent(const QGeoAreaMonitorInfo &minfo,
467 const QGeoPositionInfo &pinfo, bool isEnteredEvent)
468{
469 if (isEnteredEvent)
470 emit areaEntered(minfo, pinfo);
471 else
472 emit areaExited(minfo, pinfo);
473}
474
475#include "qgeoareamonitor_polling.moc"
476#include "moc_qgeoareamonitor_polling.cpp"
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
\inmodule QtPositioning
bool isValid() const
Returns true, if the monitor is valid.
QString identifier() const
Returns the identifier of the QGeoAreaMonitorInfo object.
QDateTime expiration() const
Returns the expiry date.
bool isPersistent() const
Returns true if the QGeoAreaMonitorInfo is persistent.
QGeoAreaMonitorInfo stopMonitoring(const QGeoAreaMonitorInfo &monitor)
void timeout(const QGeoAreaMonitorInfo &info)
void areaEventDetected(const QGeoAreaMonitorInfo &minfo, const QGeoPositionInfo &pinfo, bool isEnteredEvent)
void registerClient(QGeoAreaMonitorPolling *client)
void requestUpdate(const QGeoAreaMonitorInfo &monitor, int signalId)
void positionError(const QGeoPositionInfoSource::Error error)
QGeoPositionInfoSource * positionSource() const
void setPositionSource(QGeoPositionInfoSource *newSource)
void deregisterClient(QGeoAreaMonitorPolling *client)
void startMonitoring(const QGeoAreaMonitorInfo &monitor)
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.
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
\inmodule QtPositioning
void areaEntered(const QGeoAreaMonitorInfo &monitor, const QGeoPositionInfo &update)
Emitted when the current position has moved from a position outside of the active monitor to a positi...
void errorOccurred(QGeoAreaMonitorSource::Error error)
This signal is emitted after an error occurred.
Error
Defines the types of positioning methods.
void monitorExpired(const QGeoAreaMonitorInfo &monitor)
Emitted when monitor has expired.
void areaExited(const QGeoAreaMonitorInfo &monitor, const QGeoPositionInfo &update)
Emitted when the current position has moved from a position within the active monitor to a position o...
\inmodule QtPositioning
static QGeoPositionInfoSource * createDefaultSource(QObject *parent)
Creates and returns a position source with the given parent that reads from the system's default sour...
Error
The Error enumeration represents the errors which can occur.
\inmodule QtPositioning
\inmodule QtPositioning
Definition qgeoshape.h:17
Q_INVOKABLE bool contains(const QGeoCoordinate &coordinate) const
Returns whether the coordinate coordinate is contained within this geo shape.
bool isEmpty
This property defines whether this geo shape is empty.
Definition qgeoshape.h:21
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1098
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:985
T value(const Key &key) const noexcept
Definition qhash.h:1054
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:928
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
bool isEmpty() const noexcept
Definition qlist.h:401
qsizetype removeAll(const AT &t)
Definition qlist.h:592
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmetaobject.h:19
static QMetaMethod fromSignal(PointerToMemberFunction signal)
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qobject.h:103
bool isSignalConnected(const QMetaMethod &signal) const
Definition qobject.cpp:2801
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
\inmodule QtCore
Definition qmutex.h:309
bool remove(const T &value)
Definition qset.h:63
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qtimer.h:20
void setSingleShot(bool singleShot)
Definition qtimer.cpp:552
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void stop()
Stops the timer.
Definition qtimer.cpp:267
auto signal
auto mo
[7]
DBusConnection const char DBusError * error
QHash< QString, QGeoAreaMonitorInfo > MonitorTable
static QMetaMethod areaEnteredSignal()
#define UPDATE_INTERVAL_5S
static QMetaMethod monitorExpiredSignal()
static QMetaMethod areaExitedSignal()
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
static qint64 msecsTo(const QDateTime &from, const QDateTime &to)
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLbitfield GLuint64 timeout
[4]
GLsizei GLsizei GLchar * source
#define Q_OBJECT
#define Q_SLOTS
#define Q_SIGNALS
#define emit
QList< int > list
[14]
obj metaObject() -> className()
myObject disconnect()
[26]
QHostInfo info
[0]
\inmodule QtCore
static QByteArray normalizedSignature(const char *method)
Normalizes the signature of the given method.