i'm wanting to make a network call when location access has been granted. so i'm using LaunchedEffect(key1 = location.value){...} to decide when to make that network call to recompose, but facing some issues.
upon initial launch user is greeted with the location request (either precise or coarse). during this, the Toast.makeText(context, "Allow location access in order to see image", Toast.LENGTH_SHORT).show() get's called twice and shows up twice. when the user selects an option from the location request dialog, i would assume location.value would end up changing and viewModel.getImage(location.value!!) get's called. debugging through this, that all happens, but the image doesn't end up showing. i got it to work sometimes by force closing the app, then opening it again, then the image shows up. any insights? here is the location code in that same file:
val locationLiveData = LocationLiveData(context)
val location = locationLiveData.observeAsState()
val requestSinglePermissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
when {
it.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
locationLiveData.startLocationUpdates()
}
it.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
locationLiveData.startLocationUpdates()
} else -> {
Toast.makeText(context, "Allow location access", Toast.LENGTH_SHORT).show()
}
}
}
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED) {
locationLiveData.startLocationUpdates()
} else {
// true so we execute once not again when we compose or so
LaunchedEffect(key1 = true) {
requestSinglePermissionLauncher.launch(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION))
}
}
EDIT 2 LocationLiveData
class LocationLiveData(var context: Context): LiveData<LocationDetails>() {
// used to get last known location
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
// We have at least 1 observer or 1 component looking at us
// here we can get the last known location of the device
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
) {
return
}
fusedLocationClient.lastLocation.addOnSuccessListener {
setLocationData(it)
}
}
// no one is looking at this live data anymore
override fun onInactive() {
super.onInactive()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
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) {
value = LocationDetails(longitude = location.longitude.toString(), latitude = location.latitude.toString())
}
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(p0: LocationResult) {
super.onLocationResult(p0)
for (location in p0.locations) {
setLocationData(location)
}
}
}
companion object {
private const val ONE_MINUTE: Long = 60_000
val locationRequest: LocationRequest = LocationRequest.create().apply {
interval = ONE_MINUTE
fastestInterval = ONE_MINUTE / 4
priority = Priority.PRIORITY_HIGH_ACCURACY
}
}
}
COMPOSABLE
@RequiresApi(Build.VERSION_CODES.N)
@Composable
fun HomeScreen(viewModel: HomeScreenViewModel = hiltViewModel(), navigateToAuthScreen: () -> Unit, navigateToAddImage: () -> Unit){
var text by remember { mutableStateOf(TextFieldValue("")) }
val context = LocalContext.current
val locationLiveData = remember { LocationLiveData(context) }
val location = locationLiveData.observeAsState()
val requestSinglePermissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
when {
it.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
locationLiveData.startLocationUpdates()
}
it.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
locationLiveData.startLocationUpdates()
}
}
}
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED) {
locationLiveData.startLocationUpdates()
} else {
// true so we execute once not again when we compose or so
LaunchedEffect(key1 = true) {
requestSinglePermissionLauncher.launch(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION))
}
}
Scaffold( topBar = {
HomeScreenTopBar()
},
floatingActionButton = {
FloatingActionButton(onClick = {
if (location.value != null) {
navigateToAddImageScreen()
} else {
Toast.makeText(context, "allow location access to add image", Toast.LENGTH_SHORT).show()
}
},
backgroundColor = MaterialTheme.colors.primary
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Save note"
)
}
}) {innerPadding ->
Column(modifier = Modifier
.fillMaxSize()
.padding(innerPadding)) {
LaunchedEffect(key1 = location.value) {
if (location.value != null) {
viewModel.getListings(location.value!!)
} else {
Toast.makeText(context, "Allow location access in order to see image", Toast.LENGTH_SHORT).show()
}
}
}