[android] Update GoogleFusedLocationProvider to use the latest API

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
This commit is contained in:
Roman Tsisyk 2022-02-06 13:46:03 +03:00
parent 5e98aa3042
commit 46afc9b642
3 changed files with 84 additions and 121 deletions

View file

@ -72,7 +72,7 @@ dependencies {
// Google Mobile Services
if (googleMobileServicesEnabled) {
implementation 'com.google.android.gms:play-services-location:18.0.0'
implementation 'com.google.android.gms:play-services-location:19.0.1'
}
// Google Firebase Services

View file

@ -2,165 +2,128 @@ package com.mapswithme.maps.location;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.os.Looper;
import androidx.annotation.NonNull;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationAvailability;
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.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.location.SettingsClient;
class GoogleFusedLocationProvider extends BaseLocationProvider
implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener
{
private final static String TAG = GoogleFusedLocationProvider.class.getSimpleName();
private final GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private PendingResult<LocationSettingsResult> mLocationSettingsResult;
@NonNull
private final FusedLocationProviderClient mFusedLocationClient;
@NonNull
private final SettingsClient mSettingsClient;
private class GoogleLocationListener extends BaseLocationListener implements com.google.android.gms.location.LocationListener
private class GoogleLocationCallback extends LocationCallback
{
private GoogleLocationListener(@NonNull LocationFixChecker locationFixChecker) {super(locationFixChecker);}
@Override
public void onLocationResult(@NonNull LocationResult result)
{
final Location location = result.getLastLocation();
LOGGER.d(TAG, "onLocationResult, location = " + location);
if (location == null)
return;
onLocationChanged(location);
}
@Override
public void onLocationAvailability(@NonNull LocationAvailability availability)
{
LOGGER.d(TAG, "Location is " + (availability.isLocationAvailable() ? "available" : "unavailable"));
setActive(availability.isLocationAvailable());
}
}
@NonNull
private final GoogleLocationListener mListener;
private final GoogleLocationCallback mCallback;
GoogleFusedLocationProvider(@NonNull LocationFixChecker locationFixChecker,
@NonNull Context context)
GoogleFusedLocationProvider(@NonNull LocationFixChecker locationFixChecker, @NonNull Context context)
{
super(locationFixChecker);
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mListener = new GoogleLocationListener(locationFixChecker);
mCallback = new GoogleLocationCallback();
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
mSettingsClient = LocationServices.getSettingsClient(context);
}
@SuppressWarnings("MissingPermission")
// A permission is checked externally
@Override
protected void start()
{
LOGGER.d(TAG, "Google fused provider is started");
if (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting())
{
setActive(true);
if (isActive())
return;
}
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LOGGER.d(TAG, "Starting");
setActive(true);
final LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
long interval = LocationHelper.INSTANCE.getInterval();
mLocationRequest.setInterval(interval);
locationRequest.setInterval(interval);
LOGGER.d(TAG, "Request Google fused provider to provide locations at this interval = "
+ interval + " ms");
mLocationRequest.setFastestInterval(interval / 2);
locationRequest.setFastestInterval(interval / 2);
mGoogleApiClient.connect();
setActive(true);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(locationRequest);
final LocationSettingsRequest locationSettingsRequest = builder.build();
mSettingsClient.checkLocationSettings(locationSettingsRequest).addOnSuccessListener(locationSettingsResponse -> {
LOGGER.d(TAG, "Service is available");
mFusedLocationClient.requestLocationUpdates(locationRequest, mCallback, Looper.myLooper());
LocationHelper.INSTANCE.startSensors();
}).addOnFailureListener(e -> {
setActive(false);
LOGGER.e(TAG, "Service is not available: " + e);
LocationHelper.INSTANCE.initNativeProvider();
LocationHelper.INSTANCE.start();
});
// onLocationResult() may not always be called regularly, however the device location is known.
mFusedLocationClient.getLastLocation().addOnSuccessListener(location -> {
LOGGER.d(TAG, "onLastLocation, location = " + location);
if (location == null)
return;
GoogleFusedLocationProvider.this.onLocationChanged(location);
});
}
@Override
protected void stop()
{
LOGGER.d(TAG, "Google fused provider is stopped");
if (mGoogleApiClient.isConnected())
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, mListener);
LOGGER.d(TAG, "Stopping");
mFusedLocationClient.removeLocationUpdates(mCallback);
if (mLocationSettingsResult != null && !mLocationSettingsResult.isCanceled())
mLocationSettingsResult.cancel();
mGoogleApiClient.disconnect();
setActive(false);
}
@Override
public void onConnected(Bundle bundle)
private void onLocationChanged(@NonNull Location location)
{
LOGGER.d(TAG, "Fused onConnected. Bundle " + bundle);
checkSettingsAndRequestUpdates();
}
private void checkSettingsAndRequestUpdates()
{
LOGGER.d(TAG, "checkSettingsAndRequestUpdates()");
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true); // hides 'never' button in resolve dialog afterwards.
mLocationSettingsResult = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
mLocationSettingsResult.setResultCallback(new ResultCallback<LocationSettingsResult>()
{
@Override
public void onResult(@NonNull LocationSettingsResult locationSettingsResult)
{
final Status status = locationSettingsResult.getStatus();
LOGGER.d(TAG, "onResult status: " + status);
switch (status.getStatusCode())
{
case LocationSettingsStatusCodes.SUCCESS:
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
setActive(false);
// Location settings are not satisfied. AndroidNativeProvider should be used.
resolveResolutionRequired();
return;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way to fix the settings so we won't show the dialog.
setActive(false);
break;
}
requestLocationUpdates();
}
});
}
private static void resolveResolutionRequired()
{
LOGGER.d(TAG, "resolveResolutionRequired()");
LocationHelper.INSTANCE.initNativeProvider();
LocationHelper.INSTANCE.start();
}
@SuppressWarnings("MissingPermission")
// A permission is checked externally
private void requestLocationUpdates()
{
if (!mGoogleApiClient.isConnected())
if (!mLocationFixChecker.isAccuracySatisfied(location))
return;
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mListener);
LocationHelper.INSTANCE.startSensors();
Location last = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (last != null)
mListener.onLocationChanged(last);
}
@Override
public void onConnectionSuspended(int i)
{
setActive(false);
LOGGER.d(TAG, "Fused onConnectionSuspended. Code " + i);
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
{
setActive(false);
LOGGER.d(TAG, "Fused onConnectionFailed. Fall back to native provider. ConnResult " + connectionResult);
// TODO handle error in a smarter way
LocationHelper.INSTANCE.initNativeProvider();
LocationHelper.INSTANCE.start();
if (mLocationFixChecker.isLocationBetterThanLast(location))
{
LocationHelper.INSTANCE.onLocationUpdated(location);
LocationHelper.INSTANCE.notifyLocationUpdated();
}
else
{
Location last = LocationHelper.INSTANCE.getSavedLocation();
if (last != null)
{
LOGGER.d(TAG, "The new location from '" + location.getProvider()
+ "' is worse than the last one from '" + last.getProvider() + "'");
}
}
}
}

View file

@ -10,7 +10,7 @@ abstract class BaseLocationProvider
static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.LOCATION);
private static final String TAG = BaseLocationProvider.class.getSimpleName();
@NonNull
private final LocationFixChecker mLocationFixChecker;
protected final LocationFixChecker mLocationFixChecker;
private boolean mActive;
@NonNull
LocationFixChecker getLocationFixChecker()