diff --git a/README.md b/README.md
index f29f9f5e7..fb4cb6ae1 100644
--- a/README.md
+++ b/README.md
@@ -159,38 +159,44 @@ composable elements to the content of the `GoogleMap`.
```kotlin
GoogleMap(
- //...
+ googleMapOptionsFactory = {
+ GoogleMapOptions().mapId("DEMO_MAP_ID")
+ },
+ //...
) {
- Marker(
+ AdvancedMarker(
state = MarkerState(position = LatLng(-34, 151)),
title = "Marker in Sydney"
)
- Marker(
+ AdvancedMarker(
state = MarkerState(position = LatLng(35.66, 139.6)),
title = "Marker in Tokyo"
)
}
```
-You can also customize the marker you want to add by using `MarkerComposable`.
+You can customize a marker by using `PinConfig` with an `AdvancedMarker`.
```kotlin
val state = MyState()
GoogleMap(
- //...
+ googleMapOptionsFactory = {
+ GoogleMapOptions().mapId("DEMO_MAP_ID")
+ },
+ //...
) {
- MarkerComposable(
- keys = arrayOf(state),
+ val pinConfig = PinConfig.builder()
+ .setBackgroundColor(Color.MAGENTA)
+ .build()
+
+ AdvancedMarker(
state = MarkerState(position = LatLng(-34, 151)),
- ) {
- MyCustomMarker(state)
- }
+ title = "Magenta marker in Sydney",
+ pinConfig = pinConfig
+ )
}
```
-As this Composable is backed by a rendering of your Composable into a Bitmap, it will not render
-your Composable every recomposition. So to trigger a new render of your Composable, you can pass
-all variables that your Composable depends on to trigger a render whenever one of them change.
@@ -283,7 +289,7 @@ This library provides optional utilities in the `maps-compose-utils` library fro
The marker clustering utility helps you manage multiple markers at different zoom levels.
When a user views the map at a high zoom level, the individual markers show on the map. When the user zooms out, the markers gather together into clusters, to make viewing the map easier.
-The [MapClusteringActivity](app/src/main/java/com/google/maps/android/compose/MapClusteringActivity.kt) demonstrates usage.
+The [MarkerClusteringActivity](app/src/main/java/com/google/maps/android/compose/MarkerClusteringActivity.kt) demonstrates usage.
```kotlin
Clustering(
@@ -364,12 +370,13 @@ Contributions are welcome and encouraged! See [contributing] for more info.
## Support
-Encounter an issue while using this library?
+This library is offered via an open source [license](LICENSE). It is not governed by the Google Maps Platform [Technical Support Services Guidelines](https://cloud.google.com/maps-platform/terms/tssg?utm_source=github&utm_medium=documentation&utm_campaign=&utm_content=android_oss), the [SLA](https://cloud.google.com/maps-platform/terms/sla?utm_source=github&utm_medium=documentation&utm_campaign=&utm_content=android_oss), or the [Deprecation Policy](https://cloud.google.com/maps-platform/terms?utm_source=github&utm_medium=documentation&utm_campaign=&utm_content=android_oss) (however, any Google Maps Platform services used by the library remain subject to the Google Maps Platform Terms of Service).
+
+This library adheres to [semantic versioning](https://semver.org/) to indicate when backwards-incompatible changes are introduced.
-If you find a bug or have a feature request, please [file an issue].
-Or, if you'd like to contribute, send us a [pull request] and refer to our [code of conduct].
+If you find a bug, or have a feature request, please [file an issue] on GitHub.
-You can also discuss this library on our [Discord server].
+If you would like to get answers to technical questions from other Google Maps Platform developers, ask through one of our [developer community channels](https://developers.google.com/maps/developer-community?utm_source=github&utm_medium=documentation&utm_campaign=&utm_content=android_oss) including the Google Maps Platform [Discord server].
[maps-sdk]: https://developers.google.com/maps/documentation/android-sdk
[api-key]: https://developers.google.com/maps/documentation/android-sdk/get-api-key
diff --git a/app/build.gradle b/app/build.gradle
index c823a2ee2..8701f609f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -58,15 +58,11 @@ dependencies {
androidTestImplementation libs.androidx.test.compose.ui
androidTestImplementation libs.coroutines
- // Uncomment the implementation 'com.google...` declaration and comment out the project
- // declaration if you want to test the sample app with a Maven Central release of the library.
- //implementation "com.google.maps.android:maps-compose:2.2.1"
+ // Instead of the lines below, regular apps would load these libraries from Maven according to
+ // the README installation instructions
implementation project(':maps-compose')
- //implementation "com.google.maps.android:maps-compose-widgets:1.0.0"
implementation project(':maps-compose-widgets')
- //implementation "com.google.maps.android:maps-compose-utils:1.0.0"
implementation project(':maps-compose-utils')
- implementation libs.maps.playservice
}
secrets {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 69d8deea0..c7ea8c0db 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -42,11 +42,14 @@
+
Boolean = {
+ Log.d(TAG, "${it.title} was clicked")
+ cameraPositionState.projection?.let { projection ->
+ Log.d(TAG, "The current projection is: $projection")
+ }
+ false
+ }
+ Box(Modifier.fillMaxSize()) {
+ GoogleMap(
+ modifier = Modifier.matchParentSize(),
+ googleMapOptionsFactory = {
+ GoogleMapOptions().mapId("DEMO_MAP_ID")
+ },
+ cameraPositionState = cameraPositionState,
+ properties = mapProperties,
+ onPOIClick = {
+ Log.d(TAG, "POI clicked: ${it.name}")
+ }
+ ) {
+
+ val textView = TextView(this@AdvancedMarkersActivity)
+ textView.text = "Hello!!"
+ textView.setBackgroundColor(Color.BLACK)
+ textView.setTextColor(Color.YELLOW)
+
+ AdvancedMarker(
+ state = marker4State,
+ onClick = markerClick,
+ collisionBehavior = 1,
+ iconView = textView,
+ title="Marker 4"
+ )
+
+ val pinConfig = PinConfig.builder()
+ .setBackgroundColor(Color.MAGENTA)
+ .setBorderColor(Color.WHITE)
+ .build()
+
+ AdvancedMarker(
+ state = marker1State,
+ onClick = markerClick,
+ collisionBehavior = 1,
+ pinConfig = pinConfig,
+ title="Marker 1"
+ )
+
+ val glyphOne = PinConfig.Glyph("A", Color.BLACK)
+ val pinConfig2 = PinConfig.builder()
+ .setGlyph(glyphOne)
+ .build()
+
+ AdvancedMarker(
+ state = marker2State,
+ onClick = markerClick,
+ collisionBehavior = 1,
+ pinConfig = pinConfig2,
+ title="Marker 2"
+ )
+
+ val glyphImage: Int = ic_menu_myplaces
+ val descriptor = BitmapDescriptorFactory.fromResource(glyphImage)
+ val pinConfig3 = PinConfig.builder()
+ .setGlyph(PinConfig.Glyph(descriptor))
+ .build()
+
+ AdvancedMarker(
+ state = marker3State,
+ onClick = markerClick,
+ collisionBehavior = 1,
+ pinConfig = pinConfig3,
+ title="Marker 3"
+ )
+
+ }
+ }
+ }
+ }
+
+ override fun onMapsSdkInitialized(renderer: MapsInitializer.Renderer) {
+ when (renderer) {
+ MapsInitializer.Renderer.LATEST -> Log.d("MapsDemo", "The latest version of the renderer is used.")
+ MapsInitializer.Renderer.LEGACY -> Log.d("MapsDemo", "The legacy version of the renderer is used.")
+ else -> {}
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/google/maps/android/compose/MainActivity.kt b/app/src/main/java/com/google/maps/android/compose/MainActivity.kt
index ff8e0bdc7..f6f8ada02 100644
--- a/app/src/main/java/com/google/maps/android/compose/MainActivity.kt
+++ b/app/src/main/java/com/google/maps/android/compose/MainActivity.kt
@@ -32,8 +32,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.google.maps.android.compose.theme.MapsComposeSampleTheme
-private const val TAG = "MapSampleActivity"
-
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -63,16 +61,23 @@ class MainActivity : ComponentActivity() {
Text(getString(R.string.basic_map_activity))
}
Spacer(modifier = Modifier.padding(5.dp))
+ Button(
+ onClick = {
+ context.startActivity(Intent(context, AdvancedMarkersActivity::class.java))
+ }) {
+ Text(getString(R.string.advanced_markers))
+ }
+ Spacer(modifier = Modifier.padding(5.dp))
Button(
onClick = {
context.startActivity(
Intent(
context,
- MapInColumnActivity::class.java
+ MarkerClusteringActivity::class.java
)
)
}) {
- Text(getString(R.string.map_in_column_activity))
+ Text(getString(R.string.marker_clustering_activity))
}
Spacer(modifier = Modifier.padding(5.dp))
Button(
@@ -80,11 +85,11 @@ class MainActivity : ComponentActivity() {
context.startActivity(
Intent(
context,
- MapClusteringActivity::class.java
+ MapInColumnActivity::class.java
)
)
}) {
- Text(getString(R.string.map_clustering_activity))
+ Text(getString(R.string.map_in_column_activity))
}
Spacer(modifier = Modifier.padding(5.dp))
Button(
diff --git a/app/src/main/java/com/google/maps/android/compose/MapClusteringActivity.kt b/app/src/main/java/com/google/maps/android/compose/MarkerClusteringActivity.kt
similarity index 97%
rename from app/src/main/java/com/google/maps/android/compose/MapClusteringActivity.kt
rename to app/src/main/java/com/google/maps/android/compose/MarkerClusteringActivity.kt
index d545dd8ee..328076c5b 100644
--- a/app/src/main/java/com/google/maps/android/compose/MapClusteringActivity.kt
+++ b/app/src/main/java/com/google/maps/android/compose/MarkerClusteringActivity.kt
@@ -28,9 +28,9 @@ import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.compose.clustering.Clustering
import kotlin.random.Random
-private val TAG = MapClusteringActivity::class.simpleName
+private val TAG = MarkerClusteringActivity::class.simpleName
-class MapClusteringActivity : ComponentActivity() {
+class MarkerClusteringActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
@@ -60,7 +60,7 @@ fun GoogleMapClustering(items: List) {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = rememberCameraPositionState {
- position = CameraPosition.fromLatLngZoom(singapore, 10f)
+ position = CameraPosition.fromLatLngZoom(singapore, 6f)
}
) {
Clustering(
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index dd10f260d..a74afdd8e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -18,8 +18,9 @@
android-maps-compose
"Maps Compose Demos \uD83D\uDDFA"
Basic Map
+ Advanced Markers
Map In Column
- Map Clustering
+ Marker Clustering
Location Tracking
Scale Bar
Street View
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 80f803570..c8a074d53 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -13,7 +13,7 @@ junitktx = "1.1.5"
junit = "4.13.2"
kotlin = "1.9.10"
material = "1.9.0"
-mapsktx = "4.0.0"
+mapsktx = "5.0.0"
mapsecrets = "2.0.1"
[libraries]
@@ -39,7 +39,6 @@ kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "ko
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
maps-ktx-std = { module = "com.google.maps.android:maps-ktx", version.ref = "mapsktx" }
maps-ktx-utils = { module = "com.google.maps.android:maps-utils-ktx", version.ref = "mapsktx" }
-maps-playservice = { module = "com.google.android.gms:play-services-maps", version.require = "18.1.0" }
maps-secrets-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "mapsecrets" }
material = { module = "com.google.android.material:material", version.ref = "material" }
test-junit = { module = "junit:junit", version.ref = "junit" }
diff --git a/maps-compose-utils/build.gradle b/maps-compose-utils/build.gradle
index 2febaa60e..c5ae7898b 100644
--- a/maps-compose-utils/build.gradle
+++ b/maps-compose-utils/build.gradle
@@ -41,7 +41,5 @@ dependencies {
implementation platform(libs.androidx.compose.bom)
implementation libs.androidx.compose.ui
implementation libs.kotlin
- implementation libs.maps.playservice
- implementation libs.maps.ktx.std
api libs.maps.ktx.utils
}
diff --git a/maps-compose-widgets/build.gradle b/maps-compose-widgets/build.gradle
index ecd945fc8..ca0187bfd 100644
--- a/maps-compose-widgets/build.gradle
+++ b/maps-compose-widgets/build.gradle
@@ -42,7 +42,6 @@ dependencies {
implementation libs.androidx.compose.material
implementation libs.androidx.core
implementation libs.kotlin
- implementation libs.maps.playservice
implementation libs.maps.ktx.std
implementation libs.maps.ktx.utils
diff --git a/maps-compose/build.gradle b/maps-compose/build.gradle
index 5da3074a3..ce89fd508 100644
--- a/maps-compose/build.gradle
+++ b/maps-compose/build.gradle
@@ -39,7 +39,6 @@ dependencies {
implementation libs.androidx.core
implementation libs.androidx.compose.foundation
implementation libs.kotlin
- implementation libs.maps.playservice
implementation libs.maps.ktx.std
testImplementation libs.test.junit
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt
index 5412e917b..eff2e37e9 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt
@@ -14,6 +14,7 @@
package com.google.maps.android.compose
+import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposeNode
import androidx.compose.runtime.CompositionContext
@@ -27,9 +28,12 @@ import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
+import com.google.android.gms.maps.model.AdvancedMarkerOptions
import com.google.android.gms.maps.model.BitmapDescriptor
+import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
+import com.google.android.gms.maps.model.PinConfig
import com.google.maps.android.ktx.addMarker
internal class MarkerNode(
@@ -46,6 +50,7 @@ internal class MarkerNode(
override fun onAttached() {
markerState.marker = marker
}
+
override fun onRemoved() {
markerState.marker = null
marker.remove()
@@ -514,3 +519,204 @@ private fun MarkerImpl(
}
)
}
+
+
+/**
+ * A composable for an advanced marker on the map.
+ *
+ * @param state the [MarkerState] to be used to control or observe the marker
+ * state such as its position and info window
+ * @param alpha the alpha (opacity) of the marker
+ * @param anchor the anchor for the marker image
+ * @param draggable sets the draggability for the marker
+ * @param flat sets if the marker should be flat against the map
+ * @param infoWindowAnchor the anchor point of the info window on the marker image
+ * @param rotation the rotation of the marker in degrees clockwise about the marker's anchor point
+ * @param snippet the snippet for the marker
+ * @param tag optional tag to associate with the marker
+ * @param title the title for the marker
+ * @param visible the visibility of the marker
+ * @param zIndex the z-index of the marker
+ * @param onClick a lambda invoked when the marker is clicked
+ * @param onInfoWindowClick a lambda invoked when the marker's info window is clicked
+ * @param onInfoWindowClose a lambda invoked when the marker's info window is closed
+ * @param onInfoWindowLongClick a lambda invoked when the marker's info window is long clicked
+ * @param pinConfig the PinConfig object that will be used for the advanced marker
+ * @param iconView the custom view to be used on the advanced marker
+ * @param collisionBehavior the expected collision behavior
+ */
+@Composable
+@GoogleMapComposable
+public fun AdvancedMarker(
+ state: MarkerState = rememberMarkerState(),
+ alpha: Float = 1.0f,
+ anchor: Offset = Offset(0.5f, 1.0f),
+ draggable: Boolean = false,
+ flat: Boolean = false,
+ infoWindowAnchor: Offset = Offset(0.5f, 0.0f),
+ rotation: Float = 0.0f,
+ snippet: String? = null,
+ tag: Any? = null,
+ title: String? = null,
+ visible: Boolean = true,
+ zIndex: Float = 0.0f,
+ onClick: (Marker) -> Boolean = { false },
+ onInfoWindowClick: (Marker) -> Unit = {},
+ onInfoWindowClose: (Marker) -> Unit = {},
+ onInfoWindowLongClick: (Marker) -> Unit = {},
+ pinConfig: PinConfig? = null,
+ iconView: View? = null,
+ collisionBehavior: Int = AdvancedMarkerOptions.CollisionBehavior.REQUIRED
+) {
+
+ AdvancedMarkerImpl(
+ state = state,
+ alpha = alpha,
+ anchor = anchor,
+ draggable = draggable,
+ flat = flat,
+ infoWindowAnchor = infoWindowAnchor,
+ rotation = rotation,
+ snippet = snippet,
+ tag = tag,
+ title = title,
+ visible = visible,
+ zIndex = zIndex,
+ onClick = onClick,
+ onInfoWindowClick = onInfoWindowClick,
+ onInfoWindowClose = onInfoWindowClose,
+ onInfoWindowLongClick = onInfoWindowLongClick,
+ pinConfig = pinConfig,
+ iconView = iconView,
+ collisionBehavior = collisionBehavior
+ )
+}
+
+/**
+ * Internal implementation for an advanced marker on a Google map.
+ *
+ * @param state the [MarkerState] to be used to control or observe the marker
+ * state such as its position and info window
+ * @param alpha the alpha (opacity) of the marker
+ * @param anchor the anchor for the marker image
+ * @param draggable sets the draggability for the marker
+ * @param flat sets if the marker should be flat against the map
+ * @param infoWindowAnchor the anchor point of the info window on the marker image
+ * @param rotation the rotation of the marker in degrees clockwise about the marker's anchor point
+ * @param snippet the snippet for the marker
+ * @param tag optional tag to associate with the marker
+ * @param title the title for the marker
+ * @param visible the visibility of the marker
+ * @param zIndex the z-index of the marker
+ * @param onClick a lambda invoked when the marker is clicked
+ * @param onInfoWindowClick a lambda invoked when the marker's info window is clicked
+ * @param onInfoWindowClose a lambda invoked when the marker's info window is closed
+ * @param onInfoWindowLongClick a lambda invoked when the marker's info window is long clicked
+ * @param infoWindow optional composable lambda expression for customizing
+ * the entire info window. If this value is non-null, the value in infoContent]
+ * will be ignored.
+ * @param infoContent optional composable lambda expression for customizing
+ * the info window's content. If this value is non-null, [infoWindow] must be null.
+ * @param pinConfig the PinConfig object that will be used for the advanced marker
+ * @param iconView the custom view to be used on the advanced marker
+ * @param collisionBehavior the expected collision behavior
+ */
+@Composable
+@GoogleMapComposable
+private fun AdvancedMarkerImpl(
+
+ state: MarkerState = rememberMarkerState(),
+ alpha: Float = 1.0f,
+ anchor: Offset = Offset(0.5f, 1.0f),
+ draggable: Boolean = false,
+ flat: Boolean = false,
+ infoWindowAnchor: Offset = Offset(0.5f, 0.0f),
+ rotation: Float = 0.0f,
+ snippet: String? = null,
+ tag: Any? = null,
+ title: String? = null,
+ visible: Boolean = true,
+ zIndex: Float = 0.0f,
+ onClick: (Marker) -> Boolean = { false },
+ onInfoWindowClick: (Marker) -> Unit = {},
+ onInfoWindowClose: (Marker) -> Unit = {},
+ onInfoWindowLongClick: (Marker) -> Unit = {},
+ infoWindow: (@Composable (Marker) -> Unit)? = null,
+ infoContent: (@Composable (Marker) -> Unit)? = null,
+ pinConfig: PinConfig? = null,
+ iconView: View? = null,
+ collisionBehavior: Int = AdvancedMarkerOptions.CollisionBehavior.REQUIRED
+) {
+
+ val mapApplier = currentComposer.applier as? MapApplier
+ val compositionContext = rememberCompositionContext()
+
+ val advancedMarkerOptions = AdvancedMarkerOptions()
+ .position(state.position)
+ .collisionBehavior(collisionBehavior)
+ if (iconView != null) {
+ advancedMarkerOptions.iconView(iconView)
+ } else if (pinConfig != null) {
+ advancedMarkerOptions.icon(BitmapDescriptorFactory.fromPinConfig(pinConfig))
+ }
+
+ ComposeNode(
+ factory = {
+ val marker = mapApplier?.map?.addMarker(advancedMarkerOptions)
+ ?: error("Error adding marker")
+ marker.tag = tag
+ MarkerNode(
+ compositionContext = compositionContext,
+ marker = marker,
+ markerState = state,
+ onMarkerClick = onClick,
+ onInfoWindowClick = onInfoWindowClick,
+ onInfoWindowClose = onInfoWindowClose,
+ onInfoWindowLongClick = onInfoWindowLongClick,
+ infoContent = infoContent,
+ infoWindow = infoWindow,
+ )
+ },
+ update = {
+ update(onClick) { this.onMarkerClick = it }
+ update(onInfoWindowClick) { this.onInfoWindowClick = it }
+ update(onInfoWindowClose) { this.onInfoWindowClose = it }
+ update(onInfoWindowLongClick) { this.onInfoWindowLongClick = it }
+ update(infoContent) { this.infoContent = it }
+ update(infoWindow) { this.infoWindow = it }
+
+ set(alpha) { this.marker.alpha = it }
+ set(anchor) { this.marker.setAnchor(it.x, it.y) }
+ set(draggable) { this.marker.isDraggable = it }
+ set(flat) { this.marker.isFlat = it }
+ set(infoWindowAnchor) { this.marker.setInfoWindowAnchor(it.x, it.y) }
+ set(state.position) { this.marker.position = it }
+ set(rotation) { this.marker.rotation = it }
+ set(snippet) {
+ this.marker.snippet = it
+ if (this.marker.isInfoWindowShown) {
+ this.marker.showInfoWindow()
+ }
+ }
+ set(tag) { this.marker.tag = it }
+ set(title) {
+ this.marker.title = it
+ if (this.marker.isInfoWindowShown) {
+ this.marker.showInfoWindow()
+ }
+ }
+ set(pinConfig) {
+ if (iconView == null) {
+ this.marker.setIcon(pinConfig?.let { it1 ->
+ BitmapDescriptorFactory.fromPinConfig(
+ it1
+ )
+ })
+ }
+ }
+
+ set(visible) { this.marker.isVisible = it }
+ set(zIndex) { this.marker.zIndex = it }
+ }
+ )
+}