Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GoogleMap DroidCon SF 23

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

GoogleMap DroidCon SF 23

Avatar for Brian Gardner

Brian Gardner

June 09, 2023
Tweet

More Decks by Brian Gardner

Other Decks in Programming

Transcript

  1. / / technically incorrect (the best kind) compose - maps

    = { group = “com.google.maps.android", name = “maps - compose", version = “2.11.4” } play - services - maps = { group = "com.google.android.gms", name = "play - services - maps", version = "18.1.0" }
  2. <?xml version="1.0" encoding="utf-8"?> <manifest …> <application …> … / /

    maps api key <meta - data android:name="com.google.android.geo.API_KEY" android:value="${mapsApiKey}" / > < / application> < / manifest>
  3. public class MapProperties( public val isBuildingEnabled: Boolean = false, public

    val isIndoorEnabled: Boolean = false, public val isMyLocationEnabled: Boolean = false, )
  4. public class MapProperties( public val isBuildingEnabled: Boolean = false, public

    val isIndoorEnabled: Boolean = false, public val isMyLocationEnabled: Boolean = false, public val isTrafficEnabled: Boolean = false, )
  5. public class MapProperties( public val isBuildingEnabled: Boolean = false, public

    val isIndoorEnabled: Boolean = false, public val isMyLocationEnabled: Boolean = false, public val isTrafficEnabled: Boolean = false, public val latLngBoundsForCameraTarget: LatLngBounds? = null, )
  6. public class MapProperties( public val isBuildingEnabled: Boolean = false, public

    val isIndoorEnabled: Boolean = false, public val isMyLocationEnabled: Boolean = false, public val isTrafficEnabled: Boolean = false, public val latLngBoundsForCameraTarget: LatLngBounds? = null, public val mapStyleOptions: MapStyleOptions? = null, )
  7. public class MapProperties( public val isBuildingEnabled: Boolean = false, public

    val isIndoorEnabled: Boolean = false, public val isMyLocationEnabled: Boolean = false, public val isTrafficEnabled: Boolean = false, public val latLngBoundsForCameraTarget: LatLngBounds? = null, public val mapStyleOptions: MapStyleOptions? = null, public val mapType: MapType = MapType.NORMAL, )
  8. public class MapProperties( public val isBuildingEnabled: Boolean = false, public

    val isIndoorEnabled: Boolean = false, public val isMyLocationEnabled: Boolean = false, public val isTrafficEnabled: Boolean = false, public val latLngBoundsForCameraTarget: LatLngBounds? = null, public val mapStyleOptions: MapStyleOptions? = null, public val mapType: MapType = MapType.NORMAL, public val maxZoomPreference: Float = 21.0f, public val minZoomPreference: Float = 3.0f, )
  9. { // https://mapstyle.withgoogle.com/ "featureType": "all", "elementType": "geometry", "stylers": [ {

    "color": "#242f3e" } ] }, { "featureType": "all", "elementType": "labels.text.stroke", "stylers": [ { "lightness": -80 } ] },
  10. val mapProperties by remember(hasLocationPermission) { mutableStateOf( MapProperties( mapStyleOptions = mapStyleOptions,

    maxZoomPreference = ZOOM_MAX, minZoomPreference = ZOOM_MIN, isMyLocationEnabled = hasLocationPermission, ) ) }
  11. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, )
  12. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, )
  13. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, public val rotationGesturesEnabled: Boolean = true, )
  14. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, public val rotationGesturesEnabled: Boolean = true, public val scrollGesturesEnabled: Boolean = true, )
  15. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, public val rotationGesturesEnabled: Boolean = true, public val scrollGesturesEnabled: Boolean = true, public val scrollGesturesEnabledDuringRotateOrZoom: Boolean = true, )
  16. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, public val rotationGesturesEnabled: Boolean = true, public val scrollGesturesEnabled: Boolean = true, public val scrollGesturesEnabledDuringRotateOrZoom: Boolean = true, public val tiltGesturesEnabled: Boolean = true, )
  17. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, public val rotationGesturesEnabled: Boolean = true, public val scrollGesturesEnabled: Boolean = true, public val scrollGesturesEnabledDuringRotateOrZoom: Boolean = true, public val tiltGesturesEnabled: Boolean = true, public val zoomControlsEnabled: Boolean = true, )
  18. public class MapUiSettings( public val compassEnabled: Boolean = true, public

    val indoorLevelPickerEnabled: Boolean = true, public val mapToolbarEnabled: Boolean = true, public val myLocationButtonEnabled: Boolean = true, public val rotationGesturesEnabled: Boolean = true, public val scrollGesturesEnabled: Boolean = true, public val scrollGesturesEnabledDuringRotateOrZoom: Boolean = true, public val tiltGesturesEnabled: Boolean = true, public val zoomControlsEnabled: Boolean = true, public val zoomGesturesEnabled: Boolean = true, )
  19. val mapUiSettings by remember { mutableStateOf( MapUiSettings( indoorLevelPickerEnabled = false,

    mapToolbarEnabled = false, myLocationButtonEnabled = false, rotationGesturesEnabled = false, tiltGesturesEnabled = false, zoomControlsEnabled = false, ), ) }
  20. LaunchedEffect(cameraState.isMoving) { if (!cameraState.isMoving) { / / notify presenter of

    new location onEvent( OnCameraPositionChanged( lat = cameraState.position.target.latitude, lng = cameraState.position.target.longitude, zoom = cameraState.position.zoom ) ) } }
  21. GoogleMap(…) { Marker( state = rememberMarkerState( key = location.token, position

    = LatLng(location.lat, location.lng) ), title = "Cash Money ATM" ) }
  22. GoogleMap(…) { Marker( state = rememberMarkerState( key = location.token, position

    = LatLng(location.lat, location.lng) ), title = "Cash Money ATM" ) }
  23. MarkerInfoWindow( state = rememberMarkerState( key = location.token, position = LatLng(location.lat,

    location.lng) ), ) { Surface( modif i er = Modif i er.background(MaterialTheme.colorScheme.background) .padding(8.dp) ) { Column { Text(text = "Cash Money ATM") Text(text = "Withdraw or deposit here!") } } }
  24. MarkerInfoWindow( state = rememberMarkerState( key = location.token, position = LatLng(location.lat,

    location.lng) ), ) { Surface( modif i er = Modif i er.background(MaterialTheme.colorScheme.background) .padding(8.dp) ) { Column { Text(text = "Cash Money ATM") Text(text = "Withdraw or deposit here!") } } }
  25. MarkerInfoWindow( state = rememberMarkerState( key = location.token, position = LatLng(location.lat,

    location.lng) ), ) { Surface( modif i er = Modif i er.background(MaterialTheme.colorScheme.background) .padding(8.dp) ) { Column { Text(text = "Cash Money ATM") Text(text = "Withdraw or deposit here!") } } }
  26. val resources = LocalContext.current.resources val iconBitmapDescriptor = remember { val

    iconBitmap = ResourcesCompat.getDrawable( resources, R.drawable.icon_marker, null ) ? . toBitmap() ! ! BitmapDescriptorFactory.fromBitmap(iconBitmap) } Marker( state = rememberMarkerState( key = location.token, position = LatLng(location.lat, location.lng) ), icon = iconBitmapDescriptor, )
  27. val resources = LocalContext.current.resources val iconBitmapDescriptor = remember { val

    iconBitmap = ResourcesCompat.getDrawable( resources, R.drawable.icon_marker, null ) ? . toBitmap() ! ! BitmapDescriptorFactory.fromBitmap(iconBitmap) } Marker( state = rememberMarkerState( key = location.token, position = LatLng(location.lat, location.lng) ), icon = iconBitmapDescriptor, )
  28. val resources = LocalContext.current.resources val iconBitmapDescriptor = remember { val

    iconBitmap = ResourcesCompat.getDrawable( resources, R.drawable.icon_marker, null ) ? . toBitmap() ! ! BitmapDescriptorFactory.fromBitmap(iconBitmap) } Marker( state = rememberMarkerState( key = location.token, position = LatLng(location.lat, location.lng) ), icon = iconBitmapDescriptor, )
  29. val resources = LocalContext.current.resources val iconBitmapDescriptor = remember { val

    iconBitmap = ResourcesCompat.getDrawable( resources, R.drawable.icon_marker, null ) ? . toBitmap() ! ! BitmapDescriptorFactory.fromBitmap(iconBitmap) } Marker( state = rememberMarkerState( key = location.token, position = LatLng(location.lat, location.lng) ), icon = iconBitmapDescriptor, )
  30. val resources = LocalContext.current.resources val iconBitmapDescriptor = remember { val

    iconBitmap = ResourcesCompat.getDrawable( resources, R.drawable.icon_marker, null ) ? . toBitmap() ! ! BitmapDescriptorFactory.fromBitmap(iconBitmap) } Marker( state = rememberMarkerState( key = location.token, position = LatLng(location.lat, location.lng) ), icon = iconBitmapDescriptor, )
  31. for (marker in markers) { Marker( state = rememberMarkerState( key

    = marker.token, position = LatLng(marker.lat, marker.lng) ), ) }
  32. Options 1. Can use android-maps-utils library, but need access to

    GoogleMap instance • MapE ff ect { map: GoogleMap -> } provided in android-maps-utils library 2. Clustering support in maps-compose-util library
  33. Don’t use MapEffect 1. Manually creating ClusterManager is a pain

    2. Manual ClusterManager overrides Marker composable click handling 3. ClusterManager handles onCameraIdle callback 4. Having multiple types of markers or clusters requires MarkerManager
  34. compose - maps - utils = { group = "com.google.maps.android",

    name = “maps - compose - utils“, version.ref = “2.11.4” }
  35. class MapClusterItem( val location: MapLocation ) : ClusterItem { override

    fun getPosition() = LatLng(location.lat, location.lng) override fun getTitle() = location.title override fun getSnippet() = location.description }
  36. class MapClusterItem( val location: MapLocation ) : ClusterItem { override

    fun getPosition() = LatLng(location.lat, location.lng) override fun getTitle() = location.title override fun getSnippet() = location.description }
  37. val clusterItems = remember(markers) { markers.map { MapClusterItem(it) } }

    GoogleMap(…){ Clustering(items = clusterItems) }
  38. val clusterItems = remember(markers) { markers.map { MapClusterItem(it) } }

    GoogleMap(…){ Clustering(items = clusterItems) }
  39. val clusterItems = remember(markers) { markers.map { MapClusterItem(it) } }

    GoogleMap(…){ Clustering(items = clusterItems) }
  40. Clustering( items = clusterItems, clusterContent = { cluster - >

    CustomClusterContent(cluster) }, clusterItemContent = { mapClusterItem - > ClusterItemContent(mapClusterItem) } )
  41. Clustering( items = clusterItems, clusterContent = { cluster - >

    CustomClusterContent(cluster) }, clusterItemContent = { mapClusterItem - > ClusterItemContent(mapClusterItem) } )
  42. Clustering( items = clusterItems, clusterContent = { cluster - >

    CustomClusterContent(cluster) }, clusterItemContent = { mapClusterItem - > ClusterItemContent(mapClusterItem) } )
  43. Clustering( items = clusterItems, onClusterClick = { cluster - >

    onEvent( OnClusterClicked( cluster.position.latitude, cluster.position.longitude, cluster.items.map { it.location } ) ) true }, )
  44. Clustering( items = clusterItems, onClusterClick = { … }, onClusterItemClick

    = { mapClusterItem - > onEvent( OnClusterItemClicked( mapClusterItem.location.token ) ) true }, )
  45. Paparazzi testing // When in preview, early return a Box

    with // the received modifier preserving layout if (LocalInspectionMode.current) { Box(modifier = modifier) return }