diff --git a/.github/workflows/lint-report.yml b/.github/workflows/lint-report.yml
index c92c2c8d..e5748066 100644
--- a/.github/workflows/lint-report.yml
+++ b/.github/workflows/lint-report.yml
@@ -35,11 +35,26 @@ jobs:
- name: Run Android Lint
run: ./gradlew lint
- - name: Merge SARIF files
- run: |
- jq -s '{ "$schema": "https://json.schemastore.org/sarif-2.1.0", "version": "2.1.0", "runs": map(.runs) | add }' maps-compose/build/reports/lint-results.sarif maps-compose-utils/build/reports/lint-results.sarif maps-compose-widgets/build/reports/lint-results.sarif maps-app/build/reports/lint-results.sarif > merged.sarif
+ - name: Upload SARIF from maps-compose
+ uses: github/codeql-action/upload-sarif@v3
+ with:
+ sarif_file: maps-compose/build/reports/lint-results.sarif
+ category: maps-compose
+
+ - name: Upload SARIF from maps-compose-utils
+ uses: github/codeql-action/upload-sarif@v3
+ with:
+ sarif_file: maps-compose-utils/build/reports/lint-results.sarif
+ category: maps-compose-utils
+
+ - name: Upload SARIF from maps-compose-widgets
+ uses: github/codeql-action/upload-sarif@v3
+ with:
+ sarif_file: maps-compose-widgets/build/reports/lint-results.sarif
+ category: maps-compose-widgets
- - name: Upload SARIF file
+ - name: Upload SARIF from maps-app
uses: github/codeql-action/upload-sarif@v3
with:
- sarif_file: merged.sarif
+ sarif_file: maps-app/build/reports/lint-results.sarif
+ category: maps-app
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index fa02bfbe..22e698a4 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -18,6 +18,8 @@ mapsecrets = "2.0.1"
mapsktx = "5.2.0"
org-jacoco-core = "0.8.12"
screenshot = "0.0.1-alpha10"
+constraintlayout = "2.2.1"
+material = "1.12.0"
[libraries]
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
@@ -48,6 +50,8 @@ maps-ktx-utils = { module = "com.google.maps.android:maps-utils-ktx", version.re
maps-secrets-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "mapsecrets" }
org-jacoco-core = { module = "org.jacoco:org.jacoco.core", version.ref = "org-jacoco-core" }
test-junit = { module = "junit:junit", version.ref = "junit" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
diff --git a/maps-app/build.gradle.kts b/maps-app/build.gradle.kts
index 7af6545d..53fa4232 100644
--- a/maps-app/build.gradle.kts
+++ b/maps-app/build.gradle.kts
@@ -65,6 +65,8 @@ dependencies {
implementation(libs.kotlin)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.androidx.compose.ui.preview.tooling)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.material)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.leakcanary.android)
diff --git a/maps-app/src/main/AndroidManifest.xml b/maps-app/src/main/AndroidManifest.xml
index 0063f72d..484beb71 100644
--- a/maps-app/src/main/AndroidManifest.xml
+++ b/maps-app/src/main/AndroidManifest.xml
@@ -72,6 +72,9 @@
+
diff --git a/maps-app/src/main/java/com/google/maps/android/compose/FragmentDemoActivity.kt b/maps-app/src/main/java/com/google/maps/android/compose/FragmentDemoActivity.kt
new file mode 100644
index 00000000..0caaf178
--- /dev/null
+++ b/maps-app/src/main/java/com/google/maps/android/compose/FragmentDemoActivity.kt
@@ -0,0 +1,23 @@
+package com.google.maps.android.compose
+
+
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.widget.ViewPager2
+
+class FragmentDemoActivity : FragmentActivity() {
+
+ private lateinit var viewPager: ViewPager2
+ private lateinit var pagerAdapter: MapFragmentPagerAdapter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_fragment_demo)
+
+ viewPager = findViewById(R.id.view_pager)
+
+ pagerAdapter = MapFragmentPagerAdapter(this)
+ viewPager.adapter = pagerAdapter
+
+ }
+}
diff --git a/maps-app/src/main/java/com/google/maps/android/compose/GoogleMapComposeFragment.kt b/maps-app/src/main/java/com/google/maps/android/compose/GoogleMapComposeFragment.kt
new file mode 100644
index 00000000..fde1cdc8
--- /dev/null
+++ b/maps-app/src/main/java/com/google/maps/android/compose/GoogleMapComposeFragment.kt
@@ -0,0 +1,142 @@
+package com.google.maps.android.compose
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.geometry.Offset // For MarkerComposable anchor
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.fragment.app.Fragment
+import com.google.android.gms.maps.model.CameraPosition
+import com.google.android.gms.maps.model.LatLng
+
+
+enum class MarkerType {
+ CUSTOM_CONTENT_MARKER, // To use com.google.maps.android.compose.MarkerComposable with your Text
+ STANDARD_MARKER_WITH_SNIPPET // To use the standard com.google.maps.android.compose.Marker
+}
+
+data class MapConfig(
+ val initialLatLng: LatLng,
+ val initialZoom: Float,
+ val title: String,
+ val markerType: MarkerType = MarkerType.CUSTOM_CONTENT_MARKER, // Default
+ val standardMarkerSnippet: String? = null
+)
+
+class GoogleMapComposeFragment : Fragment() {
+
+ private var mapConfig: MapConfig? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ arguments?.let {
+ val lat = it.getDouble(ARG_LAT, Double.NaN)
+ val lng = it.getDouble(ARG_LNG, Double.NaN)
+ val zoom = it.getFloat(ARG_ZOOM, 10f)
+ val title = it.getString(ARG_TITLE, "Map")
+
+ val markerTypeName = it.getString(ARG_MARKER_TYPE)
+ val markerType = markerTypeName?.let { name ->
+ try {
+ MarkerType.valueOf(name)
+ } catch (e: IllegalArgumentException) {
+ MarkerType.CUSTOM_CONTENT_MARKER
+ }
+ } ?: MarkerType.CUSTOM_CONTENT_MARKER
+
+ val snippet = it.getString(ARG_STANDARD_MARKER_SNIPPET)
+
+ if (!lat.isNaN() && !lng.isNaN()) {
+ mapConfig = MapConfig(
+ initialLatLng = LatLng(lat, lng),
+ initialZoom = zoom,
+ title = title,
+ markerType = markerType,
+ standardMarkerSnippet = snippet
+ )
+ }
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View = ComposeView(requireContext()).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ MaterialTheme {
+ val currentConfig = mapConfig ?: MapConfig(
+ initialLatLng = LatLng(0.0, 0.0),
+ initialZoom = 2f,
+ title = "Default Map",
+ markerType = MarkerType.CUSTOM_CONTENT_MARKER
+ )
+ MapContent(config = currentConfig)
+ }
+ }
+ }
+
+ @Composable
+ fun MapContent(config: MapConfig) {
+ val cameraPositionState = rememberCameraPositionState {
+ position = CameraPosition.fromLatLngZoom(config.initialLatLng, config.initialZoom)
+ }
+
+ GoogleMap(
+ cameraPositionState = cameraPositionState
+ ) {
+ when (config.markerType) {
+ MarkerType.CUSTOM_CONTENT_MARKER -> {
+ val markerState = rememberUpdatedMarkerState(position = config.initialLatLng)
+ markerState.position = config.initialLatLng
+
+ MarkerComposable(
+ state = markerState,
+ anchor = Offset(0.5f, 1.0f)
+ ){
+ Text(text = "Hello, World! (from ${config.title})")
+ }
+ }
+ MarkerType.STANDARD_MARKER_WITH_SNIPPET -> {
+ val markerState = rememberUpdatedMarkerState(position = config.initialLatLng)
+ markerState.position = config.initialLatLng
+
+ Marker(
+ state = markerState,
+ title = config.title,
+ snippet = config.standardMarkerSnippet ?: "Standard Marker Snippet" // Snippet for the info window
+ )
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val ARG_LAT = "arg_lat"
+ private const val ARG_LNG = "arg_lng"
+ private const val ARG_ZOOM = "arg_zoom"
+ private const val ARG_TITLE = "arg_title"
+ private const val ARG_MARKER_TYPE = "arg_marker_type"
+ private const val ARG_STANDARD_MARKER_SNIPPET = "arg_standard_marker_snippet"
+
+ @JvmStatic
+ fun newInstance(config: MapConfig): GoogleMapComposeFragment {
+ return GoogleMapComposeFragment().apply {
+ arguments = Bundle().apply {
+ putDouble(ARG_LAT, config.initialLatLng.latitude)
+ putDouble(ARG_LNG, config.initialLatLng.longitude)
+ putFloat(ARG_ZOOM, config.initialZoom)
+ putString(ARG_TITLE, config.title)
+ putString(ARG_MARKER_TYPE, config.markerType.name)
+ config.standardMarkerSnippet?.let { putString(ARG_STANDARD_MARKER_SNIPPET, it) }
+ }
+ }
+ }
+ }
+}
diff --git a/maps-app/src/main/java/com/google/maps/android/compose/MainActivity.kt b/maps-app/src/main/java/com/google/maps/android/compose/MainActivity.kt
index c7dbd8af..96c47b8f 100644
--- a/maps-app/src/main/java/com/google/maps/android/compose/MainActivity.kt
+++ b/maps-app/src/main/java/com/google/maps/android/compose/MainActivity.kt
@@ -199,6 +199,13 @@ class MainActivity : ComponentActivity() {
}) {
Text(getString(R.string.draggable_markers_collection_with_polygon))
}
+ Spacer(modifier = Modifier.padding(5.dp))
+ Button(
+ onClick = {
+ context.startActivity(Intent(context, FragmentDemoActivity::class.java))
+ }) {
+ Text(getString(R.string.fragment_demo_activity))
+ }
}
}
}
diff --git a/maps-app/src/main/java/com/google/maps/android/compose/MapFragmentPagerAdapter.kt b/maps-app/src/main/java/com/google/maps/android/compose/MapFragmentPagerAdapter.kt
new file mode 100644
index 00000000..cb2cd092
--- /dev/null
+++ b/maps-app/src/main/java/com/google/maps/android/compose/MapFragmentPagerAdapter.kt
@@ -0,0 +1,33 @@
+package com.google.maps.android.compose
+
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import com.google.android.gms.maps.model.LatLng
+
+class MapFragmentPagerAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
+
+ private val mapConfigs = listOf(
+ MapConfig( // First map - Los Angeles
+ initialLatLng = LatLng(34.0522, -118.2437),
+ initialZoom = 10f,
+ title = "Los Angeles",
+ // LA gets the custom content marker
+ markerType = MarkerType.CUSTOM_CONTENT_MARKER
+ ),
+ MapConfig( // Second map - New York City
+ initialLatLng = LatLng(40.7128, -74.0060),
+ initialZoom = 10f,
+ title = "New York City",
+ // NYC gets the standard marker
+ markerType = MarkerType.STANDARD_MARKER_WITH_SNIPPET,
+ standardMarkerSnippet = "The Big Apple!"
+ )
+ )
+
+ override fun getItemCount(): Int = mapConfigs.size
+
+ override fun createFragment(position: Int): Fragment {
+ return GoogleMapComposeFragment.newInstance(mapConfigs[position])
+ }
+}
diff --git a/maps-app/src/main/res/layout/activity_fragment_demo.xml b/maps-app/src/main/res/layout/activity_fragment_demo.xml
new file mode 100644
index 00000000..5620f3cd
--- /dev/null
+++ b/maps-app/src/main/res/layout/activity_fragment_demo.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
diff --git a/maps-app/src/main/res/values/strings.xml b/maps-app/src/main/res/values/strings.xml
index 3993bcba..ea0381f6 100644
--- a/maps-app/src/main/res/values/strings.xml
+++ b/maps-app/src/main/res/values/strings.xml
@@ -28,6 +28,7 @@
Syncing Draggable Marker With Model
Updating Non-Draggable Marker With Model
Polygon around draggable markers
+ Fragment Demo Activity
Recomposition Map
Street View
Custom Location Button