diff --git a/build.gradle b/build.gradle index c0a481cebc..4b10b38dbd 100644 --- a/build.gradle +++ b/build.gradle @@ -77,7 +77,6 @@ dependencies { } implementation 'com.google.android.gms:play-services-maps:16.1.0' - implementation 'com.google.android.gms:play-services-location:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1' @@ -191,7 +190,6 @@ dependencyVerification { 'androidx.lifecycle:lifecycle-common-java8:9edc2d4f589656d470ef03b9c6ece62d335971294b033ec7d9ceb6e361e9aafa', 'com.google.firebase:firebase-messaging:e42288e7950d7d3b033d3395a5ac9365d230da3e439a2794ec13e2ef0fbaf078', 'com.google.android.gms:play-services-maps:ff50cae9e4059416202375597d99cdc8ddefd9cea3f1dc2ff53779a3a12eb480', - 'com.google.android.gms:play-services-location:240a0fcb9e8e58586e38ea43b69c09ed6e89ea9a0c69770b7634d81dabf5f3a0', 'com.google.android.gms:play-services-auth:aec9e1c584d442cb9f59481a50b2c66dc191872607c04d97ecb82dd0eb5149ec', 'com.google.android.exoplayer:exoplayer-ui:7a942afcc402ff01e9bf48e8d3942850986710f06562d50a1408aaf04a683151', 'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0', @@ -223,7 +221,6 @@ dependencyVerification { 'com.google.firebase:firebase-iid-interop:2a86322b9346fd4836219206d249e85803311655e96036a8e4b714ce7e79693b', 'com.google.android.gms:play-services-base:aca10c780c3219bc50f3db06734f4ab88badd3113c564c0a3156ff8ff674655b', 'com.google.android.gms:play-services-tasks:b31c18d8d1cc8d9814f295ee7435471333f370ba5bd904ca14f8f2bec4f35c35', - 'com.google.android.gms:play-services-places-placereport:04f8baeb1f8f8a734c7d4b1701a3974281b45591affa7e963b59dd019b8abc6e', 'com.google.android.gms:play-services-stats:5b2d8281adbfd6e74d2295c94bab9ea80fc9a84dfbb397995673f5af4d4c6368', 'com.google.android.gms:play-services-basement:e08bfd1e87c4e50ef76161d7ac76b873aeb975367eeb3afa4abe62ea1887c7c6', 'androidx.legacy:legacy-support-v4:78fec1485f0f388a4749022dd51416857127cd2544ae1c3fd0b16589055480b0', diff --git a/src/org/thoughtcrime/securesms/maps/LocationRetriever.java b/src/org/thoughtcrime/securesms/maps/LocationRetriever.java new file mode 100644 index 0000000000..9b7adf8471 --- /dev/null +++ b/src/org/thoughtcrime/securesms/maps/LocationRetriever.java @@ -0,0 +1,115 @@ +package org.thoughtcrime.securesms.maps; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; + +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.ServiceUtil; + +/** + * A lifecycle-safe way to retrieve a single location update. If a cached location is available, + * we'll use that. Otherwise we'll listen for one. + */ +class LocationRetriever implements DefaultLifecycleObserver, LocationListener { + + private static final String TAG = Log.tag(LocationRetriever.class); + + private final Context context; + private final LocationManager locationManager; + private final SuccessListener successListener; + private final FailureListener failureListener; + + LocationRetriever(@NonNull Context context, @NonNull LifecycleOwner lifecycleOwner, @NonNull SuccessListener successListener, @NonNull FailureListener failureListener) { + this.context = context; + this.locationManager = ServiceUtil.getLocationManager(context); + this.successListener = successListener; + this.failureListener = failureListener; + + lifecycleOwner.getLifecycle().addObserver(this); + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) + { + Log.w(TAG, "No location permission!"); + failureListener.onFailure(); + } + + LocationProvider provider = locationManager.getProvider(LocationManager.GPS_PROVIDER); + + if (provider == null) { + Log.w(TAG, "GPS provider is null. Trying network provider."); + provider = locationManager.getProvider(LocationManager.NETWORK_PROVIDER); + } + + if (provider == null) { + Log.w(TAG, "Network provider is null. Unable to retrieve location."); + failureListener.onFailure(); + return; + } + + Location lastKnown = locationManager.getLastKnownLocation(provider.getName()); + + if (lastKnown != null) { + Log.i(TAG, "Using last known location."); + successListener.onSuccess(lastKnown); + } else { + Log.i(TAG, "No last known location. Requesting a single update."); + locationManager.requestSingleUpdate(provider.getName(), this, null); + } + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + Log.i(TAG, "Removing any possible location listeners."); + locationManager.removeUpdates(this); + } + + @Override + public void onLocationChanged(@Nullable Location location) { + if (location != null) { + Log.w(TAG, "[onLocationChanged] Successfully retrieved location."); + successListener.onSuccess(location); + } else { + Log.w(TAG, "[onLocationChanged] Null location."); + failureListener.onFailure(); + } + } + + @Override + public void onStatusChanged(@NonNull String provider, int status, @Nullable Bundle extras) { + Log.i(TAG, "[onStatusChanged] Provider: " + provider + " Status: " + status); + } + + @Override + public void onProviderEnabled(@NonNull String provider) { + Log.i(TAG, "[onProviderEnabled] Provider: " + provider); + } + + @Override + public void onProviderDisabled(@NonNull String provider) { + Log.i(TAG, "[onProviderDisabled] Provider: " + provider); + } + + interface SuccessListener { + void onSuccess(@NonNull Location location); + } + + interface FailureListener { + void onFailure(); + } +} diff --git a/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java b/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java index a81314b729..ce593154b3 100644 --- a/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java +++ b/src/org/thoughtcrime/securesms/maps/PlacePickerActivity.java @@ -16,9 +16,8 @@ import android.view.animation.OvershootInterpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; -import com.google.android.gms.location.FusedLocationProviderClient; -import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.SupportMapFragment; @@ -74,25 +73,16 @@ public final class PlacePickerActivity extends AppCompatActivity { fab.setOnClickListener(v -> finishWithAddress()); - FusedLocationProviderClient fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || - checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || - checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED - ) { - fusedLocationClient.getLastLocation() - .addOnFailureListener(e -> { - Log.w(TAG, "Failed to get location", e); - setInitialLocation(PRIME_MERIDIAN); - }) - .addOnSuccessListener(location -> { - if (location == null) { - Log.w(TAG, "Failed to get location"); - setInitialLocation(PRIME_MERIDIAN); - } else { - setInitialLocation(new LatLng(location.getLatitude(), location.getLongitude())); - } - }); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) + { + new LocationRetriever(this, this, location -> { + setInitialLocation(new LatLng(location.getLatitude(), location.getLongitude())); + }, () -> { + Log.w(TAG, "Failed to get location."); + setInitialLocation(PRIME_MERIDIAN); + }); } else { Log.w(TAG, "No location permissions"); setInitialLocation(PRIME_MERIDIAN); diff --git a/src/org/thoughtcrime/securesms/util/ServiceUtil.java b/src/org/thoughtcrime/securesms/util/ServiceUtil.java index eb189daf9b..536fe51860 100644 --- a/src/org/thoughtcrime/securesms/util/ServiceUtil.java +++ b/src/org/thoughtcrime/securesms/util/ServiceUtil.java @@ -7,6 +7,7 @@ import android.app.NotificationManager; import android.app.job.JobScheduler; import android.content.Context; import android.hardware.display.DisplayManager; +import android.location.LocationManager; import android.media.AudioManager; import android.net.ConnectivityManager; import android.os.Build; @@ -15,6 +16,8 @@ import android.os.Vibrator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.core.content.ContextCompat; + import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.WindowManager; @@ -79,4 +82,8 @@ public class ServiceUtil { public static ActivityManager getActivityManager(@NonNull Context context) { return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); } + + public static LocationManager getLocationManager(@NonNull Context context) { + return ContextCompat.getSystemService(context, LocationManager.class); + } }