package net.pokeranalytics.android.util import android.Manifest.permission.ACCESS_FINE_LOCATION 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 */ fun askForPlacesRequest(callback: ((success: Boolean, places: ArrayList) -> Unit)?) { // Initialize Places. Places.initialize(context, context.getString(net.pokeranalytics.android.R.string.google_places_api)) // Create a new Places client instance. val placesClient = Places.createClient(context) // Use fields to define the data types to return. val placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS, Place.Field.LAT_LNG) // Use the builder to buildAndShow a FindCurrentPlaceRequest. val request = FindCurrentPlaceRequest.builder(placeFields).build() // Call findCurrentPlace and handle the response (first check that the user has granted permission). if (ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { val placeResponse = placesClient.findCurrentPlace(request) placeResponse.addOnCompleteListener { task -> val places = ArrayList() if (task.isSuccessful) { val response = task.result response?.placeLikelihoods?.let { places.addAll(it) } } else { val exception = task.exception if (exception is ApiException) { Timber.d("Error: ${"Place not found: " + exception.statusCode}") } } 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) } } /** * Return the current location of the user */ fun findCurrentLocation(callback: ((location: android.location.Location?) -> Unit)?) { 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 -> callback?.invoke(currentLocation) } ?: 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 { callback?.invoke(null) } } }