From 7476af5e6c2a84efa1d44832443ce450f42f6a90 Mon Sep 17 00:00:00 2001 From: Aurelien Hubert Date: Wed, 6 Mar 2019 17:55:23 +0100 Subject: [PATCH] Add findNearestLocation & refactor callbacks management --- .../components/PokerAnalyticsActivity.kt | 55 +++++++---- .../android/util/LocationManager.kt | 91 +++++++++++++++++++ 2 files changed, 126 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt index 850025de..da29643a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt @@ -10,8 +10,8 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.google.android.libraries.places.api.model.PlaceLikelihood import io.realm.Realm +import net.pokeranalytics.android.model.realm.Location import net.pokeranalytics.android.util.LocationManager -import java.util.* open class PokerAnalyticsActivity : AppCompatActivity() { @@ -21,8 +21,7 @@ open class PokerAnalyticsActivity : AppCompatActivity() { } private val realm = Realm.getDefaultInstance() - private var askFromPlaces = false - private var currentCallback: ((success: Boolean, places: ArrayList) -> Unit)? = null + private var permissionCallback: ((granted: Boolean) -> Unit)? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -36,8 +35,9 @@ open class PokerAnalyticsActivity : AppCompatActivity() { if (permissions.isNotEmpty() && permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED ) { - locationPermissionGranted() + permissionCallback?.invoke(true) } else { + permissionCallback?.invoke(false) // permission denied, boo! Disable the // functionality that depends on this permission. // showMessage(getString(R.string.error)); @@ -71,35 +71,50 @@ open class PokerAnalyticsActivity : AppCompatActivity() { /** * Ask for location permission */ - private fun askForLocationPermission() { + private fun askForLocationPermission(permissionCallback: ((granted: Boolean) -> Unit)) { + this.permissionCallback = permissionCallback ActivityCompat.requestPermissions( - this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), - PERMISSION_REQUEST_ACCESS_FINE_LOCATION + this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_ACCESS_FINE_LOCATION ) } - /** - * Called when the permission location has been granted - */ - open fun locationPermissionGranted() { - if (askFromPlaces) { - askFromPlaces = false - askForPlacesRequest(currentCallback) - } - } /** * Ask for places request */ fun askForPlacesRequest(callback: ((success: Boolean, places: ArrayList) -> Unit)?) { - // Call findCurrentPlace and handle the response (first check that the user has granted permission). if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { LocationManager(this).askForPlacesRequest(callback) } else { - askFromPlaces = true - currentCallback = callback - askForLocationPermission() + askForLocationPermission { granted -> + if (granted) { + LocationManager(this).askForPlacesRequest(callback) + } else { + callback?.invoke(false, ArrayList()) + } + } + } + } + + /** + * Find the nearest location from the user + */ + fun findNearestLocation(callback: ((location: Location?) -> Unit)?) { + if (LocationManager(this).databaseContainsLocationsWithCoordinates()) { + if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + LocationManager(this).findNearestLocationFromUser(callback) + } else { + askForLocationPermission { granted -> + if (granted) { + LocationManager(this).findNearestLocationFromUser(callback) + } else { + callback?.invoke(null) + } + } + } + } else { + callback?.invoke(null) } } diff --git a/app/src/main/java/net/pokeranalytics/android/util/LocationManager.kt b/app/src/main/java/net/pokeranalytics/android/util/LocationManager.kt index 1a8e11f2..a410f1c2 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/LocationManager.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/LocationManager.kt @@ -5,15 +5,25 @@ import android.content.Context import android.content.pm.PackageManager import androidx.core.content.ContextCompat import com.google.android.gms.common.api.ApiException +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationServices import com.google.android.libraries.places.api.Places import com.google.android.libraries.places.api.model.Place import com.google.android.libraries.places.api.model.PlaceLikelihood import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest +import io.realm.Realm +import io.realm.kotlin.where +import net.pokeranalytics.android.model.realm.Location import timber.log.Timber import java.util.* +import kotlin.collections.ArrayList class LocationManager(private var context: Context) { + companion object { + const val MAXIMUM_DISTANCE_FROM_USER = 5000 // in meters + } + /** * Return the [places] around the user */ @@ -50,8 +60,89 @@ class LocationManager(private var context: Context) { callback?.invoke(task.isSuccessful, places) } + } else { + // If we don't have the permission, return + callback?.invoke(false, ArrayList()) } } + /** + * Return if the database contains one or many locations with coordinates + */ + fun databaseContainsLocationsWithCoordinates(): Boolean { + val realm = Realm.getDefaultInstance() + val result = realm.where().isNotNull("latitude") + .and().isNotNull("longitude").findAll().isNotEmpty() + realm.close() + return result + } + + /** + * Find the nearest location from user + */ + fun findNearestLocationFromUser(callback: ((location: Location?) -> Unit)?) { + + val locations = ArrayList() + val realm = Realm.getDefaultInstance() + val realmResults = realm.where().isNotNull("latitude") + .and().isNotNull("longitude").findAll() + locations.addAll(realm.copyFromRealm(realmResults)) + realm.close() + + // If we have no locations with coordinates, return null + if (realmResults.size == 0) { + callback?.invoke(null) + return + } + + val fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context) + + if (ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + fusedLocationClient.lastLocation.addOnSuccessListener { location: android.location.Location? -> + // Got last known location. In some rare situations this can be null. + + location?.let { currentLocation -> + + locations.sortBy { + val locationToSort = android.location.Location("") + locationToSort.latitude = it.latitude!! + locationToSort.longitude = it.longitude!! + currentLocation.distanceTo(locationToSort) + } + + val nearestLocation = locations.firstOrNull() + nearestLocation?.let { + + val locationForDistance = android.location.Location("") + locationForDistance.latitude = it.latitude ?: 0.0 + locationForDistance.longitude = it.longitude ?: 0.0 + + // Final check: if the distance between the location and the user is lower than MAXIMUM_DISTANCE_FROM_USER + if (currentLocation.distanceTo(locationForDistance) < MAXIMUM_DISTANCE_FROM_USER) { + callback?.invoke(nearestLocation) + } else { + callback?.invoke(null) + } + + + } ?: run { + callback?.invoke(null) + } + + } ?: run { + // If the current location is null, return null + callback?.invoke(null) + } + + }.addOnCanceledListener { + // If there was a problem during the call to last location, return null + callback?.invoke(null) + } + } else { + // If we don't have the permission, return null + callback?.invoke(null) + } + + } } \ No newline at end of file