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