Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
QtPositioning.java
Go to the documentation of this file.
1// Copyright (C) 2016 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
4package org.qtproject.qt.android.positioning;
5
6import android.content.Context;
7import android.location.GpsSatellite;
8import android.location.GpsStatus;
9import android.location.Location;
10import android.location.LocationListener;
11import android.location.LocationManager;
12import android.location.GnssStatus;
13import android.location.GnssStatus.Callback;
14import android.os.Bundle;
15import android.os.Handler;
16import android.os.Looper;
17import android.os.Build;
18import java.util.ArrayList;
19import java.util.HashMap;
20import java.util.Iterator;
21import java.util.List;
22
23import android.util.Log;
24
25public class QtPositioning implements LocationListener
26{
27
28 private static final String TAG = "qt.positioning.android";
29 static LocationManager locationManager = null;
30 static Object m_syncObject = new Object();
31 static HashMap<Integer, QtPositioning> runningListeners = new HashMap<Integer, QtPositioning>();
32
33 /*
34 The positionInfo instance to which this
35 QtPositioning instance is attached to.
36 */
37 private int nativeClassReference = 0;
38
39 /*
40 The provider type requested by Qt
41 */
42 private int expectedProviders = 0;
43
44 public static final int QT_GPS_PROVIDER = 1;
45 public static final int QT_NETWORK_PROVIDER = 2;
46
47 /* The following values must match the corresponding error enums in the Qt API*/
48 public static final int QT_ACCESS_ERROR = 0;
49 public static final int QT_CLOSED_ERROR = 1;
50 public static final int QT_POSITION_UNKNOWN_SOURCE_ERROR = 2;
51 public static final int QT_POSITION_NO_ERROR = 3;
52 public static final int QT_SATELLITE_NO_ERROR = 2;
53 public static final int QT_SATELLITE_UNKNOWN_SOURCE_ERROR = -1;
54
55 /* True, if updates were caused by requestUpdate() */
56 private boolean isSingleUpdate = false;
57 /* The length requested for regular intervals in msec. */
58 private int updateIntervalTime = 0;
59
60 /* The last received GPS update */
61 private Location lastGps = null;
62 /* The last received network update */
63 private Location lastNetwork = null;
64 /* If true this class acts as satellite signal monitor rather than location monitor */
65 private boolean isSatelliteUpdate = false;
66
67 private PositioningLooperBase looperThread;
68
69 private boolean isLocationProvidersDisabledInvoked = false;
70
71 static public void setContext(Context context)
72 {
73 try {
74 locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
75 } catch(Exception e) {
76 e.printStackTrace();
77 }
78 }
79
80 static private int[] providerList()
81 {
82 if (locationManager == null) {
83 Log.w(TAG, "No locationManager available in QtPositioning");
84 return new int[0];
85 }
86 List<String> providers = locationManager.getProviders(true);
87 int retList[] = new int[providers.size()];
88 for (int i = 0; i < providers.size(); i++) {
89 if (providers.get(i).equals(LocationManager.GPS_PROVIDER)) {
90 //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_GPS
91 retList[i] = 0;
92 } else if (providers.get(i).equals(LocationManager.NETWORK_PROVIDER)) {
93 //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_NETWORK
94 retList[i] = 1;
95 } else if (providers.get(i).equals(LocationManager.PASSIVE_PROVIDER)) {
96 //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_PASSIVE
97 retList[i] = 2;
98 } else {
99 retList[i] = -1;
100 }
101 }
102 return retList;
103 }
104
105 static public Location lastKnownPosition(boolean fromSatelliteOnly)
106 {
107 Location gps = null;
108 Location network = null;
109 try {
110 gps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
111 if (!fromSatelliteOnly)
112 network = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
113 } catch(Exception e) {
114 e.printStackTrace();
115 gps = network = null;
116 }
117
118 if (gps != null && network != null) {
119 //we return the most recent location but slightly prefer GPS
120 //prefer GPS if it is max 4 hrs older than network
121 long delta = network.getTime() - gps.getTime();
122 if (delta < 4*60*60*1000) {
123 return gps;
124 } else {
125 return network;
126 }
127 } else if (gps != null ) {
128 return gps;
129 } else if (network != null) {
130 return network;
131 }
132
133 return null;
134 }
135
136 /* Returns true if at least on of the given providers is enabled. */
137 static private boolean expectedProvidersAvailable(int desiredProviders)
138 {
139 List<String> enabledProviders = locationManager.getProviders(true);
140 if ((desiredProviders & QT_GPS_PROVIDER) > 0) { //gps desired
141 if (enabledProviders.contains(LocationManager.GPS_PROVIDER)) {
142 return true;
143 }
144 }
145 if ((desiredProviders & QT_NETWORK_PROVIDER) > 0) { //network desired
146 if (enabledProviders.contains(LocationManager.NETWORK_PROVIDER)) {
147 return true;
148 }
149 }
150
151 return false;
152 }
153
154
155 static private void addActiveListener(QtPositioning listener, String provider, long minTime, float minDistance)
156 {
157 int androidClassKey = listener.nativeClassReference;
158 //start update thread
159 listener.setActiveLooper(true);
160
161 if (runningListeners.containsKey(androidClassKey) && runningListeners.get(androidClassKey) != listener) {
162 removeActiveListener(androidClassKey);
163 }
164
165 locationManager.requestLocationUpdates(provider,
166 minTime, minDistance,
167 listener,
168 listener.looper());
169
170 runningListeners.put(androidClassKey, listener);
171 }
172
173
174 static private void removeActiveListener(QtPositioning listener)
175 {
176 removeActiveListener(listener.nativeClassReference);
177 }
178
179
180 static private void removeActiveListener(int androidClassKey)
181 {
182 QtPositioning listener = runningListeners.remove(androidClassKey);
183
184 if (listener != null) {
185 locationManager.removeUpdates(listener);
186 listener.setActiveLooper(false);
187 }
188 }
189
190
191 static public int startUpdates(int androidClassKey, int locationProvider, int updateInterval)
192 {
193 synchronized (m_syncObject) {
194 try {
195 boolean exceptionOccurred = false;
196 QtPositioning positioningListener = new QtPositioning();
197 positioningListener.nativeClassReference = androidClassKey;
198 positioningListener.expectedProviders = locationProvider;
199 positioningListener.isSatelliteUpdate = false;
200
201 if (updateInterval == 0)
202 updateInterval = 50; //don't update more often than once per 50ms
203
204 positioningListener.updateIntervalTime = updateInterval;
205 if ((locationProvider & QT_GPS_PROVIDER) > 0) {
206 Log.d(TAG, "Regular updates using GPS " + updateInterval);
207 try {
208 addActiveListener(positioningListener,
209 LocationManager.GPS_PROVIDER,
210 updateInterval, 0);
211 } catch (SecurityException se) {
212 se.printStackTrace();
213 exceptionOccurred = true;
214 }
215 }
216
217 if ((locationProvider & QT_NETWORK_PROVIDER) > 0) {
218 Log.d(TAG, "Regular updates using network " + updateInterval);
219 try {
220 addActiveListener(positioningListener,
221 LocationManager.NETWORK_PROVIDER,
222 updateInterval, 0);
223 } catch (SecurityException se) {
224 se.printStackTrace();
225 exceptionOccurred = true;
226 }
227 }
228 if (exceptionOccurred) {
229 removeActiveListener(positioningListener);
230 return QT_ACCESS_ERROR;
231 }
232
233 if (!expectedProvidersAvailable(locationProvider)) {
234 //all location providers unavailbe -> when they come back we resume automatically
235 return QT_CLOSED_ERROR;
236 }
237
238 } catch(Exception e) {
239 e.printStackTrace();
241 }
242
244 }
245 }
246
247 static public void stopUpdates(int androidClassKey)
248 {
249 synchronized (m_syncObject) {
250 try {
251 Log.d(TAG, "Stopping updates");
252 removeActiveListener(androidClassKey);
253 } catch(Exception e) {
254 e.printStackTrace();
255 return;
256 }
257 }
258 }
259
260 static public int requestUpdate(int androidClassKey, int locationProvider, int timeout)
261 {
262 synchronized (m_syncObject) {
263 try {
264 boolean exceptionOccurred = false;
265 QtPositioning positioningListener = new QtPositioning();
266 positioningListener.nativeClassReference = androidClassKey;
267 positioningListener.isSingleUpdate = true;
268 positioningListener.expectedProviders = locationProvider;
269 positioningListener.isSatelliteUpdate = false;
270
271 if ((locationProvider & QT_GPS_PROVIDER) > 0) {
272 Log.d(TAG, "Single update using GPS");
273 try {
274 addActiveListener(positioningListener, LocationManager.GPS_PROVIDER,
275 timeout, 0);
276 } catch (SecurityException se) {
277 se.printStackTrace();
278 exceptionOccurred = true;
279 }
280 }
281
282 if ((locationProvider & QT_NETWORK_PROVIDER) > 0) {
283 Log.d(TAG, "Single update using network");
284 try {
285 addActiveListener(positioningListener, LocationManager.NETWORK_PROVIDER,
286 timeout, 0);
287 } catch (SecurityException se) {
288 se.printStackTrace();
289 exceptionOccurred = true;
290 }
291 }
292 if (exceptionOccurred) {
293 removeActiveListener(positioningListener);
294 return QT_ACCESS_ERROR;
295 }
296
297 if (!expectedProvidersAvailable(locationProvider)) {
298 //all location providers unavailable -> when they come back we resume automatically
299 //in the mean time return ClosedError
300 return QT_CLOSED_ERROR;
301 }
302
303 } catch(Exception e) {
304 e.printStackTrace();
306 }
307
309 }
310 }
311
312 static public int startSatelliteUpdates(int androidClassKey, int updateInterval, boolean isSingleRequest)
313 {
314 synchronized (m_syncObject) {
315 try {
316 boolean exceptionOccurred = false;
317 QtPositioning positioningListener = new QtPositioning();
318 positioningListener.isSatelliteUpdate = true;
319 positioningListener.nativeClassReference = androidClassKey;
320 positioningListener.expectedProviders = 1; //always satellite provider
321 positioningListener.isSingleUpdate = isSingleRequest;
322
323 if (updateInterval == 0)
324 updateInterval = 50; //don't update more often than once per 50ms
325
326 if (isSingleRequest)
327 Log.d(TAG, "Single update for Satellites " + updateInterval);
328 else
329 Log.d(TAG, "Regular updates for Satellites " + updateInterval);
330 try {
331 addActiveListener(positioningListener, LocationManager.GPS_PROVIDER,
332 updateInterval, 0);
333 } catch (SecurityException se) {
334 se.printStackTrace();
335 exceptionOccurred = true;
336 }
337
338 if (exceptionOccurred) {
339 removeActiveListener(positioningListener);
340 return QT_ACCESS_ERROR;
341 }
342
343 if (!expectedProvidersAvailable(positioningListener.expectedProviders)) {
344 //all location providers unavailable -> when they come back we resume automatically
345 //in the mean time return ClosedError
346 return QT_CLOSED_ERROR;
347 }
348
349 } catch(Exception e) {
350 e.printStackTrace();
352 }
353
355 }
356 }
357
359 {
360 // Use GpsStatus for API Level <= 23 (version M and below) and
361 // GnssStatus for other API levels.
362 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
363 looperThread = new PositioningLooperGps();
364 else
365 looperThread = new PositioningLooperGnss();
366 }
367
368 public Looper looper()
369 {
370 return looperThread.looper();
371 }
372
373 private void setActiveLooper(boolean setActive)
374 {
375 try{
376 if (setActive) {
377 if (looperThread.isAlive())
378 return;
379
380 if (isSatelliteUpdate)
381 looperThread.isSatelliteListener(true);
382
383 long start = System.currentTimeMillis();
384 looperThread.start();
385
386 //busy wait but lasts ~20-30 ms only
387 while (!looperThread.isReady());
388
389 long stop = System.currentTimeMillis();
390 Log.d(TAG, "Looper Thread startup time in ms: " + (stop-start));
391 } else {
392 looperThread.quitLooper();
393 }
394 } catch(Exception e) {
395 e.printStackTrace();
396 }
397 }
398
399 private abstract class PositioningLooperBase extends Thread
400 {
401 private boolean looperRunning;
402 private Looper posLooper;
403 private boolean isSatelliteLooper = false;
404
405 abstract protected void addSatelliteInfoListener();
406 abstract protected void removeSatelliteInfoListener();
407
408 private PositioningLooperBase()
409 {
410 looperRunning = false;
411 }
412
413 public void run()
414 {
415 Looper.prepare();
416 Handler handler = new Handler();
417
418 if (isSatelliteLooper)
419 addSatelliteInfoListener();
420
421 posLooper = Looper.myLooper();
422 synchronized (this) {
423 looperRunning = true;
424 }
425 Looper.loop();
426 synchronized (this) {
427 looperRunning = false;
428 }
429 }
430
431 public void quitLooper()
432 {
433 if (isSatelliteLooper)
434 removeSatelliteInfoListener();
435 looper().quit();
436 }
437
438 public synchronized boolean isReady()
439 {
440 return looperRunning;
441 }
442
443 public void isSatelliteListener(boolean isListener)
444 {
445 isSatelliteLooper = isListener;
446 }
447
448 public Looper looper()
449 {
450 return posLooper;
451 }
452
453 }
454
455 private class PositioningLooperGps extends PositioningLooperBase implements GpsStatus.Listener
456 {
457 @Override
458 protected void addSatelliteInfoListener()
459 {
460 try {
461 locationManager.addGpsStatusListener(this);
462 } catch(Exception e) {
463 e.printStackTrace();
464 }
465 }
466
467 @Override
468 protected void removeSatelliteInfoListener()
469 {
470 locationManager.removeGpsStatusListener(this);
471 }
472
473 @Override
474 public void onGpsStatusChanged(int event) {
475 switch (event) {
476 case GpsStatus.GPS_EVENT_FIRST_FIX:
477 break;
478 case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
479 GpsStatus status = locationManager.getGpsStatus(null);
480 Iterable<GpsSatellite> iterable = status.getSatellites();
481 Iterator<GpsSatellite> it = iterable.iterator();
482
483 ArrayList<GpsSatellite> list = new ArrayList<GpsSatellite>();
484 while (it.hasNext()) {
485 GpsSatellite sat = (GpsSatellite) it.next();
486 list.add(sat);
487 }
488 GpsSatellite[] sats = list.toArray(new GpsSatellite[list.size()]);
489 satelliteGpsUpdated(sats, nativeClassReference, isSingleUpdate);
490
491 break;
492 case GpsStatus.GPS_EVENT_STARTED:
493 break;
494 case GpsStatus.GPS_EVENT_STOPPED:
495 break;
496 }
497 }
498 }
499
500 private class PositioningGnssListener extends GnssStatus.Callback
501 {
502 @Override
503 public void onSatelliteStatusChanged(GnssStatus status)
504 {
505 satelliteGnssUpdated(status, nativeClassReference, isSingleUpdate);
506 }
507 }
508
509 private class PositioningLooperGnss extends PositioningLooperBase
510 {
511 private PositioningGnssListener gnssListener;
512
513 private PositioningLooperGnss()
514 {
515 gnssListener = new PositioningGnssListener();
516 }
517
518 @Override
519 protected void addSatelliteInfoListener()
520 {
521 try {
522 locationManager.registerGnssStatusCallback(gnssListener);
523 } catch(Exception e) {
524 e.printStackTrace();
525 }
526 }
527
528 @Override
529 protected void removeSatelliteInfoListener()
530 {
531 locationManager.unregisterGnssStatusCallback(gnssListener);
532 }
533 }
534
535 public static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate);
536 public static native void locationProvidersDisabled(int androidClassKey);
537 public static native void locationProvidersChanged(int androidClassKey);
538 public static native void satelliteGpsUpdated(Object[] update, int androidClassKey, boolean isSingleUpdate);
539 public static native void satelliteGnssUpdated(GnssStatus update, int androidClassKey, boolean isSingleUpdate);
540
541 @Override
542 public void onLocationChanged(Location location) {
543 //Log.d(TAG, "**** Position Update ****: " + location.toString() + " " + isSingleUpdate);
544 if (location == null)
545 return;
546
547 if (isSatelliteUpdate) //we are a QGeoSatelliteInfoSource -> ignore
548 return;
549
550 if (isSingleUpdate || expectedProviders < 3) {
551 positionUpdated(location, nativeClassReference, isSingleUpdate);
552 return;
553 }
554
555 /*
556 We can use GPS and Network, pick the better location provider.
557 Generally we prefer GPS data due to their higher accurancy but we
558 let Network data pass until GPS fix is available
559 */
560
561 if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
562 lastGps = location;
563
564 // assumption: GPS always better -> pass it on
565 positionUpdated(location, nativeClassReference, isSingleUpdate);
566 } else if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
567 lastNetwork = location;
568
569 if (lastGps == null) { //no GPS fix yet use network location
570 positionUpdated(location, nativeClassReference, isSingleUpdate);
571 return;
572 }
573
574 long delta = location.getTime() - lastGps.getTime();
575
576 // Ignore if network update is older than last GPS (delta < 0)
577 // Ignore if gps update still has time to provide next location (delta < updateInterval)
578 if (delta < updateIntervalTime)
579 return;
580
581 // Use network data -> GPS has timed out on updateInterval
582 positionUpdated(location, nativeClassReference, isSingleUpdate);
583 }
584 }
585
586 @Override
587 public void onStatusChanged(String provider, int status, Bundle extras) {}
588
589 @Override
590 public void onProviderEnabled(String provider) {
591 Log.d(TAG, "Enabled provider: " + provider);
592 locationProvidersChanged(nativeClassReference);
593 if (isLocationProvidersDisabledInvoked && expectedProvidersAvailable(expectedProviders))
594 isLocationProvidersDisabledInvoked = false;
595 }
596
597 @Override
598 public void onProviderDisabled(String provider) {
599 Log.d(TAG, "Disabled provider: " + provider);
600 locationProvidersChanged(nativeClassReference);
601 if (!isLocationProvidersDisabledInvoked && !expectedProvidersAvailable(expectedProviders)) {
602 isLocationProvidersDisabledInvoked = true;
603 locationProvidersDisabled(nativeClassReference);
604 }
605 }
606}
Definition main.cpp:8
qsizetype size() const noexcept
Definition qlist.h:386
static native void satelliteGpsUpdated(Object[] update, int androidClassKey, boolean isSingleUpdate)
static int requestUpdate(int androidClassKey, int locationProvider, int timeout)
static native void locationProvidersChanged(int androidClassKey)
static int startUpdates(int androidClassKey, int locationProvider, int updateInterval)
void onStatusChanged(String provider, int status, Bundle extras)
static Location lastKnownPosition(boolean fromSatelliteOnly)
static int startSatelliteUpdates(int androidClassKey, int updateInterval, boolean isSingleRequest)
static native void locationProvidersDisabled(int androidClassKey)
static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate)
static native void satelliteGnssUpdated(GnssStatus update, int androidClassKey, boolean isSingleUpdate)
double e
QSet< QString >::iterator it
auto run(QThreadPool *pool, Function &&f, Args &&...args)
static void * context
#define TAG(x)
GLint location
GLbitfield GLuint64 timeout
[4]
GLuint start
struct _cl_event * event
QT_BEGIN_NAMESPACE typedef void(* Callback)(QQmlNotifierEndpoint *, void **)
QList< int > list
[14]