Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: fused location provider #3447

Merged
merged 4 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,18 @@ if (IOS)
endif ()

if (ANDROID)
set(MM_HDRS ${MM_HDRS} position/tracking/androidtrackingbackend.h
position/tracking/androidtrackingbroadcast.h
set(MM_HDRS
${MM_HDRS}
position/tracking/androidtrackingbackend.h
position/tracking/androidtrackingbroadcast.h
position/providers/androidpositionprovider.h
)

set(MM_SRCS ${MM_SRCS} position/tracking/androidtrackingbackend.cpp
position/tracking/androidtrackingbroadcast.cpp
set(MM_SRCS
${MM_SRCS}
position/tracking/androidtrackingbackend.cpp
position/tracking/androidtrackingbroadcast.cpp
position/providers/androidpositionprovider.cpp
)
endif ()

Expand Down
196 changes: 196 additions & 0 deletions app/android/src/uk/co/lutraconsulting/MMAndroidPosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package uk.co.lutraconsulting;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Looper;
import android.os.Handler;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.Priority;
wonder-sk marked this conversation as resolved.
Show resolved Hide resolved



public class MMAndroidPosition {

static public abstract class Callback {
public void onPositionChanged(@NonNull Location location, GnssStatus gnssStatus) {
}
}

private static native void jniOnPositionUpdated(int instanceId, Location location, GnssStatus gnssStatus);

// find out whether fused provider could be actually used
static public boolean isFusedLocationProviderAvailable(Context context) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
return googleApiAvailability.isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
}

// get more details why FLP is not available (e.g. play services missing, disabled, updating...)
static public String fusedLocationProviderErrorString(Context context) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
return googleApiAvailability.getErrorString(googleApiAvailability.isGooglePlayServicesAvailable(context));
}

// called from C++ code
static public MMAndroidPosition createWithJniCallback(Context context, boolean useFused, int instanceId) {
Log.i("CPP", "[java] createWithJniCallback");

MMAndroidPosition.Callback callback = new MMAndroidPosition.Callback() {
@Override
public void onPositionChanged(@NonNull Location location, GnssStatus gnssStatus) {
jniOnPositionUpdated(instanceId, location, gnssStatus);
}
};

return new MMAndroidPosition(context, callback, useFused);
}

private final Context mContext;
private final LocationManager mLocationManager;
private final boolean mUseFused;
private FusedLocationProviderClient mFusedLocationClient = null;
private final LocationCallback mLocationCallback;
private final LocationListener mLocationManagerCallback;
private final GnssStatus.Callback mGnssStatusCallback;
private final MMAndroidPosition.Callback mClientCallback;
private boolean mFusedAvailable = false;
private boolean mGpsProviderAvailable = false;
private boolean mIsStarted = false;
private String mErrorMessage;
private GnssStatus mLastGnssStatus;

public MMAndroidPosition(Context context, MMAndroidPosition.Callback clientCallback, boolean useFused) {
mContext = context;
mClientCallback = clientCallback;
mUseFused = useFused;

Log.i("CPP", "[java] constructor!");

mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

if (mUseFused) {
mFusedAvailable = isFusedLocationProviderAvailable(context);
Log.i("CPP", "[java] fused available: " + mFusedAvailable);
if (mFusedAvailable) {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
}
wonder-sk marked this conversation as resolved.
Show resolved Hide resolved
} else {
mGpsProviderAvailable = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
Log.i("CPP", "[java] gps provider available: " + mGpsProviderAvailable);
}

mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(@NonNull LocationResult locationResult) {
for (Location location : locationResult.getLocations()) {
Log.i("CPP", "[java] FLP " + location.getLatitude() + " " + location.getLongitude());

// call the native function!
mClientCallback.onPositionChanged(location, mLastGnssStatus);
}
}
};

mGnssStatusCallback = new GnssStatus.Callback() {
@Override
public void onSatelliteStatusChanged(@NonNull GnssStatus status) {

// store the satellite info
mLastGnssStatus = status;
}
};

mLocationManagerCallback = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
Log.i("CPP", "[java] GPS " + location.getLatitude() + " " + location.getLongitude());
wonder-sk marked this conversation as resolved.
Show resolved Hide resolved

mClientCallback.onPositionChanged(location, mLastGnssStatus);
}
};

