GoogleMaps in Jetpack Compose: Marking the Device Location

GoogleMaps in Jetpack Compose: Marking the Device Location

In this article, we will be using android-maps-compose library and implementing GoogleMaps in Jetpack compose. Additionally, we will be also using Android’s Places SDK to get your device's last known location and mark it on Map.

There was no dedicated documentation to get this thing done in Jetpack compose and many folks can face difficulties in implementing this simple feature. So, I will be showing you a step-by-step guide to do so.

Output:

Before Starting:

  1. You need an API KEY for using this Maps and Places SDK. Get it here.

  2. Enable the required APIs from here.

  3. Add these Dependencies.

implementation 'com.google.maps.android:maps-compose:2.11.1'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'com.google.android.libraries.places:places:3.0.0'

4. Add API KEY in AndroidManifest.xml.

Inside your application block add this..

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="@string/MAPS_API_KEY" />//This will be your API KEY

Implementing:

First, we need to get all the required permissions.

  1. Add this in your AndroidManifest.xml inside manifest tag.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

2. In your MainActivity, make a request and show the Map accordingly…

  • Create locationPermissionGranted variable
private val locationPermissionGranted = mutableStateOf(false)
  • Create request code.
companion object {
    const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1234
}
  • Create a function to request location permission when we don’t have one.
private fun getLocationPermission() {
    if (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        )
        == PackageManager.PERMISSION_GRANTED
    ) {
        locationPermissionGranted.value = true//we already have the permission
    } else {
        ActivityCompat.requestPermissions(
            this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
            PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION
        )
    }
}
  • Set locationPermissionGranted to true when the user grants the permission.
@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if(requestCode== PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
        locationPermissionGranted.value=true
    }
}
  • Initialize the Places SDK(in onCreate), we need that for our use.
Places.initialize(applicationContext, getString(R.string.MAPS_API_KEY))
  • That’s it, now open the MapScreeen() when you have permission, else show something else.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Places.initialize(applicationContext, getString(R.string.MAPS_API_KEY))

    setContent {
        getLocationPermission()//Check for permission before showing anything
        if (locationPermissionGranted.value) {
            HomeScreen()
        }else{
            Text("Need permission")
        }
    }
}

Full code for MainActivity :

class MainActivity : ComponentActivity() {
    companion object {
        const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1234
    }

    private val locationPermissionGranted = mutableStateOf(false)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Places.initialize(applicationContext, getString(R.string.MAPS_API_KEY))

        setContent {
            getLocationPermission()
            if (locationPermissionGranted.value) {
                MapScreen()
            }else{
                Text("Need permission")
            }
        }
    }


    @Deprecated("Deprecated in Java")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if(requestCode== PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            locationPermissionGranted.value=true
        }
    }

    private fun getLocationPermission() {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
            == PackageManager.PERMISSION_GRANTED
        ) {
            locationPermissionGranted.value = true
        } else {
            ActivityCompat.requestPermissions(
                this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION
            )
        }
    }

}

3. In your MapScreen composable, we will be using GoogleMap composable.

  • Get the context
val context = LocalContext.current
  • We can get the lastKnownLocation of the device by using a fusedLocationProviderClient.
val fusedLocationProviderClient =
    remember { LocationServices.getFusedLocationProviderClient(context) }
  • We will also store some important values for our map and they will be

lastKnownLocation — Last known location of device.

var lastKnownLocation by remember {
    mutableStateOf<Location?>(null)
}

deviceLatLng- Latitude Longitude value of device location

var deviceLatLng by remember {
    mutableStateOf(LatLng(0.0, 0.0))
}

cameraPositionState- To be used for camera position in our GoogleMap.

val cameraPositionState = rememberCameraPositionState {
    position = CameraPosition.fromLatLngZoom(deviceLatLng, 18f)
}
  • Now get the Last known location and set all values
val locationResult = fusedLocationProviderClient.lastLocation
locationResult.addOnCompleteListener(context as MainActivity) { task ->
    if (task.isSuccessful) {
        // Set the map's camera position to the current location of the device.
        lastKnownLocation = task.result
        deviceLatLng = LatLng(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)
        cameraPositionState.position = CameraPosition.fromLatLngZoom(deviceLatLng, 18f)
    } else {
        Log.d(TAG, "Current location is null. Using defaults.")
        Log.e(TAG, "Exception: %s", task.exception)
    }
}
  • Now, create a GoogleMap composable and use all values
GoogleMap(cameraPositionState = cameraPositionState) {
    MarkerInfoWindowContent(
     state = MarkerState(position = deviceLatLng)
    ) { marker ->
        Text(marker.title ?: "You", color = Color.Red)
    }
}

Full code:

@Composable
fun HomeScreen() {
    val context = LocalContext.current

    val fusedLocationProviderClient =
        remember { LocationServices.getFusedLocationProviderClient(context) }

    var lastKnownLocation by remember {
        mutableStateOf<Location?>(null)
    }

    var deviceLatLng by remember {
        mutableStateOf(LatLng(0.0, 0.0))
    }

    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(deviceLatLng, 18f)
    }

    val locationResult = fusedLocationProviderClient.lastLocation
    locationResult.addOnCompleteListener(context as MainActivity) { task ->
        if (task.isSuccessful) {
            // Set the map's camera position to the current location of the device.
            lastKnownLocation = task.result
            deviceLatLng = LatLng(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)
            cameraPositionState.position = CameraPosition.fromLatLngZoom(deviceLatLng, 18f)
        } else {
            Log.d(TAG, "Current location is null. Using defaults.")
            Log.e(TAG, "Exception: %s", task.exception)
        }
    }

    GoogleMap(
        cameraPositionState = cameraPositionState
    ) {
        MarkerInfoWindowContent(
            state = MarkerState(
                position = deviceLatLng
            )
        ) { marker ->
            Text(marker.title ?: "You", color = Color.Red)
        }
    }
}

Output:

I hope you found this helpful. If yes, then do FOLLOW ‘Sagar Malhotra’ for more Android-related content.

#androidWithSagar #android #androiddevelopment #development #compose #kotlin