Hello everyone, i am currently having an issue wit...
# android
s
Hello everyone, i am currently having an issue with FusedLocationProviderClient. I used to call .lastlocation from this service. It has always until i tried it nowadays with Android 9/10. it always returns null now. I googled and couldnt find relevant hints, i also updated the google play location service. Any help appreciated.
google 1
n
I think you need to actually use
requestLocationUpdates
lastLocation
will return only the cached location, probably at the point of your request it's
null
, you could also check out
currentLocation
, which will trigger a location calculation, so probably is heavier
Maybe a
lastLocation ?: currentLocation ?: requestLocationUpdates
logic would make sense in your case
s
I actually call fusedLocationProvider.requestlocationUpdate(...) on the Onresume
n
Are the permissions granted by the user?
s
Yes, permissions are granted, location is activated, wifi too. This used to work till recently while no code change has been made
n
Just for testing, could you open a maps application before, then after check the location in your app?
s
Wow, after opening maps, it seems to get the last location
That is so unusual, how come?
n
Can you show your current logic? And perhaps try it with currentLocation instead of lastLocation
s
here is my current Implementation of the service:
Copy code
/**
 *
 * @property locationProviderClient Android Native location tracking service
 * @property appContext Global Application Context
 * @constructor
 */
class LocationServiceProviderImpl (
    private val locationProviderClient: FusedLocationProviderClient,
    context: Context) : LocationServiceProvider {

    private val appContext = context.applicationContext

    private val TAG = this::class.java.name

    /**
     *
     * @param lastFetchedLocation Coordinate
     * @return Boolean
     */
    override suspend fun hasLocationChanged(lastFetchedLocation: Coordinate): Boolean {
        return try {
            hasDeviceLocationChanged(lastFetchedLocation)
        }
        catch (e: LocationTrackingDenied){
            Log.d(TAG, appContext.getString(R.string.location_provider_error))
            false
        }
    }

    /**
     *
     * @return Coordinate
     */
    override suspend fun getCurrentLocation() : Coordinate? {

        try {
         val test =   getLastKnownLocation()
            val deviceLocation= getLastDeviceLocation().await()
            return Coordinate(deviceLocation.latitude,deviceLocation.longitude)
        }catch (e: LocationTrackingDenied){
            //Temporary
            Log.e(TAG, appContext.getString(R.string.location_provider_error))
        }
        catch (e: NullPointerException){
            //Temporary
            Log.e(TAG, appContext.getString(R.string.location_device_error))
            return null
        }

        return null
    }

    @SuppressLint("MissingPermission")
    override suspend fun getLastKnownLocation(): Location = suspendCoroutine { coroutine ->
        LocationServices.getFusedLocationProviderClient(appContext).lastLocation
            .addOnSuccessListener { location -> coroutine.resume(location) }
            .addOnCanceledListener { Log.e(TAG,"Last Known location: on canceled listener") }
            .addOnFailureListener {  Log.e(TAG,"Last Known location: on failure listener") }
    }
    /**
     * Decide if Device location has changed by a defined threshold range
     * @param lastLocation Coordinate
     * @return Boolean
     */
    private suspend fun hasDeviceLocationChanged(lastLocation: Coordinate) : Boolean{

        val deviceLocation = getLastDeviceLocation().await()
        val comparisonThreshold = 0.05
        return abs(deviceLocation.latitude - lastLocation.latitude)  > comparisonThreshold &&
                abs(deviceLocation.longitude - lastLocation.longitude) > comparisonThreshold
    }

    /**
     * Retrieves last fetched location asynchronously
     * @precondition Location Permission granted
     * @return Deferred<Location>
     */
    @SuppressLint("MissingPermission")
    private fun getLastDeviceLocation(): Deferred<Location> {
        return if (hasGPSPermission()) locationProviderClient.lastLocation.executeAsync()
        else  throw LocationTrackingDenied()

    }

    /**
     * Check if App has Geolocation Permission
     * @return Boolean
     */
    private fun hasGPSPermission() : Boolean{
        return PreferencesHelper.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)
    }

}
as per your suggestion I opened the map app and a popup appeared briefly and afterwards showed my current location. When I went back in the app, the "val test" and "val deviceLocation" correctly retrieved the fusedLocation
n
In your
getLastKnownLocation
I'd do the following, if the lastLocation is null I'd call
requestLocationUpdates
and I'd resume the continuation after the first result (Also you should dispose the listener after the first result)
s
Copy code
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@SuppressLint("MissingPermission")
fun startLocationUpdate() = fusedLocationProviderClient.requestLocationUpdates(
    locationRequest,
    locationCallback,
    Looper.getMainLooper()
)
this is what I call every time from a lifecycle aware service binded to the UI
the action with the google map app somehow fixed it and I still do not know why
j
dunno if this has been mentioned, but on Huawei devices it's different https://developer.huawei.com/consumer/en/codelab/HMSLocationKit/index.html#0