Skip to content

Instantly share code, notes, and snippets.

@thegarlynch
Last active August 13, 2020 06:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thegarlynch/259bc86f4ed773d746fe7506c3bdd47a to your computer and use it in GitHub Desktop.
Save thegarlynch/259bc86f4ed773d746fe7506c3bdd47a to your computer and use it in GitHub Desktop.
Implementasi Location Finder
package com.thegar.example.helper.location
import android.Manifest
import android.app.Activity
import android.content.IntentSender
import android.content.pm.PackageManager
import android.os.Looper
import androidx.core.app.ActivityCompat
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.*
import com.thegar.example.helper.permission.LocationPermissionHandler
import io.reactivex.subjects.BehaviorSubject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
/***
* Helper for finding Location
*/
@Singleton
class LocationFinder @Inject constructor() : LocationCallback() {
companion object {
const val REQUEST_CHECK_SETTINGS = 12313
}
private val subject = BehaviorSubject.create<LocationInfo>()
private val locationRequest = LocationRequest.create().apply {
interval = 1000
fastestInterval = 100
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
private val locationSettingsBuilder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
override fun onLocationResult(p0: LocationResult?) {
p0?.locations ?: return
if(p0.locations.isEmpty()){
return
}
val location = p0.locations.findLast { !it.isFromMockProvider } ?: throw NoMockedLocation()
subject.onNext(LocationInfo(location.latitude.toString(), location.longitude.toString()))
}
private suspend fun requireSettingsMatch(activity: Activity) = suspendCoroutine<Boolean>{ continuation ->
val settingsClient = LocationServices.getSettingsClient(activity)
val task = settingsClient.checkLocationSettings(locationSettingsBuilder.build())
task.addOnSuccessListener {
continuation.resume(true)
}
task.addOnFailureListener { ex ->
if(ex is ResolvableApiException){
try {
ex.startResolutionForResult(activity, REQUEST_CHECK_SETTINGS)
}catch (sendEx : IntentSender.SendIntentException){
// User explicitly not changing options
continuation.resume(false)
}catch (otherEx : Exception){
continuation.resumeWithException(otherEx)
}
}else{
continuation.resumeWithException(ex)
}
}
}
/**
* @return null if it should be ignored.
*/
suspend fun singleShotCurrentLocation(activity: Activity) : LocationInfo?{
val client = LocationServices.getFusedLocationProviderClient(activity)
if (ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
LocationPermissionHandler.attach(activity).await()
Timber.d("Permission granted")
return null
}
Timber.d("Asking for settings")
if(!requireSettingsMatch(activity)) return null
Timber.d("Asking for location request")
client.requestLocationUpdates(locationRequest, this, Looper.getMainLooper())
val locationInfo = withContext(Dispatchers.IO){
subject.share()
.doOnNext { Timber.d("Location is fetched $it") }
.throttleLatest(5000, TimeUnit.MILLISECONDS)
.doOnNext { Timber.d("Fetched Throttled location $it") }
.skip(1)
.blockingFirst()
}
client.removeLocationUpdates(this)
return locationInfo
}
data class LocationInfo(val latitude: String, val longitude: String)
class NoMockedLocation : Exception("Cannot Have Mocked Location")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment