From 46afc9b64286ea5f01de7fcdece0588f41e6e634 Mon Sep 17 00:00:00 2001 From: Roman Tsisyk Date: Sun, 6 Feb 2022 13:46:03 +0300 Subject: [PATCH] [android] Update GoogleFusedLocationProvider to use the latest API Signed-off-by: Roman Tsisyk --- android/build.gradle | 2 +- .../location/GoogleFusedLocationProvider.java | 201 +++++++----------- .../maps/location/BaseLocationProvider.java | 2 +- 3 files changed, 84 insertions(+), 121 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index fe08dc3da3..e59ac7bbe6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -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 diff --git a/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java b/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java index e66f9ccf46..3eed438c18 100644 --- a/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java +++ b/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java @@ -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 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() - { - @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() + "'"); + } + } } } diff --git a/android/src/com/mapswithme/maps/location/BaseLocationProvider.java b/android/src/com/mapswithme/maps/location/BaseLocationProvider.java index 179caef083..89b5d07a7a 100644 --- a/android/src/com/mapswithme/maps/location/BaseLocationProvider.java +++ b/android/src/com/mapswithme/maps/location/BaseLocationProvider.java @@ -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()