0

I am trying to get the User Location in Compose.

I found this link and tried the Mitch solution. But it has an infinite loop and the snackbar is not shown. From other links I read that it needs to be attached to a scaffold but it seems than on M3 is no longer valid.

I change the infinite loop from:

LaunchedEffect(true) {
    while (true) {
        isGPSEnabled = isGPSEnabled(context)
        delay(2.seconds)
    }
}

To:

LaunchedEffect(true) {
    while (!isGPSEnabled) {   //while (true) {
        isGPSEnabled = isGPSEnabled(context)
        delay(2.seconds)
    }
}

But I don't know how to make the snackbar to show:

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
    }

I know the code [val result = snackbarHostState.showSnackbar(...) ]is executed because I have a break point in debug that reach it. It stays there an never reach the [when(result)] since it has[duration = SnackbarDuration.Indefinite]

On my Screen I have the following to call the function:

Image(painter = painterResource(R.drawable.baseline_my_location_24),
                contentDescription = "Get Coordinates",
                modifier = Modifier
                    .clickable {
                        doGet = true
                    }
                    .size(40.dp)
            )

            if (doGet) {
                ExampleForegroundLocationTrackerScreen()
            }

That is for testing purpose, but I really want to receive the value(latitud, longitud) to show them on the screen and later save then in a Database.

1 Answer 1

0

After many days or weeks I end up using a BottomSheetScaffold.

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LocationScreen(
startLocationUpdates: ()->Unit,
context: Context,
currentLocation: LatLng
) {

////// This permissions are use down bellow at a Button Click event that
////// actually calls for the GPS location. The Button  is part of the BottomSheet content.
val permissions = arrayOf(
    Manifest.permission.ACCESS_COARSE_LOCATION,
    Manifest.permission.ACCESS_FINE_LOCATION,
)

////// startActivityForResult function is deprecated and in Jetpack Compose the solution is 
////// to use "rememberLauncherForActivityResult" with "ActivityResultContracts" 
val launchMultiplePermissions = rememberLauncherForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissionMaps ->
    val areGranted = permissionMaps.values.reduce { acc, next -> acc && next }
    if (areGranted) {
        locationRequired = true
        startLocationUpdates()
        Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
    }
}

var readable by remember { mutableStateOf("") }
//val scope = rememberCoroutineScope()
val scaffoldState = rememberBottomSheetScaffoldState()

BottomSheetScaffold(
    //modifier = Modifier.heightIn(min=0.dp, max=400.dp),
    scaffoldState = scaffoldState,
    sheetPeekHeight = 590.dp,
    sheetSwipeEnabled = false,
    sheetContent = {

        Column(
            Modifier
                .fillMaxWidth()
                .padding(6.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            if (currentLocation.latitude != 0.toDouble() && currentLocation.longitude != 0.toDouble() && readable != "") {
                WebViewScreen(location = currentLocation) //, address = readable)
                showdialog.value = false
            } else {Text(text = "Please click [Get your location]")}
            
        }
    }) { innerPadding ->
    Box(Modifier.padding(innerPadding)) {

        Column(
            modifier = Modifier.fillMaxWidth(),
            verticalArrangement = Arrangement.SpaceEvenly,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Your location: ${currentLocation.latitude}/${currentLocation.longitude}")
            if (currentLocation.latitude != 0.toDouble() && currentLocation.longitude != 0.toDouble()) {
                Text(
                    text = "Readable location: $currentLocation"
                )
                location = ""
                readable = getReadableLocation(currentLocation.latitude, currentLocation.longitude, context)
                glocation = currentLocation
            }
    //////Here is where the permissions are used to call for the system permissions pop up.
            Button(onClick = {
                if (permissions.all {
                        ContextCompat.checkSelfPermission(
                            context,
                            it
                        ) == PackageManager.PERMISSION_GRANTED
                    }) {
                    //Get Locations
  ////// Here once the permissions are granted I call my function that actually gets the GPS Location
                    startLocationUpdates()
                } else {
                    launchMultiplePermissions.launch(permissions)
                }
                =
            }) {
                Text(text = "Get your location")
            }
        }
    }
}

}

Maybe you can read this post for better explanation. In there the example is directed toward Pictures instead of GPS location.

The GPS Location function is the following:

private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationCallback: LocationCallback


override fun onResume() {
    super.onResume()
    if (locationRequired)
        startLocationUpdates()
}

override fun onPause() {
    super.onPause()
    if (this::locationCallback.isInitialized) {
        locationCallback?.let {
            fusedLocationClient?.removeLocationUpdates(it)
        }
    }
}

@SuppressLint("MissingPermission")
private fun startLocationUpdates() {
    locationCallback?.let {
        val locationRequest = LocationRequest.Builder(
            Priority.PRIORITY_HIGH_ACCURACY, 100
        )
            .setWaitForAccurateLocation(false)
            .setMinUpdateIntervalMillis(3000)
            .setMaxUpdateDelayMillis(100)
            .build()

        fusedLocationClient?.requestLocationUpdates(
            locationRequest,
            it,
            Looper.getMainLooper()
        )
    }
}

This code is written in the MainActivity before the onCreate and is pass to the Screen as a Lambda function along the currentLocation:LatLng which is calculated/retrieved by the "startLocationUpdates" function already detail above.

Then inside the onCreate the following code:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

    setContent {

        var currentLocation by remember {
            mutableStateOf(LatLng(0.toDouble(), 0.toDouble()))
        }

        locationCallback = object : LocationCallback() {
            override fun onLocationResult(p0: LocationResult) {
                super.onLocationResult(p0)
                for (location in p0.locations) {
                    currentLocation = LatLng(location.latitude, location.longitude)

                }
            }
        }

        MainTheme { ...
        
        NavHost(navController = navController, startDestination = "MainScreen") {
              composable("LocationScreen") {
                   LocationScreen(
                      { startLocationUpdates() },
                        this@MainActivity,
                        currentLocation
                      )
                   }

        ...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.