Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

GoogleMap DroidCon SF 23

GoogleMap DroidCon SF 23

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 }