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
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
26class 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 static final int QT_GPS_PROVIDER = 1;
46 static final int QT_NETWORK_PROVIDER = 2;
47
48 /* The following values must match the corresponding error enums in the Qt API*/
49 static final int QT_ACCESS_ERROR = 0;
50 static final int QT_CLOSED_ERROR = 1;
51 static final int QT_POSITION_UNKNOWN_SOURCE_ERROR = 2;
52 static final int QT_POSITION_NO_ERROR = 3;
53 static final int QT_SATELLITE_NO_ERROR = 2;
54 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 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 (location.hasMslAltitude()) // Nothing to be done
116 return;
117 if (altitudeConverter == null)
118 altitudeConverter = new AltitudeConverter();
119 try {
120 altitudeConverter.addMslAltitudeToLocation(appContext, location);
121 } catch (Exception e) {
122 e.printStackTrace();
123 }
124 }
125 }
126
127 static Location lastKnownPosition(boolean fromSatelliteOnly,
128 boolean useAltitudeConverter)
129 {
130 Location gps = null;
131 Location network = null;
132 try {
133 gps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
134 if (useAltitudeConverter)
135 addMslAltitude(gps);
136 } catch (Exception e) {
137 // This can throw if we only use ACCESS_COARSE_LOCATION. However,
138 // if we didn't set fromSatelliteOnly to true, that is not an error.
139 if (fromSatelliteOnly)
140 e.printStackTrace();
141 gps = null;
142 }
143 if (!fromSatelliteOnly) {
144 try {
145 network = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
146 if (useAltitudeConverter)
147 addMslAltitude(network);
148 } catch(Exception e) {
149 e.printStackTrace();
150 network = null;
151 }
152 }
153
154 if (gps != null && network != null) {
155 //we return the most recent location but slightly prefer GPS
156 //prefer GPS if it is max 4 hrs older than network
157 long delta = network.getTime() - gps.getTime();
158 if (delta < 4*60*60*1000) {
159 return gps;
160 } else {
161 return network;
162 }
163 } else if (gps != null ) {
164 return gps;
165 } else if (network != null) {
166 return network;
167 }
168
169 return null;
170 }
171
172 /* Returns true if at least on of the given providers is enabled. */
173 static private boolean expectedProvidersAvailable(int desiredProviders)
174 {
175 List<String> enabledProviders = locationManager.getProviders(true);
176 if ((desiredProviders & QT_GPS_PROVIDER) > 0) { //gps desired
177 if (enabledProviders.contains(LocationManager.GPS_PROVIDER)) {
178 return true;
179 }
180 }
181 if ((desiredProviders & QT_NETWORK_PROVIDER) > 0) { //network desired
182 if (enabledProviders.contains(LocationManager.NETWORK_PROVIDER)) {
183 return true;
184 }
185 }
186
187 return false;
188 }
189
190
191 static private void addActiveListener(QtPositioning listener, String provider, long minTime, float minDistance)
192 {
193 int androidClassKey = listener.nativeClassReference;
194 //start update thread
195 listener.setActiveLooper(true);
196
197 if (runningListeners.containsKey(androidClassKey) && runningListeners.get(androidClassKey) != listener) {
198 removeActiveListener(androidClassKey);
199 }
200
201 locationManager.requestLocationUpdates(provider,
202 minTime, minDistance,
203 listener,
204 listener.looper());
205
206 runningListeners.put(androidClassKey, listener);
207 }
208
209
210 static private void removeActiveListener(QtPositioning listener)
211 {
212 removeActiveListener(listener.nativeClassReference);
213 }
214
215
216 static private void removeActiveListener(int androidClassKey)
217 {
218 QtPositioning listener = runningListeners.remove(androidClassKey);
219
220 if (listener != null) {
221 locationManager.removeUpdates(listener);
222 listener.setActiveLooper(false);
223 }
224 }
225
226
227 static int startUpdates(int androidClassKey, int locationProvider, int updateInterval,
228 boolean useAltitudeConverter)
229 {
230 synchronized (m_syncObject) {
231 try {
232 boolean providerStarted = false;
233 SecurityException lastException = null;
234 QtPositioning positioningListener = new QtPositioning();
235 positioningListener.nativeClassReference = androidClassKey;
236 positioningListener.expectedProviders = locationProvider;
237 positioningListener.isSatelliteUpdate = false;
238 positioningListener.useAltitudeConverter = useAltitudeConverter;
239
240 if (updateInterval == 0)
241 updateInterval = 50; //don't update more often than once per 50ms
242
243 positioningListener.updateIntervalTime = updateInterval;
244 if ((locationProvider & QT_GPS_PROVIDER) > 0) {
245 Log.d(TAG, "Regular updates using GPS " + updateInterval);
246 try {
247 addActiveListener(positioningListener,
248 LocationManager.GPS_PROVIDER,
249 updateInterval, 0);
250 providerStarted = true;
251 } catch (SecurityException se) {
252 lastException = se;
253 }
254 }
255
256 if ((locationProvider & QT_NETWORK_PROVIDER) > 0) {
257 Log.d(TAG, "Regular updates using network " + updateInterval);
258 try {
259 addActiveListener(positioningListener,
260 LocationManager.NETWORK_PROVIDER,
261 updateInterval, 0);
262 providerStarted = true;
263 } catch (SecurityException se) {
264 lastException = se;
265 }
266 }
267 // The preferred positioning methods and the granted permissions
268 // might not match, so raise an error only if none of the
269 // requested providers could be started.
270 if (!providerStarted && lastException != null) {
271 lastException.printStackTrace();
272 removeActiveListener(positioningListener);
273 return QT_ACCESS_ERROR;
274 }
275
276 if (!expectedProvidersAvailable(locationProvider)) {
277 //all location providers unavailbe -> when they come back we resume automatically
278 return QT_CLOSED_ERROR;
279 }
280
281 } catch(Exception e) {
282 e.printStackTrace();
283 return QT_POSITION_UNKNOWN_SOURCE_ERROR;
284 }
285
286 return QT_POSITION_NO_ERROR;
287 }
288 }
289
290 static void stopUpdates(int androidClassKey)
291 {
292 synchronized (m_syncObject) {
293 try {
294 Log.d(TAG, "Stopping updates");
295 removeActiveListener(androidClassKey);
296 } catch(Exception e) {
297 e.printStackTrace();
298 return;
299 }
300 }
301 }
302
303 static int requestUpdate(int androidClassKey, int locationProvider, int timeout,
304 boolean useAltitudeConverter)
305 {
306 synchronized (m_syncObject) {
307 try {
308 boolean providerStarted = false;
309 SecurityException lastException = null;
310 QtPositioning positioningListener = new QtPositioning();
311 positioningListener.nativeClassReference = androidClassKey;
312 positioningListener.isSingleUpdate = true;
313 positioningListener.expectedProviders = locationProvider;
314 positioningListener.isSatelliteUpdate = false;
315 positioningListener.useAltitudeConverter = useAltitudeConverter;
316
317 if ((locationProvider & QT_GPS_PROVIDER) > 0) {
318 Log.d(TAG, "Single update using GPS");
319 try {
320 addActiveListener(positioningListener, LocationManager.GPS_PROVIDER,
321 timeout, 0);
322 providerStarted = true;
323 } catch (SecurityException se) {
324 lastException = se;
325 }
326 }
327
328 if ((locationProvider & QT_NETWORK_PROVIDER) > 0) {
329 Log.d(TAG, "Single update using network");
330 try {
331 addActiveListener(positioningListener, LocationManager.NETWORK_PROVIDER,
332 timeout, 0);
333 providerStarted = true;
334 } catch (SecurityException se) {
335 lastException = se;
336 }
337 }
338 // The preferred positioning methods and the granted permissions
339 // might not match, so raise an error only if none of the
340 // requested providers could be started.
341 if (!providerStarted && lastException != null) {
342 lastException.printStackTrace();
343 removeActiveListener(positioningListener);
344 return QT_ACCESS_ERROR;
345 }
346
347 if (!expectedProvidersAvailable(locationProvider)) {
348 //all location providers unavailable -> when they come back we resume automatically
349 //in the mean time return ClosedError
350 return QT_CLOSED_ERROR;
351 }
352
353 } catch(Exception e) {
354 e.printStackTrace();
355 return QT_POSITION_UNKNOWN_SOURCE_ERROR;
356 }
357
358 return QT_POSITION_NO_ERROR;
359 }
360 }
361
362 static int startSatelliteUpdates(int androidClassKey, int updateInterval, boolean isSingleRequest)
363 {
364 synchronized (m_syncObject) {
365 try {
366 boolean exceptionOccurred = false;
367 QtPositioning positioningListener = new QtPositioning();
368 positioningListener.isSatelliteUpdate = true;
369 positioningListener.nativeClassReference = androidClassKey;
370 positioningListener.expectedProviders = 1; //always satellite provider
371 positioningListener.isSingleUpdate = isSingleRequest;
372
373 if (updateInterval == 0)
374 updateInterval = 50; //don't update more often than once per 50ms
375
376 if (isSingleRequest)
377 Log.d(TAG, "Single update for Satellites " + updateInterval);
378 else
379 Log.d(TAG, "Regular updates for Satellites " + updateInterval);
380 try {
381 addActiveListener(positioningListener, LocationManager.GPS_PROVIDER,
382 updateInterval, 0);
383 } catch (SecurityException se) {
384 se.printStackTrace();
385 exceptionOccurred = true;
386 }
387
388 if (exceptionOccurred) {
389 removeActiveListener(positioningListener);
390 return QT_ACCESS_ERROR;
391 }
392
393 if (!expectedProvidersAvailable(positioningListener.expectedProviders)) {
394 //all location providers unavailable -> when they come back we resume automatically
395 //in the mean time return ClosedError
396 return QT_CLOSED_ERROR;
397 }
398
399 } catch(Exception e) {
400 e.printStackTrace();
401 return QT_SATELLITE_UNKNOWN_SOURCE_ERROR;
402 }
403
404 return QT_SATELLITE_NO_ERROR;
405 }
406 }
407
408 QtPositioning()
409 {
410 // Use GpsStatus for API Level <= 23 (version M and below) and
411 // GnssStatus for other API levels.
412 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
413 looperThread = new PositioningLooperGps();
414 else
415 looperThread = new PositioningLooperGnss();
416 }
417
418 Looper looper()
419 {
420 return looperThread.looper();
421 }
422
423 private void setActiveLooper(boolean setActive)
424 {
425 try{
426 if (setActive) {
427 if (looperThread.isAlive())
428 return;
429
430 if (isSatelliteUpdate)
431 looperThread.isSatelliteListener(true);
432
433 long start = System.currentTimeMillis();
434 looperThread.start();
435
436 //busy wait but lasts ~20-30 ms only
437 while (!looperThread.isReady());
438
439 long stop = System.currentTimeMillis();
440 Log.d(TAG, "Looper Thread startup time in ms: " + (stop-start));
441 } else {
442 looperThread.quitLooper();
443 }
444 } catch(Exception e) {
445 e.printStackTrace();
446 }
447 }
448
449 private abstract class PositioningLooperBase extends Thread
450 {
451 private boolean looperRunning;
452 private Looper posLooper;
453 private boolean isSatelliteLooper = false;
454
455 abstract protected void addSatelliteInfoListener();
456 abstract protected void removeSatelliteInfoListener();
457
458 private PositioningLooperBase()
459 {
460 looperRunning = false;
461 }
462
463 @Override
464 public void run()
465 {
466 Looper.prepare();
467
468 if (isSatelliteLooper)
469 addSatelliteInfoListener();
470
471 posLooper = Looper.myLooper();
472 synchronized (this) {
473 looperRunning = true;
474 }
475 Looper.loop();
476 synchronized (this) {
477 looperRunning = false;
478 }
479 }
480
481 void quitLooper()
482 {
483 if (isSatelliteLooper)
484 removeSatelliteInfoListener();
485 looper().quit();
486 }
487
488 synchronized boolean isReady()
489 {
490 return looperRunning;
491 }
492
493 void isSatelliteListener(boolean isListener)
494 {
495 isSatelliteLooper = isListener;
496 }
497
498 Looper looper()
499 {
500 return posLooper;
501 }
502
503 }
504
505 private class PositioningLooperGps extends PositioningLooperBase implements GpsStatus.Listener
506 {
507 @Override
508 protected void addSatelliteInfoListener()
509 {
510 try {
511 locationManager.addGpsStatusListener(this);
512 } catch(Exception e) {
513 e.printStackTrace();
514 }
515 }
516
517 @Override
518 protected void removeSatelliteInfoListener()
519 {
520 locationManager.removeGpsStatusListener(this);
521 }
522
523 @Override
524 public void onGpsStatusChanged(int event) {
525 switch (event) {
526 case GpsStatus.GPS_EVENT_FIRST_FIX:
527 break;
528 case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
529 GpsStatus status = locationManager.getGpsStatus(null);
530 Iterable<GpsSatellite> iterable = status.getSatellites();
531 Iterator<GpsSatellite> it = iterable.iterator();
532
533 ArrayList<GpsSatellite> list = new ArrayList<GpsSatellite>();
534 while (it.hasNext()) {
535 GpsSatellite sat = it.next();
536 list.add(sat);
537 }
538 GpsSatellite[] sats = list.toArray(new GpsSatellite[list.size()]);
539 satelliteGpsUpdated(sats, nativeClassReference, isSingleUpdate);
540
541 break;
542 case GpsStatus.GPS_EVENT_STARTED:
543 break;
544 case GpsStatus.GPS_EVENT_STOPPED:
545 break;
546 }
547 }
548 }
549
550 private class PositioningGnssListener extends GnssStatus.Callback
551 {
552 @Override
553 public void onSatelliteStatusChanged(GnssStatus status)
554 {
555 satelliteGnssUpdated(status, nativeClassReference, isSingleUpdate);
556 }
557 }
558
559 private class PositioningLooperGnss extends PositioningLooperBase
560 {
561 private PositioningGnssListener gnssListener;
562
563 private PositioningLooperGnss()
564 {
565 gnssListener = new PositioningGnssListener();
566 }
567
568 @Override
569 protected void addSatelliteInfoListener()
570 {
571 try {
572 locationManager.registerGnssStatusCallback(gnssListener);
573 } catch(Exception e) {
574 e.printStackTrace();
575 }
576 }
577
578 @Override
579 protected void removeSatelliteInfoListener()
580 {
581 locationManager.unregisterGnssStatusCallback(gnssListener);
582 }
583 }
584
585 static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate);
586 static native void locationProvidersDisabled(int androidClassKey);
587 static native void locationProvidersChanged(int androidClassKey);
588 static native void satelliteGpsUpdated(Object[] update, int androidClassKey, boolean isSingleUpdate);
589 static native void satelliteGnssUpdated(GnssStatus update, int androidClassKey, boolean isSingleUpdate);
590
591 @Override
592 public void onLocationChanged(Location location) {
593 //Log.d(TAG, "**** Position Update ****: " + location.toString() + " " + isSingleUpdate);
594 if (location == null)
595 return;
596
597 if (useAltitudeConverter)
598 addMslAltitude(location);
599
600 if (isSatelliteUpdate) //we are a QGeoSatelliteInfoSource -> ignore
601 return;
602
603 if (isSingleUpdate || expectedProviders < 3) {
604 positionUpdated(location, nativeClassReference, isSingleUpdate);
605 return;
606 }
607
608 /*
609 We can use GPS and Network, pick the better location provider.
610 Generally we prefer GPS data due to their higher accurancy but we
611 let Network data pass until GPS fix is available
612 */
613
614 if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
615 lastGps = location;
616
617 // assumption: GPS always better -> pass it on
618 positionUpdated(location, nativeClassReference, isSingleUpdate);
619 } else if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
620 lastNetwork = location;
621
622 if (lastGps == null) { //no GPS fix yet use network location
623 positionUpdated(location, nativeClassReference, isSingleUpdate);
624 return;
625 }
626
627 long delta = location.getTime() - lastGps.getTime();
628
629 // Ignore if network update is older than last GPS (delta < 0)
630 // Ignore if gps update still has time to provide next location (delta < updateInterval)
631 if (delta < updateIntervalTime)
632 return;
633
634 // Use network data -> GPS has timed out on updateInterval
635 positionUpdated(location, nativeClassReference, isSingleUpdate);
636 }
637 }
638
639 @Override
640 public void onStatusChanged(String provider, int status, Bundle extras) {}
641
642 @Override
643 public void onProviderEnabled(String provider) {
644 Log.d(TAG, "Enabled provider: " + provider);
645 locationProvidersChanged(nativeClassReference);
646 if (isLocationProvidersDisabledInvoked && expectedProvidersAvailable(expectedProviders))
647 isLocationProvidersDisabledInvoked = false;
648 }
649
650 @Override
651 public void onProviderDisabled(String provider) {
652 Log.d(TAG, "Disabled provider: " + provider);
653 locationProvidersChanged(nativeClassReference);
654 if (!isLocationProvidersDisabledInvoked && !expectedProvidersAvailable(expectedProviders)) {
655 isLocationProvidersDisabledInvoked = true;
656 locationProvidersDisabled(nativeClassReference);
657 }
658 }
659}
The Location class provides a way to mark a location in a file.
Definition location.h:20
QPainter Context
static const QString context()
Definition java.cpp:398
static void positionUpdated(JNIEnv *env, jobject thiz, QtJniTypes::Location location, jint androidClassKey, jboolean isSingleUpdate)
static void satelliteGpsUpdated(JNIEnv *env, jobject thiz, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate)
static void satelliteGnssUpdated(JNIEnv *env, jobject thiz, QtJniTypes::GnssStatus gnssStatus, jint androidClassKey, jboolean isSingleUpdate)
static void locationProvidersChanged(JNIEnv *env, jobject thiz, jint androidClassKey)
static void locationProvidersDisabled(JNIEnv *env, jobject thiz, jint androidClassKey)
QMap< Name, StatePointer > Bundle
Definition lalr.h:46
[vector_of_multirole_objects_0]
Definition main.cpp:188
#define TAG(x)
GLuint start
GLbitfield GLuint64 timeout
GLint location
struct _cl_event * event
QT_BEGIN_NAMESPACE typedef void(* Callback)(QQmlNotifierEndpoint *, void **)
QList< int > list
[14]
QJSValueIterator it(object)