Log.i("CPP", "[java] constructor end");

}

public String errorMessage() {
return mErrorMessage;
}

public boolean start() {
Log.i("CPP", "[java] start()");

if (mIsStarted)
return false;

if (mUseFused && !mFusedAvailable) {
mErrorMessage = "FUSED_NOT_AVAILABLE";
Log.e("CPP", "[java] FUSED_NOT_AVAILABLE");
return false;
}

if (!mUseFused && !mGpsProviderAvailable) {
mErrorMessage = "GPS_NOT_AVAILABLE";
Log.e("CPP", "[java] GPS_NOT_AVAILABLE");
return false;
}

if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
mErrorMessage = "MISSING_PERMISSIONS";
Log.e("CPP", "[java] MISSING_PERMISSIONS");
return false;
}

if (mUseFused) {
LocationRequest locationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 1000).build();

mFusedLocationClient.requestLocationUpdates(locationRequest, mLocationCallback, Looper.getMainLooper());
}
else {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000L, 0.F, mLocationManagerCallback, Looper.getMainLooper());
}

mLocationManager.registerGnssStatusCallback(mGnssStatusCallback, new Handler(Looper.getMainLooper()));

Log.i("CPP", "[java] started!");

mIsStarted = true;
return true;
}

public boolean stop() {
Log.i("CPP", "[java] stop()");

if (!mIsStarted)
return false;

if (mUseFused) {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
} else {
mLocationManager.removeUpdates(mLocationManagerCallback);
}

mLocationManager.unregisterGnssStatusCallback(mGnssStatusCallback);

Log.i("CPP", "[java] stopped!");

mIsStarted = false;
return true;
}
}
32 changes: 26 additions & 6 deletions app/position/positionkit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

#include "position/providers/internalpositionprovider.h"
#include "position/providers/simulatedpositionprovider.h"
#ifdef ANDROID
#include "position/providers/androidpositionprovider.h"
#include <android/log.h>
#endif

#include "appsettings.h"
#include "inpututils.h"
Expand Down Expand Up @@ -105,6 +109,25 @@ AbstractPositionProvider *PositionKit::constructProvider( const QString &type, c
QQmlEngine::setObjectOwnership( provider, QQmlEngine::CppOwnership );
return provider;
}
#ifdef ANDROID
else if ( id == QStringLiteral( "android_fused" ) || id == QStringLiteral( "android_gps" ) )
{
bool fused = ( id == QStringLiteral( "android_fused" ) );
if ( fused && !AndroidPositionProvider::isFusedAvailable() )
{
// TODO: inform user + use AndroidPositionProvider::fusedErrorString() output?
tomasMizera marked this conversation as resolved.
Show resolved Hide resolved

// fallback to the default - at this point the Qt Positioning implementation
AbstractPositionProvider *provider = new InternalPositionProvider();
QQmlEngine::setObjectOwnership( provider, QQmlEngine::CppOwnership );
return provider;
}
__android_log_print( ANDROID_LOG_INFO, "CPP", "MAKE PROVIDER %d", fused );
AbstractPositionProvider *provider = new AndroidPositionProvider( fused );
QQmlEngine::setObjectOwnership( provider, QQmlEngine::CppOwnership );
return provider;
}
#endif
else // id == devicegps
{
AbstractPositionProvider *provider = new InternalPositionProvider();
Expand Down Expand Up @@ -132,13 +155,10 @@ AbstractPositionProvider *PositionKit::constructActiveProvider( AppSettings *app
return constructProvider( QStringLiteral( "internal" ), QStringLiteral( "simulated" ) );
}
}
else if ( providerId == QStringLiteral( "devicegps" ) )
{
return constructProvider( QStringLiteral( "internal" ), QStringLiteral( "devicegps" ) );
}
else if ( providerId == QStringLiteral( "simulated" ) )
else if ( providerId == QStringLiteral( "devicegps" ) || providerId == QStringLiteral( "simulated" ) ||
providerId == QStringLiteral( "android_fused" ) || providerId == QStringLiteral( "android_gps" ) )
{
return constructProvider( QStringLiteral( "internal" ), QStringLiteral( "simulated" ) );
return constructProvider( QStringLiteral( "internal" ), providerId );
}
else
{
Expand Down
Loading
Loading