Qt 6.x
The Qt SDK
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
21
22
24{
26 return signal;
27}
28
30{
32 return signal;
33}
34
36{
38 return signal;
39}
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 foreach (const QGeoAreaMonitorPolling *client, 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 foreach (const QGeoAreaMonitorInfo &info, activeMonitors()) {
183 if (info.expiration().isValid()) {
184 if (!activeExpiry.first.isValid()) {
185 activeExpiry.first = info.expiration();
186 activeExpiry.second = info.identifier();
187 continue;
188 }
189 if (info.expiration() < activeExpiry.first) {
190 activeExpiry.first = info.expiration();
191 activeExpiry.second = info.identifier();
192 }
193 }
194 }
195
196 if (activeExpiry.first.isValid())
197 nextExpiryTimer->start(QDateTime::currentDateTime().msecsTo(activeExpiry.first));
198 }
199
200
201 //returns true if areaEntered should be emitted
202 bool processInsideArea(const QString &monitorIdent)
203 {
204 if (!insideArea.contains(monitorIdent)) {
205 if (singleShotTrigger.value(monitorIdent, -1) == areaEnteredSignal().methodIndex()) {
206 //this is the finishing singleshot event
207 singleShotTrigger.remove(monitorIdent);
208 activeMonitorAreas.remove(monitorIdent);
209 setupNextExpiryTimeout();
210 } else {
211 insideArea.insert(monitorIdent);
212 }
213 return true;
214 }
215
216 return false;
217 }
218
219 //returns true if areaExited should be emitted
220 bool processOutsideArea(const QString &monitorIdent)
221 {
222 if (insideArea.contains(monitorIdent)) {
223 if (singleShotTrigger.value(monitorIdent, -1) == areaExitedSignal().methodIndex()) {
224 //this is the finishing singleShot event
225 singleShotTrigger.remove(monitorIdent);
226 activeMonitorAreas.remove(monitorIdent);
227 setupNextExpiryTimeout();
228 } else {
229 insideArea.remove(monitorIdent);
230 }
231 return true;
232 }
233 return false;
234 }
235
236
237
242 const QGeoPositionInfo &pinfo, bool isEnteredEvent);
243private Q_SLOTS:
244 void timeout()
245 {
246 /*
247 * Don't block timer firing even if monitorExpiredSignal is not connected.
248 * This allows us to continue to remove the existing monitors as they expire.
249 **/
250 const QGeoAreaMonitorInfo info = activeMonitorAreas.take(activeExpiry.second);
251 setupNextExpiryTimeout();
253
254 }
255
256 void positionUpdated(const QGeoPositionInfo &info)
257 {
258 foreach (const QGeoAreaMonitorInfo &monInfo, activeMonitors()) {
259 const QString identifier = monInfo.identifier();
260 if (monInfo.area().contains(info.coordinate())) {
261 if (processInsideArea(identifier))
262 emit areaEventDetected(monInfo, info, true);
263 } else {
264 if (processOutsideArea(identifier))
265 emit areaEventDetected(monInfo, info, false);
266 }
267 }
268 }
269
270private:
271 QPair<QDateTime, QString> activeExpiry;
272 QHash<QString, int> singleShotTrigger;
273 QTimer* nextExpiryTimer;
274 QSet<QString> insideArea;
275
276 MonitorTable activeMonitorAreas;
277
279 QList<QGeoAreaMonitorPolling*> registeredClients;
280 mutable QRecursiveMutex mutex;
281};
282
284
286{
287 d = pollingPrivate();
288 d->registerClient(this);
289 //hookup to default source if existing
290 if (!positionInfoSource())
291 setPositionInfoSource(QGeoPositionInfoSource::createDefaultSource(this));
292}
293
295{
296 d->deregisterClient(this);
297}
298
300{
301 return d->positionSource();
302}
303
305{
307}
308
310{
311 return lastError;
312}
313
315{
316 if (!monitor.isValid())
317 return false;
318
319 //reject an expiry in the past
320 if (monitor.expiration().isValid() &&
322 return false;
323
324 //don't accept persistent monitor since we don't support it
325 if (monitor.isPersistent())
326 return false;
327
329
330 //update or insert
331 d->startMonitoring(monitor);
332
333 return true;
334}
335
336int QGeoAreaMonitorPolling::idForSignal(const char *signal)
337{
339 const QMetaObject * const mo = metaObject();
340
341 return mo->indexOfSignal(sig.constData());
342}
343
344bool QGeoAreaMonitorPolling::hasConnections() const
345{
346 // This method is internal and requires the mutex to be already locked.
347 return signalConnections > 0;
348}
349
351{
352 if (!monitor.isValid())
353 return false;
354 //reject an expiry in the past
355 if (monitor.expiration().isValid() &&
357 return false;
358
359 //don't accept persistent monitor since we don't support it
360 if (monitor.isPersistent())
361 return false;
362
363 if (!signal)
364 return false;
365
366 const int signalId = idForSignal(signal);
367 if (signalId < 0)
368 return false;
369
370 //only accept area entered or exit signal
371 if (signalId != areaEnteredSignal().methodIndex() &&
372 signalId != areaExitedSignal().methodIndex())
373 {
374 return false;
375 }
376
378
379 d->requestUpdate(monitor, signalId);
380
381 return true;
382}
383
385{
387
388 return info.isValid();
389}
390
392{
393 return d->activeMonitors().values();
394}
395
397{
399 if (region.isEmpty())
400 return results;
401
402 const MonitorTable list = d->activeMonitors();
403 foreach (const QGeoAreaMonitorInfo &monitor, list) {
404 if (region.contains(monitor.area().center()))
405 results.append(monitor);
406 }
407
408 return results;
409}
410
411QGeoAreaMonitorSource::AreaMonitorFeatures QGeoAreaMonitorPolling::supportedAreaMonitorFeatures() const
412{
413 return {};
414}
415
417{
418 QMutexLocker locker(&connectionMutex);
420 const bool alreadyConnected = hasConnections();
421 signalConnections++;
422 if (!alreadyConnected)
423 d->checkStartStop();
424 }
425}
426
428{
429 QMutexLocker locker(&connectionMutex);
431 if (hasConnections())
432 signalConnections--;
433 if (!hasConnections())
434 d->checkStartStop();
435 }
436}
437
438void QGeoAreaMonitorPolling::positionError(const QGeoPositionInfoSource::Error error)
439{
440 switch (error) {
443 break;
446 break;
450 break;
452 return;
453 }
454
456}
457
458void QGeoAreaMonitorPolling::timeout(const QGeoAreaMonitorInfo& monitor)
459{
461 emit monitorExpired(monitor);
462}
463
464void QGeoAreaMonitorPolling::processAreaEvent(const QGeoAreaMonitorInfo &minfo,
465 const QGeoPositionInfo &pinfo, bool isEnteredEvent)
466{
467 if (isEnteredEvent)
468 emit areaEntered(minfo, pinfo);
469 else
470 emit areaExited(minfo, pinfo);
471}
472
473#include "qgeoareamonitor_polling.moc"
474#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:122
\inmodule QtCore\reentrant
Definition qdatetime.h:257
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
QGeoShape area() const
Returns the boundaries of the to-be-monitored area.
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
QGeoCoordinate center
Definition qgeoshape.h:22
\inmodule QtCore
Definition qhash.h:818
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:956
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1088
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:975
T value(const Key &key) const noexcept
Definition qhash.h:1044
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:926
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
qsizetype removeAll(const AT &t)
Definition qlist.h:575
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmetaobject.h:18
static QMetaMethod fromSignal(PointerToMemberFunction signal)
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qobject.h:90
bool isSignalConnected(const QMetaMethod &signal) const
Definition qobject.cpp:2664
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2142
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
\inmodule QtCore
Definition qmutex.h:313
Definition qset.h:18
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:127
\inmodule QtCore
Definition qtimer.h:20
void setSingleShot(bool singleShot)
Definition qtimer.cpp:580
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:208
void stop()
Stops the timer.
Definition qtimer.cpp:226
auto signal
auto mo
[7]
std::pair< T1, T2 > QPair
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:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLsizei GLsizei GLchar * source
#define Q_OBJECT
#define Q_SLOTS
#define Q_SIGNALS
#define emit
QList< int > list
[14]
QFileInfo info(fileName)
[8]
obj metaObject() -> className()
myObject disconnect()
[26]
\inmodule QtCore
static QByteArray normalizedSignature(const char *method)
Normalizes the signature of the given method.
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent