在 Jetpack Compose Android 中获取用户的当前位置



意图: 在用户授予(粗略/精细(位置权限后,我试图获取用户的当前位置。我正在使用jetpack编写伴奏库以获得经理的许可。

因此,当用户授予权限时,我使用FusedLocationProviderClient的getCurrentLocation来获取位置对象,并从中获取lat-lang

问题:在下面的代码块中,Logcat日志:Coordinates[0.0,0.0]

class LocationManager @Inject constructor(
private val fusedLocation: FusedLocationProviderClient
) {
@SuppressLint("MissingPermission")
@Composable
@ExperimentalPermissionsApi
fun getLatLang(context: Context): Coordinates {
val coordinates = Coordinates(0.0, 0.0)
/**
* Checking and requesting permission. If granted it will fetch current lat lang,
* else it will request for permission.
* If denied, will show popup to open app settings and grant location permission.
*/
LocationPermissionManager.RequestPermission(
actionPermissionGranted = {
fusedLocation.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY, null)
.addOnSuccessListener { location ->
if (location != null) {
coordinates.lat = location.latitude
coordinates.long = location.longitude
}
}
},
actionPermissionDenied = { context.openAppSystemSettings() }
)
return coordinates
}
}

data class Coordinates(var lat: Double, var long: Double)

消费LocationManager下方:

@ExperimentalPermissionsApi
@Composable
fun AirQualityLayout(locationManager: LocationManager) {
val context: Context = LocalContext.current
val coordinates: Coordinates = locationManager.getLatLang(context = context)
if (coordinates.lat != 0.0 && coordinates.long != 0.0) {
Timber.d("Current location: $coordinates")
ShowUI()
}
}

期待建议/帮助我在这里做错了什么。

这对前台位置更新很有用

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.location.Location
import android.location.LocationManager
import android.os.Looper
import android.provider.Settings
import android.util.Log
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.DisposableEffectResult
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.google.android.gms.location.LocationAvailability
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
private const val TAG = "ForegroundLocationTracker"
private object SimulatedDisposableEffectResult : DisposableEffectResult {
override fun dispose() {
}
}
@OptIn(ExperimentalPermissionsApi::class)
private object SimulatedMultiplePermissionsState : MultiplePermissionsState {
override val allPermissionsGranted: Boolean
get() = false
override val permissions: List<PermissionState>
get() = emptyList()
override val revokedPermissions: List<PermissionState>
get() = emptyList()
override val shouldShowRationale: Boolean
get() = false
override fun launchMultiplePermissionRequest() {
}
}
@OptIn(ExperimentalPermissionsApi::class)
sealed interface LocationPermissionsState {
@Composable
operator fun invoke(): MultiplePermissionsState
object CoarseAndFine : LocationPermissionsState {
@Composable
override fun invoke(): MultiplePermissionsState {
return rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
)
}
}
/**
* This could be used in a compose preview
*/
object Simulated : LocationPermissionsState {
@Composable
override fun invoke(): MultiplePermissionsState {
return remember {
SimulatedMultiplePermissionsState
}
}
}
}
private suspend fun requestToEnableGPS(context: Context, snackbarHostState: SnackbarHostState) {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
val canNavigateToGPSSettings =
intent.resolveActivity(context.packageManager) != null
val result = snackbarHostState.showSnackbar(
message = "GPS is disabled",
actionLabel = if (!canNavigateToGPSSettings) {
null
} else {
"ENABLE"
},
withDismissAction = true,
duration = SnackbarDuration.Indefinite,
)
when (result) {
SnackbarResult.Dismissed -> {
}
SnackbarResult.ActionPerformed -> {
if (canNavigateToGPSSettings) {
context.startActivity(intent)
}
}
}
}
fun isGPSEnabled(context: Context): Boolean {
val locationManager = context.getSystemService(
Context.LOCATION_SERVICE
) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|| locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}
@OptIn(ExperimentalPermissionsApi::class)
@SuppressLint("MissingPermission")
@Composable
fun ForegroundLocationTracker(
/**
* Will be used to show a prompt for enabling GPS if it is disabled
* or requesting location permissions if non has been granted.
*/
snackbarHostState: SnackbarHostState,
permissionsState: LocationPermissionsState = LocationPermissionsState.CoarseAndFine,
onLocationUpdates: (Location) -> Unit,
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
val fusedLocationProviderClient = remember {
LocationServices.getFusedLocationProviderClient(context)
}
val permissions = permissionsState()
var isGPSEnabled by remember {
mutableStateOf(isGPSEnabled(context))
}
LaunchedEffect(true) {
while (true) {
isGPSEnabled = isGPSEnabled(context)
delay(2.seconds)
}
}
DisposableEffect(
isGPSEnabled,
permissions.shouldShowRationale,
permissions.allPermissionsGranted,
) {
if (!permissions.allPermissionsGranted || permissions.shouldShowRationale) {
scope.launch {
val result = snackbarHostState.showSnackbar(
"Missing required permissions",
"Grant",
withDismissAction = true,
duration = SnackbarDuration.Indefinite,
)
when (result) {
SnackbarResult.Dismissed -> {
}
SnackbarResult.ActionPerformed -> {
permissions.launchMultiplePermissionRequest()
}
}
}
return@DisposableEffect SimulatedDisposableEffectResult
}
if (!isGPSEnabled) {
scope.launch {
requestToEnableGPS(
context = context,
snackbarHostState = snackbarHostState,
)
}
return@DisposableEffect SimulatedDisposableEffectResult
}
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY,
10000L,
).build()
val locationCallback = object : LocationCallback() {
override fun onLocationAvailability(p0: LocationAvailability) {
if (p0.isLocationAvailable) {
return super.onLocationAvailability(p0)
}
scope.launch {
requestToEnableGPS(
context = context,
snackbarHostState = snackbarHostState,
)
}
}
override fun onLocationResult(p0: LocationResult) {
super.onLocationResult(p0)
p0.lastLocation?.also(onLocationUpdates)?.also {
Log.i(TAG, "Current location: $it")
}
}
}
fusedLocationProviderClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper(),
)
onDispose {
fusedLocationProviderClient.removeLocationUpdates(locationCallback)
}
}
}

示例用法

@Composable
fun ExampleForegroundLocationTrackerScreen() {
ForegroundLocationTracker(snackbarHostState = SnackbarHostState()){
Log.i("LogTag", "Current location is: $it")
}
}

您拥有您的清单权限吗?(Access_fine_location和Access_coarse_location

这是我不久前做的一个课程:

class LocationLiveData(var context: Context): LiveData<LocationDetails>() {
//add dependency implementation "com.google.android.gms:play-services-maps:18.0.2"
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)

override fun onActive() {
super.onActive()


if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) { // alse geen permissie hebben just return, anders voer functie location uit
return
}
fusedLocationClient.lastLocation.addOnSuccessListener {
location -> location.also {
setLocationData(it)

}
}
}
internal fun startLocationUpdates() {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
}
private fun setLocationData(location: Location?) {

location?.let { it ->
//value is observed in LiveData
value = LocationDetails(
longitude = it.longitude.toString(),
lattitude = it.latitude.toString()
)
}
println("value $value")

}

override fun onInactive() {
super.onInactive()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
println("we have a new location result")
locationResult ?: return //als er een result is dan prima, zo niet dan just return (elvis operator)
for (location in locationResult.locations) {
setLocationData(location = location)
}
}
}
companion object {
val ONE_MINUTE: Long = 1000
@RequiresApi(Build.VERSION_CODES.S)
val locationRequest : com.google.android.gms.location.LocationRequest = com.google.android.gms.location.LocationRequest.create().apply {
interval = ONE_MINUTE
fastestInterval = ONE_MINUTE/4
priority = LocationRequest.QUALITY_HIGH_ACCURACY
}

}
}

我刚刚用UiState解决了这个问题,将函数放入ViewModel类中

这里是我的解决方案:

U状态:

data class MyCityUiState(
...
val currentLocation: Location? = null
...
)

ViewModel中的更新功能:

fun updateCurrentLocation(location: Location?) {
_uiState.update {
it.copy(
currentLocation = location
)
}
}

使用的乐趣"。addOnListener":

@SuppressLint("MissingPermission")
fun displayDistance(placeLocation: LatLng, context: Context): String? {
var fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationClient.lastLocation
.addOnSuccessListener { location: Location? ->
if (location != null) {
updateCurrentLocation(location)
}
}
var result: Float?
var formatResult: String? = null
val placeLocationToLocationType = Location("Place")
placeLocationToLocationType.latitude = placeLocation.latitude
placeLocationToLocationType.longitude = placeLocation.longitude
result = 
uiState.value.currentLocation?.distanceTo(placeLocationToLocationType)
if (result != null) {
formatResult = "%.1f".format(result / 1000) + " km"
}
return formatResult
}
``

最新更新