I have a function that gets user location and returns a LatLng. How can I "await" a getLocation task...
w
I have a function that gets user location and returns a LatLng. How can I "await" a getLocation task and return the function only after completion? I assume I may be doing something fundamentally wrong here, as you can't return the function from inside the task CompleteListener... Code is next comment.
not kotlin but kotlin colored 1
Copy code
fun getUserLocation(MainActivity: ComponentActivity): LatLng {
    var userLocation = LatLng(0.0, 0.0)
    //get watch location
    val location = LocationServices.getFusedLocationProviderClient(MainActivity)
    //get current location
    if (ActivityCompat.checkSelfPermission(
            MainActivity,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
            MainActivity,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        getLocationPermissions(MainActivity)
    }

    location.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY, null).addOnSuccessListener {
        Log.d("Location", "User Location: ${it.latitude}, ${it.longitude}")
        userLocation = LatLng(it.latitude, it.longitude)
    }.addOnFailureListener { e ->
        Log.d("Location", "Failed to get current location: ${e.message}")
        //Get last location
        location.lastLocation.addOnSuccessListener {
            if (it == null) {
                Log.d("Location", "lastLocation: null")
            }
            else {
                Log.d("Location", "Location: ${it.latitude}, ${it.longitude}")
                userLocation = LatLng(it.latitude, it.longitude)
            }
        }.addOnFailureListener { e ->
            Log.d("Location", "Failed to get lastLocation: ${e.message}")
        }
    }.addOnCompleteListener {
        Log.d("Location", "User location task complete")
        
        //Can't do this
        return userLocation
    }

    //Wait for location task to end, then return userLocation
    //TODO: ???
    
    //Returns before task completion, which is expected
    return userLocation
}
j
You should probably make
getUserLocation()
a
suspend fun
. Then you can use
suspendCancellableCoroutine
(docs) to await the callback result:
Copy code
suspend fun getUserLocation(MainActivity: ComponentActivity): LatLng {
    ...
    return suspendCancellableCoroutine { continuation ->
        location.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY, null).addOnSuccessListener {
            ...
            continuation.resume(LatLng(it.latitude, it.longitude))
        }.addOnFailureListener { e ->
            continuation.resumeWithException(e)
        }
    }
}
p
If it’s a GMS task which it seems it is, it’s just better to use .await() on the task (it’s a kotlinx coroutines extension)
w
Thanks @Jeff Lockhart this worked for my solution. I looked into .await() but did not see where it applies in my situation. The task is GMS.
p
Why doesn’t it apply to your situation? https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/await.html
Copy code
location.getCurrentLocafion(..).await()
w
@Patrick Steiger That function doesn't exist with my current imports, at least off the GMS getCurrentLocation. I don't see what dependency that comes from. I have kotlinx.coroutines included already.
p
Ah. It’s
kotlinx-coroutines-play-services
, you can add it to Gradle alongisde
kotlinx-coroutines-android
and
kotlinx-coroutines-core
I recommend using await() instead of doing your own thing because there’s some optimizations done by await(): it has a fast-path for not suspending when result is already there, it also passes a DirectExecutor to task to avoid thread switching
w
For the life of me can't get it working. I don't have a tasks.await package. (https://developers.google.com/android/guides/tasks#kotlin_coroutine)
Copy code
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.maps.android:maps-compose:2.14.0'
implementation 'com.google.android.gms:play-services-base:18.2.0'
implementation 'com.google.android.gms:play-services-location:21.0.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4'
p
Did you Gradle sync after adding the dependency?
w
Yeah, synced without issue.