Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
distribution: 'temurin'

- name: Build modules
run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:testDebugScreenshotTest -x :maps-app:generateReleaseScreenshotTestConfig -x :maps-app:testReleaseScreenshotTest --stacktrace
run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace

- name: Run Screenshot Tests
run: ./gradlew :maps-app:validateDebugScreenshotTest
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ secrets.properties

# This covers new IDEs, like Antigravity
.vscode/
**/bin/
build-logic/**/bin/
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,10 +497,21 @@ The colors of the text, line, and shadow are also all configurable (e.g., based

## Internal usage attribution ID

This library calls the MapsApiSettings.addInternalUsageAttributionId method, which helps Google
understand which libraries and samples are helpful to developers and is optional. Instructions for
opting out of the identifier are provided in
[reference documentation](maps-compose/src/main/java/com/google/maps/android/compose/internal/GoogleMapsInitializer.kt#L77-L82).
This library calls the `addInternalUsageAttributionId` method, which helps Google understand which libraries and samples are helpful to developers and is optional. Instructions for opting out of the identifier are provided below.

If you wish to disable this, you can do so by removing the initializer in your `AndroidManifest.xml` using the `tools:node="remove"` attribute:

```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.google.maps.android.compose.utils.attribution.AttributionIdInitializer"
tools:node="remove" />
</provider>
```

## Contributing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.gradle.kotlin.dsl.*
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
import org.gradle.api.tasks.testing.Test
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
import org.gradle.testing.jacoco.tasks.JacocoReport

class PublishingConventionPlugin : Plugin<Project> {
override fun apply(project: Project) {
Expand All @@ -20,22 +21,40 @@ class PublishingConventionPlugin : Plugin<Project> {

private fun Project.applyPlugins() {
apply(plugin = "com.android.library")
apply(plugin = "com.mxalbert.gradle.jacoco-android")
apply(plugin = "org.jetbrains.dokka")
apply(plugin = "org.gradle.jacoco")
apply(plugin = "com.vanniktech.maven.publish")
}

private fun Project.configureJacoco() {
configure<JacocoPluginExtension> {
toolVersion = "0.8.7"

toolVersion = "0.8.11" // Compatible with newer JDKs
}

tasks.withType<Test>().configureEach {
extensions.configure(JacocoTaskExtension::class.java) {
isIncludeNoLocationClasses = true
excludes = listOf("jdk.internal.*")
}
// AGP 9.0+ built-in Jacoco support or manual configuration.
// We create a "jacocoTestReport" task to match the CI workflow.

tasks.register<JacocoReport>("jacocoTestReport") {
// Dependencies
dependsOn("testDebugUnitTest")

reports {
xml.required.set(true)
html.required.set(true)
}

// Source directories
val mainSrc = "${layout.projectDirectory}/src/main/java"
sourceDirectories.setFrom(files(mainSrc))

// Class directories - we need to point to where Kotlin compiles to
val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug")
classDirectories.setFrom(files(debugTree))

// Execution data from the unit test task
executionData.setFrom(fileTree(layout.buildDirectory.get()) {
include("outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec")
})
}
}

Expand Down
7 changes: 7 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ allprojects {
// {x-release-please-start-version}
version = "7.0.0"
// {x-release-please-end}
}

tasks.register<Exec>("installAndLaunch") {
description = "Installs and launches the demo app."
group = "install"
dependsOn(":maps-app:installDebug")
commandLine("adb", "shell", "am", "start", "-n", "com.google.maps.android.compose/.MainActivity")
}
10 changes: 10 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,13 @@ android.nonTransitiveRClass=false
android.nonFinalResIds=false

android.experimental.enableScreenshotTest=true
android.defaults.buildfeatures.resvalues=true
android.sdk.defaultTargetSdkToCompileSdkIfUnset=false
android.enableAppCompileTimeRClass=false
android.usesSdkInManifest.disallowed=false
android.uniquePackageNames=false
android.dependency.useConstraints=true
android.r8.strictFullModeForKeepRules=false
android.r8.optimizedResourceShrinking=false
android.builtInKotlin=false
android.newDsl=false
101 changes: 64 additions & 37 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,69 +1,96 @@
# This file is organized into functional groups: Base, Android, Material, Google Maps Platform (GMP), and Testing.
# Please maintain this structure when adding new dependencies or versions.

[versions]
activitycompose = "1.12.1"
agp = "8.13.1"
androidCore = "1.7.0"
# Base
androidCompileSdk = "36"
androidMinSdk = "23"
androidTargetSdk = "36"

agp = "9.0.0"
dokka = "2.1.0"
gradleMavenPublishPlugin = "0.36.0"
kotlin = "2.3.0"
kotlinxCoroutines = "1.10.2"

# Android
activitycompose = "1.12.2"
androidx-core = "1.17.0"
androidx-startup = "1.2.0"
compose-bom = "2026.01.00"
constraintlayout = "2.2.1"

# Material
material = "1.13.0"
material3 = "1.4.0"
materialIconsExtendedAndroid = "1.7.8"

# Google Maps Platform
mapsecrets = "2.0.1"
mapsktx = "6.0.0"

# Testing
androidCore = "1.7.0"
androidxtest = "1.7.0"
compose-bom = "2025.12.00"
dokka = "2.1.0"
espresso = "3.7.0"
gradleMavenPublishPlugin = "0.35.0"
jacoco-plugin = "0.2.1"
junit = "4.13.2"
junitktx = "1.3.0"
kotlin = "2.2.21"
kotlinxCoroutines = "1.10.2"
leakcanaryAndroid = "2.14"
mapsecrets = "2.0.1"
mapsktx = "5.2.1"
material3 = "1.4.0"
materialIconsExtendedAndroid = "1.7.8"
mockk = "1.14.6"
mockkAndroid = "1.14.6"
mockk = "1.14.9"
org-jacoco-core = "0.8.14"
screenshot = "0.0.1-alpha12"
constraintlayout = "2.2.1"
material = "1.13.0"
robolectric = "4.16"
jacoco-plugin = "0.2.1"
robolectric = "4.16.1"
screenshot = "0.0.1-alpha13"
truth = "1.4.5"

[libraries]
# Base
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
androidx-compose-activity = { module = "androidx.activity:activity-compose", version.ref = "activitycompose" }
dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
gradle-maven-publish-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "gradleMavenPublishPlugin" }
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }

# Android
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activitycompose" }
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
androidx-compose-material = { module = "androidx.compose.material:material" }
androidx-compose-material-icons-extended-android = { module = "androidx.compose.material:material-icons-extended-android", version.ref = "materialIconsExtendedAndroid" }
androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
androidx-compose-ui = { module = "androidx.compose.ui:ui" }
androidx-compose-ui-preview-tooling = { module = "androidx.compose.ui:ui-tooling-preview" }
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup" }

# Material
androidx-compose-material = { module = "androidx.compose.material:material" }
androidx-compose-material-icons-extended-android = { module = "androidx.compose.material:material-icons-extended-android", version.ref = "materialIconsExtendedAndroid" }
androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }

# Google Maps Platform
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-secrets-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "mapsecrets" }

# Testing
androidx-test-compose-ui = { module = "androidx.compose.ui:ui-test-junit4" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidCore" }
androidx-test-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" }
androidx-test-junit-ktx = { module = "androidx.test.ext:junit-ktx", version.ref = "junitktx" }
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidCore" }
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidxtest" }
dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
gradle-maven-publish-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "gradleMavenPublishPlugin" }
jacoco-android-plugin = { module = "com.mxalbert.gradle:jacoco-android", version.ref = "jacoco-plugin", version.require = "0.2.1" }
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
jacoco-android-plugin = { module = "com.mxalbert.gradle:jacoco-android", version.ref = "jacoco-plugin" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }
leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanaryAndroid" }
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-secrets-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "mapsecrets" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockkAndroid" }
#mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockkAndroid" }
mockk-android = { group = "io.mockk", name = "mockk-android", version.ref = "mockk" }
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" }
screenshot-validation-api = { group = "com.android.tools.screenshot", name = "screenshot-validation-api", version.ref = "screenshot" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
screenshot-validation-api = { group = "com.android.tools.screenshot", name = "screenshot-validation-api", version.ref = "screenshot" }
test-junit = { module = "junit:junit", version.ref = "junit" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }

[plugins]
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
26 changes: 14 additions & 12 deletions maps-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ android {
}

namespace = "com.google.maps.android.compose"
compileSdk = 36
compileSdk = libs.versions.androidCompileSdk.get().toInt()

useLibrary("org.apache.http.legacy")

defaultConfig {
minSdk = 23
targetSdk = 36
minSdk = libs.versions.androidMinSdk.get().toInt()
targetSdk = libs.versions.androidTargetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -73,7 +75,7 @@ android {

dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.activity)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.compose.material3)
Expand All @@ -84,7 +86,6 @@ dependencies {
implementation(libs.material)
implementation(libs.androidx.compose.material.icons.extended.android)

screenshotTestImplementation(libs.screenshot.validation.api)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.leakcanary.android)

Expand All @@ -100,23 +101,24 @@ dependencies {
androidTestImplementation(libs.truth)
androidTestImplementation(libs.mockk.android)

testImplementation(libs.test.junit)
testImplementation(libs.robolectric)
testImplementation(kotlin("test"))
testImplementation(libs.androidx.test.core)
testImplementation(libs.truth)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.mockk)
testImplementation(libs.robolectric)
testImplementation(libs.test.junit)
testImplementation(libs.truth)

screenshotTestImplementation(kotlin("test"))
screenshotTestImplementation(libs.androidx.compose.ui.tooling)
screenshotTestImplementation(libs.mockk.android)
screenshotTestImplementation(libs.screenshot.validation.api)

// Instead of the lines below, regular apps would load these libraries from Maven according to
// the README installation instructions
implementation(project(":maps-compose"))
implementation(project(":maps-compose-widgets"))
implementation(project(":maps-compose-utils"))

testImplementation(libs.mockk)
testImplementation(libs.mockk.android)
testImplementation(kotlin("test"))
}

secrets {
Expand Down
6 changes: 6 additions & 0 deletions maps-app/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-sdk tools:overrideLibrary="io.mockk.android,io.mockk.proxy.android" />
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.common.truth.Truth.assertThat
import com.google.maps.android.compose.LatLngSubject.Companion.assertThat
import com.google.maps.android.compose.internal.DefaultGoogleMapsInitializer
import com.google.maps.android.compose.internal.InitializationState
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
Expand All @@ -54,13 +52,7 @@ class GoogleMapViewTests {
val countDownLatch = CountDownLatch(1)

val appContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
val googleMapsInitializer = DefaultGoogleMapsInitializer()

runBlocking {
googleMapsInitializer.initialize(appContext)
}

assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.SUCCESS)

composeTestRule.setContent {
GoogleMapView(
Expand Down
Loading