From 1582916cb9dfd3c4041d0c0f06c8d9fa2c47ac51 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:00:10 -0700 Subject: [PATCH 01/14] feat!: update Maps SDK to v20.0.0 and add internal usage attribution Updates the Maps SDK dependencies and implements internal usage attribution using androidx.startup. Changes: - Replaced manual initialization with AttributionIdInitializer using androidx.startup. - Removed GoogleMapsInitializer and MapsComposeApplication (Breaking Change). - Updated README.md with attribution opt-out instructions. - Added installAndLaunch Gradle task for easier demo app testing. - Fixed ScaleBarTests density calculation. - Updated minSdk overrides for test dependencies. Breaking Changes: - GoogleMapsInitializer and MapsComposeApplication have been removed. Initialization is now handled automatically by androidx.startup. - minSdk is now 23. --- README.md | 19 +- build.gradle.kts | 7 + gradle/libs.versions.toml | 22 +- maps-app/src/androidTest/AndroidManifest.xml | 6 + .../android/compose/GoogleMapViewTests.kt | 8 - .../maps/android/compose/ScaleBarTests.kt | 8 +- .../internal/GoogleMapsInitializerTest.kt | 235 ------------------ maps-app/src/main/AndroidManifest.xml | 1 - .../android/compose/MapsComposeApplication.kt | 40 --- .../internal/GoogleMapsInitializerTest.kt | 79 ------ maps-compose/build.gradle.kts | 3 +- maps-compose/src/main/AndroidManifest.xml | 17 +- .../google/maps/android/compose/GoogleMap.kt | 37 +-- .../compose/internal/GoogleMapsInitializer.kt | 195 --------------- .../attribution/AttributionIdInitializer.kt | 40 +++ 15 files changed, 116 insertions(+), 601 deletions(-) create mode 100644 maps-app/src/androidTest/AndroidManifest.xml delete mode 100644 maps-app/src/androidTest/java/com/google/maps/android/compose/internal/GoogleMapsInitializerTest.kt delete mode 100644 maps-app/src/main/java/com/google/maps/android/compose/MapsComposeApplication.kt delete mode 100644 maps-app/src/test/java/com/google/maps/android/compose/internal/GoogleMapsInitializerTest.kt delete mode 100644 maps-compose/src/main/java/com/google/maps/android/compose/internal/GoogleMapsInitializer.kt create mode 100644 maps-compose/src/main/java/com/google/maps/android/compose/utils/attribution/AttributionIdInitializer.kt diff --git a/README.md b/README.md index c42ee1e6..ad75ab4d 100644 --- a/README.md +++ b/README.md @@ -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 + + + +``` ## Contributing diff --git a/build.gradle.kts b/build.gradle.kts index e9c36c88..7e21ee67 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,4 +35,11 @@ allprojects { // {x-release-please-start-version} version = "7.0.0" // {x-release-please-end} +} + +tasks.register("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") } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0b3f816b..2ae6ee2c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,30 +1,31 @@ [versions] -activitycompose = "1.12.1" -agp = "8.13.1" +activitycompose = "1.12.2" +agp = "8.13.2" androidCore = "1.7.0" androidx-core = "1.17.0" androidxtest = "1.7.0" -compose-bom = "2025.12.00" +androidx-startup = "1.2.0" +compose-bom = "2026.01.00" dokka = "2.1.0" espresso = "3.7.0" -gradleMavenPublishPlugin = "0.35.0" +gradleMavenPublishPlugin = "0.36.0" jacoco-plugin = "0.2.1" junit = "4.13.2" junitktx = "1.3.0" -kotlin = "2.2.21" +kotlin = "2.3.0" kotlinxCoroutines = "1.10.2" leakcanaryAndroid = "2.14" mapsecrets = "2.0.1" -mapsktx = "5.2.1" +mapsktx = "5.2.2" material3 = "1.4.0" materialIconsExtendedAndroid = "1.7.8" -mockk = "1.14.6" -mockkAndroid = "1.14.6" +mockk = "1.14.7" +mockkAndroid = "1.14.7" org-jacoco-core = "0.8.14" -screenshot = "0.0.1-alpha12" +screenshot = "0.0.1-alpha13" constraintlayout = "2.2.1" material = "1.13.0" -robolectric = "4.16" +robolectric = "4.16.1" truth = "1.4.5" [libraries] @@ -40,6 +41,7 @@ androidx-compose-ui-preview-tooling = { module = "androidx.compose.ui:ui-tooling androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" } androidx-test-compose-ui = { module = "androidx.compose.ui:ui-test-junit4" } +androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup" } 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" } diff --git a/maps-app/src/androidTest/AndroidManifest.xml b/maps-app/src/androidTest/AndroidManifest.xml new file mode 100644 index 00000000..6fae52d7 --- /dev/null +++ b/maps-app/src/androidTest/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt b/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt index 318d270a..abdd2a46 100644 --- a/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt +++ b/maps-app/src/androidTest/java/com/google/maps/android/compose/GoogleMapViewTests.kt @@ -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 @@ -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( diff --git a/maps-app/src/androidTest/java/com/google/maps/android/compose/ScaleBarTests.kt b/maps-app/src/androidTest/java/com/google/maps/android/compose/ScaleBarTests.kt index efe2d22e..fe55c3a5 100644 --- a/maps-app/src/androidTest/java/com/google/maps/android/compose/ScaleBarTests.kt +++ b/maps-app/src/androidTest/java/com/google/maps/android/compose/ScaleBarTests.kt @@ -16,8 +16,10 @@ package com.google.maps.android.compose import android.graphics.Point import androidx.compose.foundation.layout.Box +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.LatLng @@ -44,6 +46,7 @@ class ScaleBarTests { val composeTestRule = createComposeRule() private lateinit var cameraPositionState: CameraPositionState + private lateinit var density: Density private fun initScaleBar(initialZoom: Float, initialPosition: LatLng) { check(hasValidApiKey) { "Maps API key not specified" } @@ -55,6 +58,7 @@ class ScaleBarTests { ) composeTestRule.setContent { + density = LocalDensity.current Box { GoogleMap( cameraPositionState = cameraPositionState, @@ -87,7 +91,9 @@ class ScaleBarTests { val projection = cameraPositionState.projection projection?.let { proj -> val widthInDp = 65.dp - val widthInPixels = widthInDp.value.toInt() + val widthInPixels = with(density) { + widthInDp.toPx().toInt() + } val upperLeftLatLng = proj.fromScreenLocation(Point(0, 0)) val upperRightLatLng = proj.fromScreenLocation(Point(0, widthInPixels)) diff --git a/maps-app/src/androidTest/java/com/google/maps/android/compose/internal/GoogleMapsInitializerTest.kt b/maps-app/src/androidTest/java/com/google/maps/android/compose/internal/GoogleMapsInitializerTest.kt deleted file mode 100644 index 5408badf..00000000 --- a/maps-app/src/androidTest/java/com/google/maps/android/compose/internal/GoogleMapsInitializerTest.kt +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package com.google.maps.android.compose.internal - -import android.content.Context -import android.os.StrictMode -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.time.Duration.Companion.milliseconds -import com.google.android.gms.common.ConnectionResult -import com.google.android.gms.common.GooglePlayServicesMissingManifestValueException -import com.google.android.gms.maps.MapsInitializer -import com.google.android.gms.maps.MapsApiSettings -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.mockkStatic -import io.mockk.Runs -import io.mockk.just - -@OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidJUnit4::class) -class GoogleMapsInitializerTest { - - private val googleMapsInitializer = DefaultGoogleMapsInitializer() - - @After - fun tearDown() = runTest { - googleMapsInitializer.reset() - } - - @Test - fun testInitializationSuccess() = runTest { - // In an instrumentation test environment, Google Play services are available. - // Therefore, we expect the initialization to succeed. - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - - // Note: we need to establish the Strict Mode settings here as there are violations outside - // of our control if we try to set them in setUp - val threadPolicy = StrictMode.getThreadPolicy() - val vmPolicy = StrictMode.getVmPolicy() - - StrictMode.setThreadPolicy( - StrictMode.ThreadPolicy.Builder() - .detectDiskReads() - .detectAll() - .penaltyLog() - .penaltyDeath() - .build() - ) - StrictMode.setVmPolicy( - StrictMode.VmPolicy.Builder() - .detectAll() - .detectLeakedClosableObjects() - .penaltyLog() - .penaltyDeath() - .build() - ) - - googleMapsInitializer.initialize(context) - - StrictMode.setThreadPolicy(threadPolicy) - StrictMode.setVmPolicy(vmPolicy) - - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.SUCCESS) - } - - @Test - fun testInitializationCancellationLeavesStateUninitialized() = runTest { - // In an instrumentation test environment, Google Play services are available. - // Therefore, we expect the initialization to succeed. - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - - // Note: we need to establish the Strict Mode settings here as there are violations outside - // of our control if we try to set them in setUp - val threadPolicy = StrictMode.getThreadPolicy() - val vmPolicy = StrictMode.getVmPolicy() - - StrictMode.setThreadPolicy( - StrictMode.ThreadPolicy.Builder() - .detectDiskReads() - .detectAll() - .penaltyLog() - .penaltyDeath() - .build() - ) - StrictMode.setVmPolicy( - StrictMode.VmPolicy.Builder() - .detectAll() - .detectLeakedClosableObjects() - .penaltyLog() - .penaltyDeath() - .build() - ) - - val job = launch { - googleMapsInitializer.reset() - googleMapsInitializer.initialize(context) - } - - // Allow the initialization coroutine to start before we cancel it. - delay(1.milliseconds) - job.cancel() - job.join() - - StrictMode.setThreadPolicy(threadPolicy) - StrictMode.setVmPolicy(vmPolicy) - - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.UNINITIALIZED) - } - - @Test - fun testInitializeSuccessState() = runTest { - // Arrange - mockkStatic(MapsInitializer::class) - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.UNINITIALIZED) - - coEvery { MapsInitializer.initialize(any()) } returns ConnectionResult.SUCCESS - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - // Act - // Direct call pattern matching original successful test structure - googleMapsInitializer.initialize(context) - - // Assert - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.SUCCESS) - coVerify(exactly = 1) { MapsInitializer.initialize( - eq(context), - any(), - any(), - )} - } - - @Test - fun testInitializeConcurrentCallsOnlyRunOnce() = runTest { - mockkStatic(MapsInitializer::class) - coEvery { MapsInitializer.initialize(any()) } returns ConnectionResult.SUCCESS - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - val job1 = launch { googleMapsInitializer.initialize(context) } - val job2 = launch { googleMapsInitializer.initialize(context) } - - job1.join() - job2.join() - - // Assert: The actual initialization method should only have been called once - coVerify(exactly = 1) { MapsInitializer.initialize( - eq(context), - any(), - any(), - )} - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.SUCCESS) - } - - @Test - fun testInitializeUnrecoverableFailureSetsFailureState() = runTest { - // Arrange - mockkStatic(MapsInitializer::class) - val error = GooglePlayServicesMissingManifestValueException() - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - var caughtException: Throwable? = null - - coEvery { - MapsInitializer.initialize( - eq(context), - isNull(), - any() - ) - } throws error - - // Act - val job = launch { - try { - googleMapsInitializer.initialize(context) - } catch (e: GooglePlayServicesMissingManifestValueException) { - caughtException = e - } - } - job.join() - - // Assert: The exception was caught, and the state became FAILURE - assertThat(caughtException).isInstanceOf(GooglePlayServicesMissingManifestValueException::class.java) - assertThat(caughtException).isEqualTo(error) - - // 2. Assert the state was set to FAILURE - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.FAILURE) - } - - @Test - fun testInitializeSuccessAlsoSetsAttributionId() = runTest { - // Arrange: Mock MapsApiSettings locally - mockkStatic(MapsInitializer::class, MapsApiSettings::class) - - coEvery { MapsInitializer.initialize(any()) } returns ConnectionResult.SUCCESS - coEvery { MapsApiSettings.addInternalUsageAttributionId(any(), any()) } just Runs - - val context: Context = InstrumentationRegistry.getInstrumentation().targetContext - - // Act - // Direct call pattern matching original successful test structure - googleMapsInitializer.initialize(context) - - // Assert: Verify both the primary initialization and the attribution call occurred - coVerify(exactly = 1) { - MapsInitializer.initialize( - eq(context), - any(), - any(), - ) - } - coVerify(exactly = 1) { MapsApiSettings.addInternalUsageAttributionId(any(), any()) } - assertThat(googleMapsInitializer.state.value).isEqualTo(InitializationState.SUCCESS) - } -} \ No newline at end of file diff --git a/maps-app/src/main/AndroidManifest.xml b/maps-app/src/main/AndroidManifest.xml index e7af6d8e..e6904543 100644 --- a/maps-app/src/main/AndroidManifest.xml +++ b/maps-app/src/main/AndroidManifest.xml @@ -19,7 +19,6 @@ { - googleMapsInitializer.initialize(mockContext) - } - assertEquals(InitializationState.UNINITIALIZED, googleMapsInitializer.state.value) - } -} diff --git a/maps-compose/build.gradle.kts b/maps-compose/build.gradle.kts index b3872e13..8d601503 100644 --- a/maps-compose/build.gradle.kts +++ b/maps-compose/build.gradle.kts @@ -60,6 +60,7 @@ dependencies { implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.core) implementation(libs.androidx.compose.foundation) + implementation(libs.androidx.startup.runtime) implementation(libs.kotlin) implementation(libs.kotlinx.coroutines.android) api(libs.maps.ktx.std) @@ -76,7 +77,7 @@ val attributionId = "gmp_git_androidmapscompose_v$version" val generateArtifactIdFile = tasks.register("generateArtifactIdFile") { val outputDir = layout.buildDirectory.dir("generated/source/artifactId") - val packageName = "com.google.maps.android.compose.meta" + val packageName = "com.google.maps.android.compose.utils.meta" val packagePath = packageName.replace('.', '/') val outputFile = outputDir.get().file("$packagePath/ArtifactId.kt").asFile diff --git a/maps-compose/src/main/AndroidManifest.xml b/maps-compose/src/main/AndroidManifest.xml index e67f88c1..d503585c 100644 --- a/maps-compose/src/main/AndroidManifest.xml +++ b/maps-compose/src/main/AndroidManifest.xml @@ -15,4 +15,19 @@ limitations under the License. --> - + + + + + + + + + diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt index 5c2be9d3..d0266903 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt @@ -50,8 +50,7 @@ import com.google.android.gms.maps.MapView import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.MapColorScheme import com.google.android.gms.maps.model.PointOfInterest -import com.google.maps.android.compose.internal.InitializationState -import com.google.maps.android.compose.internal.LocalGoogleMapsInitializer + import com.google.maps.android.ktx.awaitMap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart @@ -113,31 +112,18 @@ public fun GoogleMap( return } - val googleMapsInitializer = LocalGoogleMapsInitializer.current - val initializationState by googleMapsInitializer.state - - if (initializationState != InitializationState.SUCCESS) { - val context = LocalContext.current - LaunchedEffect(Unit) { - // Coroutine to initialize Google Maps SDK. - // This will run once when the composable is first displayed. - googleMapsInitializer.initialize(context) - } + // rememberUpdatedState and friends are used here to make these values observable to + // the subcomposition without providing a new content function each recomposition + val mapClickListeners = remember { MapClickListeners() }.also { + it.indoorStateChangeListener = indoorStateChangeListener + it.onMapClick = onMapClick + it.onMapLongClick = onMapLongClick + it.onMapLoaded = onMapLoaded + it.onMyLocationButtonClick = onMyLocationButtonClick + it.onMyLocationClick = onMyLocationClick + it.onPOIClick = onPOIClick } - if (initializationState == InitializationState.SUCCESS) { - // rememberUpdatedState and friends are used here to make these values observable to - // the subcomposition without providing a new content function each recomposition - val mapClickListeners = remember { MapClickListeners() }.also { - it.indoorStateChangeListener = indoorStateChangeListener - it.onMapClick = onMapClick - it.onMapLongClick = onMapLongClick - it.onMapLoaded = onMapLoaded - it.onMyLocationButtonClick = onMyLocationButtonClick - it.onMyLocationClick = onMyLocationClick - it.onPOIClick = onPOIClick - } - val mapUpdaterState = remember { MapUpdaterState( mergeDescendants, @@ -228,7 +214,6 @@ public fun GoogleMap( ) } }) - } } /** diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/internal/GoogleMapsInitializer.kt b/maps-compose/src/main/java/com/google/maps/android/compose/internal/GoogleMapsInitializer.kt deleted file mode 100644 index 6107f68c..00000000 --- a/maps-compose/src/main/java/com/google/maps/android/compose/internal/GoogleMapsInitializer.kt +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.maps.android.compose.internal - -import android.content.Context -import android.os.StrictMode -import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.State -import androidx.compose.runtime.compositionLocalOf -import androidx.compose.runtime.mutableStateOf -import com.google.android.gms.common.ConnectionResult -import com.google.android.gms.maps.MapsInitializer -import com.google.android.gms.maps.MapsApiSettings -import com.google.maps.android.compose.meta.AttributionId -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext - -/** - * Enum representing the initialization state of the Google Maps SDK. - */ -public enum class InitializationState { - /** - * The SDK has not been initialized. - */ - UNINITIALIZED, - - /** - * The SDK is currently being initialized. - */ - INITIALIZING, - - /** - * The SDK has been successfully initialized. - */ - SUCCESS, - - /** - * The SDK initialization failed. - */ - FAILURE -} - -/** - * A singleton object to manage the initialization of the Google Maps SDK. - * - * This object provides a state machine to track the initialization process and ensures that - * the initialization is performed only once. It also provides a mechanism to reset the - * initialization state, which can be useful in test environments. - * - * The initialization process consists of two main steps: - * 1. Calling `MapsInitializer.initialize(context)` to initialize the Google Maps SDK. - * 2. Calling `MapsApiSettings.addInternalUsageAttributionId(context, attributionId)` to add - * the library's attribution ID to the Maps API settings. - * - * The state of the initialization is exposed via the `state` property, which is a [State] object - * that can be observed for changes. - */ -public interface GoogleMapsInitializer { - public val state: State - - /** - * The value of the attribution ID. Set this to the empty string to opt out of attribution. - * - * This must be set before calling the `initialize` function. - */ - public var attributionId: String - - /** - * Initializes Google Maps. This function must be called before using any other - * functions in this library. - * - * If initialization fails with a recoverable error (e.g., a network issue), - * the state will be reset to [InitializationState.UNINITIALIZED], allowing for a - * subsequent retry. In the case of an unrecoverable error (e.g., a missing - * manifest value), the state will be set to [InitializationState.FAILURE] and the - * original exception will be re-thrown. - * - * @param context The context to use for initialization. - * @param forceInitialization When true, initialization will be attempted even if it - * has already succeeded or is in progress. This is useful for retrying a - * previously failed initialization. - */ - public suspend fun initialize( - context: Context, - forceInitialization: Boolean = false, - ) - - /** - * Resets the initialization state. - * - * This function cancels any ongoing initialization and resets the state to `UNINITIALIZED`. - * This is primarily useful in test environments where the SDK might need to be - * re-initialized multiple times. - */ - public suspend fun reset() -} - -/** - * The default implementation of [GoogleMapsInitializer]. - * - * @param ioDispatcher The dispatcher to use for IO operations. - */ -public class DefaultGoogleMapsInitializer( - private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, - private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main, -) : GoogleMapsInitializer { - private val _state = mutableStateOf(InitializationState.UNINITIALIZED) - override val state: State = _state - - private val mutex = Mutex() - - override var attributionId: String = AttributionId.VALUE - - override suspend fun initialize( - context: Context, - forceInitialization: Boolean, - ) { - try { - if (!forceInitialization && - (_state.value == InitializationState.INITIALIZING || _state.value == InitializationState.SUCCESS) - ) { - return - } - - mutex.withLock { - if (_state.value != InitializationState.UNINITIALIZED) { - return - } - _state.value = InitializationState.INITIALIZING - } - - withContext(mainDispatcher) { - val scope = this - - val policy = StrictMode.getThreadPolicy() - try { - StrictMode.allowThreadDiskReads() - val result = MapsInitializer.initialize(context, null) { - scope.launch(ioDispatcher) { - MapsApiSettings.addInternalUsageAttributionId(context, attributionId) - _state.value = InitializationState.SUCCESS - } - } - - if (result != ConnectionResult.SUCCESS) { - _state.value = InitializationState.FAILURE - } - } finally { - StrictMode.setThreadPolicy(policy) - } - } - } catch (e: com.google.android.gms.common.GooglePlayServicesMissingManifestValueException) { - // This is an unrecoverable error. Play Services is not available (could be a test?) - // Set the state to FAILURE to prevent further attempts. - _state.value = InitializationState.FAILURE - throw e - } catch (e: Exception) { - // This could be a transient error. - // Reset to UNINITIALIZED to allow for a retry. - _state.value = InitializationState.UNINITIALIZED - throw e - } - } - - override suspend fun reset() { - mutex.withLock { - _state.value = InitializationState.UNINITIALIZED - } - } -} - -/** - * CompositionLocal that provides a [GoogleMapsInitializer]. - */ -public val LocalGoogleMapsInitializer: ProvidableCompositionLocal = - compositionLocalOf { - // Default implementation of the initializer - DefaultGoogleMapsInitializer() - } diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/utils/attribution/AttributionIdInitializer.kt b/maps-compose/src/main/java/com/google/maps/android/compose/utils/attribution/AttributionIdInitializer.kt new file mode 100644 index 00000000..d4638371 --- /dev/null +++ b/maps-compose/src/main/java/com/google/maps/android/compose/utils/attribution/AttributionIdInitializer.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.maps.android.compose.utils.attribution + +import android.content.Context +import androidx.startup.Initializer +import com.google.android.gms.maps.MapsApiSettings +import com.google.maps.android.compose.utils.meta.AttributionId + +/** + * Adds a usage attribution ID to the initializer, which helps Google understand which libraries + * and samples are helpful to developers, such as usage of this library. + * To opt out of sending the usage attribution ID, please remove this initializer from your manifest. + */ +internal class AttributionIdInitializer : Initializer { + override fun create(context: Context) { + MapsApiSettings.addInternalUsageAttributionId( + /* context = */ context, + /* internalUsageAttributionId = */ AttributionId.VALUE + ) + } + + override fun dependencies(): List>> { + return emptyList() + } +} From 30408a4f865aa498ee82be609f62fea29ab71251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20L=C3=B3pez-Ma=C3=B1as?= Date: Mon, 26 Jan 2026 17:37:57 +0100 Subject: [PATCH 02/14] fix: downgraded the screenshot library --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ae6ee2c..fe7f0cd3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ materialIconsExtendedAndroid = "1.7.8" mockk = "1.14.7" mockkAndroid = "1.14.7" org-jacoco-core = "0.8.14" -screenshot = "0.0.1-alpha13" +screenshot = "0.0.1-alpha12" constraintlayout = "2.2.1" material = "1.13.0" robolectric = "4.16.1" @@ -73,4 +73,4 @@ android-application = { id = "com.android.application", version.ref = "agp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -screenshot = { id = "com.android.compose.screenshot", version.ref = "screenshot"} \ No newline at end of file +screenshot = { id = "com.android.compose.screenshot", version.ref = "screenshot"} From 7ab1b12e90c7e7288ff6edad30cea140c46c7b05 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:28:28 -0700 Subject: [PATCH 03/14] build: pull the Android SDK versions into the version catalog --- .gitignore | 3 +- gradle/libs.versions.toml | 93 +++++++++++++++++---------- maps-app/build.gradle.kts | 8 +-- maps-compose-utils/build.gradle.kts | 4 +- maps-compose-widgets/build.gradle.kts | 4 +- maps-compose/build.gradle.kts | 4 +- 6 files changed, 70 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 1590fc53..7376cd81 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ secrets.properties # This covers new IDEs, like Antigravity .vscode/ -**/bin/ \ No newline at end of file +**/bin/BOB_DATA/ +build-logic/**/bin/ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fe7f0cd3..4b4a2f68 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,71 +1,94 @@ +# 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] +# Base +agp = "8.13.2" # Do not upgrade to 9.0.0 yet +dokka = "2.1.0" +gradleMavenPublishPlugin = "0.36.0" +jacoco-plugin = "0.2.1" +kotlin = "2.3.0" +kotlinxCoroutines = "1.10.2" + +# Android activitycompose = "1.12.2" -agp = "8.13.2" -androidCore = "1.7.0" +androidCompileSdk = "36" +androidMinSdk = "23" +androidTargetSdk = "36" androidx-core = "1.17.0" -androidxtest = "1.7.0" androidx-startup = "1.2.0" compose-bom = "2026.01.00" -dokka = "2.1.0" +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" espresso = "3.7.0" -gradleMavenPublishPlugin = "0.36.0" -jacoco-plugin = "0.2.1" junit = "4.13.2" junitktx = "1.3.0" -kotlin = "2.3.0" -kotlinxCoroutines = "1.10.2" leakcanaryAndroid = "2.14" -mapsecrets = "2.0.1" -mapsktx = "5.2.2" -material3 = "1.4.0" -materialIconsExtendedAndroid = "1.7.8" -mockk = "1.14.7" -mockkAndroid = "1.14.7" +mockk = "1.14.9" +mockkAndroid = "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.1" +screenshot = "0.0.1-alpha12" # Do not upgrade to alpha13 (broken) 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" } +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" } + +# 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-test-compose-ui = { module = "androidx.compose.ui:ui-test-junit4" } 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" } 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" } 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] diff --git a/maps-app/build.gradle.kts b/maps-app/build.gradle.kts index e437f95a..713346b6 100644 --- a/maps-app/build.gradle.kts +++ b/maps-app/build.gradle.kts @@ -25,11 +25,11 @@ android { } namespace = "com.google.maps.android.compose" - compileSdk = 36 + compileSdk = libs.versions.androidCompileSdk.get().toInt() 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" @@ -73,7 +73,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) diff --git a/maps-compose-utils/build.gradle.kts b/maps-compose-utils/build.gradle.kts index 9cbf3ae9..8b5ab8ca 100644 --- a/maps-compose-utils/build.gradle.kts +++ b/maps-compose-utils/build.gradle.kts @@ -13,10 +13,10 @@ android { } namespace = "com.google.maps.android.compose.utils" - compileSdk = 36 + compileSdk = libs.versions.androidCompileSdk.get().toInt() defaultConfig { - minSdk = 23 + minSdk = libs.versions.androidMinSdk.get().toInt() } compileOptions { diff --git a/maps-compose-widgets/build.gradle.kts b/maps-compose-widgets/build.gradle.kts index 876bb3bd..23eae787 100644 --- a/maps-compose-widgets/build.gradle.kts +++ b/maps-compose-widgets/build.gradle.kts @@ -20,10 +20,10 @@ android { } namespace = "com.google.maps.android.compose.widgets" - compileSdk = 36 + compileSdk = libs.versions.androidCompileSdk.get().toInt() defaultConfig { - minSdk = 23 + minSdk = libs.versions.androidMinSdk.get().toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/maps-compose/build.gradle.kts b/maps-compose/build.gradle.kts index 8d601503..db4ecf19 100644 --- a/maps-compose/build.gradle.kts +++ b/maps-compose/build.gradle.kts @@ -13,10 +13,10 @@ android { } namespace = "com.google.maps.android.compose" - compileSdk = 36 + compileSdk = libs.versions.androidCompileSdk.get().toInt() defaultConfig { - minSdk = 23 + minSdk = libs.versions.androidMinSdk.get().toInt() } compileOptions { From d343b98ae6d237b70bf038a765c4fe289eed6e50 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:36:44 -0700 Subject: [PATCH 04/14] build: remove unnecessary directory reference from .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7376cd81..a77c626a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,4 @@ secrets.properties # This covers new IDEs, like Antigravity .vscode/ -**/bin/BOB_DATA/ build-logic/**/bin/ From 1292de1d607b9cbc2b0d6dae1f1c1fe7e1fb5c72 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:22:04 -0700 Subject: [PATCH 05/14] build: upgrade AGP to 9.0.0 and Gradle to 9.2.1 Upgrades the Android Gradle Plugin to 9.0.0, the Gradle Wrapper to 9.2.1, and the Compose Screenshot plugin to 0.0.1-alpha13. This resolves configuration issues with the `GenerateTestConfig` task where `mergedManifest` was not properly configured. Also adds necessary AGP 9.0 compatibility flags to gradle.properties. --- gradle.properties | 10 ++++++++++ gradle/libs.versions.toml | 14 ++++++++------ gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2c6f44b5..2caeae0c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4b4a2f68..b604549f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,13 @@ # 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] # Base -agp = "8.13.2" # Do not upgrade to 9.0.0 yet +androidCompileSdk = "36" +androidMinSdk = "23" +androidTargetSdk = "36" + +agp = "9.0.0" dokka = "2.1.0" gradleMavenPublishPlugin = "0.36.0" jacoco-plugin = "0.2.1" @@ -11,9 +16,6 @@ kotlinxCoroutines = "1.10.2" # Android activitycompose = "1.12.2" -androidCompileSdk = "36" -androidMinSdk = "23" -androidTargetSdk = "36" androidx-core = "1.17.0" androidx-startup = "1.2.0" compose-bom = "2026.01.00" @@ -39,7 +41,7 @@ mockk = "1.14.9" mockkAndroid = "1.14.9" org-jacoco-core = "0.8.14" robolectric = "4.16.1" -screenshot = "0.0.1-alpha12" # Do not upgrade to alpha13 (broken) +screenshot = "0.0.1-alpha13" truth = "1.4.5" [libraries] @@ -96,4 +98,4 @@ android-application = { id = "com.android.application", version.ref = "agp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -screenshot = { id = "com.android.compose.screenshot", version.ref = "screenshot"} +screenshot = { id = "com.android.compose.screenshot", version.ref = "screenshot"} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2a84e188..23449a2b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 From cadfb329e5e96782510aa3b5d19ab29bc723f8f4 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:34:52 -0700 Subject: [PATCH 06/14] build: upgrade AGP to 9.0.0 and Gradle to 9.2.1 Upgrades the Android Gradle Plugin to 9.0.0, the Gradle Wrapper to 9.2.1, and the Compose Screenshot plugin to 0.0.1-alpha13. This resolves configuration issues with the 'GenerateTestConfig' task where 'mergedManifest' was not properly configured. Also: - Adopts AGP 9.0 compatibility flags in gradle.properties. - Removes obsolete 'testDebugScreenshotTest' and 'testReleaseScreenshotTest' exclusions from CI workflows. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 012a13e9..4c1c0a26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 From 1ebbb216702dc9dd26df5c09ad6a49517cb27400 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:49:40 -0700 Subject: [PATCH 07/14] build: remove broken jacoco-android plugin Removes com.mxalbert.gradle.jacoco-android plugin which is incompatible with AGP 9.0. Removed related configuration from convention plugin and test workflow. --- .github/workflows/test.yml | 2 +- .../main/kotlin/PublishingConventionPlugin.kt | 18 ------------------ gradle/libs.versions.toml | 2 -- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c1c0a26..f31d41cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: distribution: 'temurin' - name: Build modules - run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace + run: ./gradlew build -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace - name: Run Screenshot Tests run: ./gradlew :maps-app:validateDebugScreenshotTest diff --git a/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt b/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt index 0ea4f542..155850cd 100644 --- a/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt @@ -4,41 +4,23 @@ import com.vanniktech.maven.publish.MavenPublishBaseExtension import org.gradle.api.Plugin import org.gradle.api.Project 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 class PublishingConventionPlugin : Plugin { override fun apply(project: Project) { project.run { applyPlugins() - configureJacoco() configureVanniktechPublishing() } } private fun Project.applyPlugins() { apply(plugin = "com.android.library") - apply(plugin = "com.mxalbert.gradle.jacoco-android") apply(plugin = "org.jetbrains.dokka") apply(plugin = "com.vanniktech.maven.publish") } - private fun Project.configureJacoco() { - configure { - toolVersion = "0.8.7" - - } - - tasks.withType().configureEach { - extensions.configure(JacocoTaskExtension::class.java) { - isIncludeNoLocationClasses = true - excludes = listOf("jdk.internal.*") - } - } - } - private fun Project.configureVanniktechPublishing() { extensions.configure { configure( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b604549f..2bf98875 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,6 @@ androidTargetSdk = "36" agp = "9.0.0" dokka = "2.1.0" gradleMavenPublishPlugin = "0.36.0" -jacoco-plugin = "0.2.1" kotlin = "2.3.0" kotlinxCoroutines = "1.10.2" @@ -49,7 +48,6 @@ truth = "1.4.5" android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } 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" } From a4b95a1588a053fde2eccf2882a93e3ddd0f73fb Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:33:10 -0700 Subject: [PATCH 08/14] build: revert some deps to fix jacoco --- .github/workflows/test.yml | 2 +- .../main/kotlin/PublishingConventionPlugin.kt | 18 ++++++++++++++++++ gradle/libs.versions.toml | 8 +++++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f31d41cc..012a13e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: distribution: 'temurin' - name: Build modules - run: ./gradlew build -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace + run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:testDebugScreenshotTest -x :maps-app:generateReleaseScreenshotTestConfig -x :maps-app:testReleaseScreenshotTest --stacktrace - name: Run Screenshot Tests run: ./gradlew :maps-app:validateDebugScreenshotTest diff --git a/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt b/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt index 155850cd..0ea4f542 100644 --- a/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt @@ -4,23 +4,41 @@ import com.vanniktech.maven.publish.MavenPublishBaseExtension import org.gradle.api.Plugin import org.gradle.api.Project 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 class PublishingConventionPlugin : Plugin { override fun apply(project: Project) { project.run { applyPlugins() + configureJacoco() configureVanniktechPublishing() } } private fun Project.applyPlugins() { apply(plugin = "com.android.library") + apply(plugin = "com.mxalbert.gradle.jacoco-android") apply(plugin = "org.jetbrains.dokka") apply(plugin = "com.vanniktech.maven.publish") } + private fun Project.configureJacoco() { + configure { + toolVersion = "0.8.7" + + } + + tasks.withType().configureEach { + extensions.configure(JacocoTaskExtension::class.java) { + isIncludeNoLocationClasses = true + excludes = listOf("jdk.internal.*") + } + } + } + private fun Project.configureVanniktechPublishing() { extensions.configure { configure( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2bf98875..0a260c46 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,10 +7,10 @@ androidCompileSdk = "36" androidMinSdk = "23" androidTargetSdk = "36" -agp = "9.0.0" +agp = "8.13.1" dokka = "2.1.0" gradleMavenPublishPlugin = "0.36.0" -kotlin = "2.3.0" +kotlin = "2.2.21" kotlinxCoroutines = "1.10.2" # Android @@ -39,8 +39,9 @@ leakcanaryAndroid = "2.14" mockk = "1.14.9" mockkAndroid = "1.14.9" org-jacoco-core = "0.8.14" +jacoco-plugin = "0.2.1" robolectric = "4.16.1" -screenshot = "0.0.1-alpha13" +screenshot = "0.0.1-alpha12" truth = "1.4.5" [libraries] @@ -81,6 +82,7 @@ androidx-test-espresso = { module = "androidx.test.espresso:espresso-core", vers 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" } +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" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 23449a2b..2a84e188 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 02686b1f51d18535f4fd8a3aa8e57f2696573988 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:11:35 -0700 Subject: [PATCH 09/14] build: restore jacoco coverage and fix D8 error Upgrades AGP to 9.0.0 and Screenshot plugin to 0.0.1-alpha13 to resolve D8 Kotlin metadata errors with Kotlin 2.3.0. Restores Jacoco coverage reporting using the native Gradle Jacoco plugin (compatible with AGP 9.0), replacing the incompatible `jacoco-android` plugin. Switches coverage collection to the `debug` build type as `release` unit test tasks are not created by default for libraries. --- .github/workflows/test.yml | 2 +- .../main/kotlin/PublishingConventionPlugin.kt | 35 ++++++++++++++----- gradle/libs.versions.toml | 6 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- maps-compose-utils/build.gradle.kts | 7 ++++ maps-compose-widgets/build.gradle.kts | 7 ++++ maps-compose/build.gradle.kts | 7 ++++ 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 012a13e9..499f62fd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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:testDebugSc run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace - name: Run Screenshot Tests run: ./gradlew :maps-app:validateDebugScreenshotTest diff --git a/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt b/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt index 0ea4f542..8af5773a 100644 --- a/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/PublishingConventionPlugin.kt @@ -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 { override fun apply(project: Project) { @@ -20,22 +21,40 @@ class PublishingConventionPlugin : Plugin { 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 { - toolVersion = "0.8.7" - + toolVersion = "0.8.11" // Compatible with newer JDKs } - tasks.withType().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("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") + }) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a260c46..73904ddd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,10 +7,10 @@ androidCompileSdk = "36" androidMinSdk = "23" androidTargetSdk = "36" -agp = "8.13.1" +agp = "9.0.0" dokka = "2.1.0" gradleMavenPublishPlugin = "0.36.0" -kotlin = "2.2.21" +kotlin = "2.3.0" kotlinxCoroutines = "1.10.2" # Android @@ -41,7 +41,7 @@ mockkAndroid = "1.14.9" org-jacoco-core = "0.8.14" jacoco-plugin = "0.2.1" robolectric = "4.16.1" -screenshot = "0.0.1-alpha12" +screenshot = "0.0.1-alpha13" truth = "1.4.5" [libraries] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2a84e188..23449a2b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/maps-compose-utils/build.gradle.kts b/maps-compose-utils/build.gradle.kts index 8b5ab8ca..86ce8933 100644 --- a/maps-compose-utils/build.gradle.kts +++ b/maps-compose-utils/build.gradle.kts @@ -38,6 +38,13 @@ android { ) } } + + buildTypes { + getByName("debug") { + enableUnitTestCoverage = true + enableAndroidTestCoverage = true + } + } } dependencies { diff --git a/maps-compose-widgets/build.gradle.kts b/maps-compose-widgets/build.gradle.kts index 23eae787..f8e96ccc 100644 --- a/maps-compose-widgets/build.gradle.kts +++ b/maps-compose-widgets/build.gradle.kts @@ -46,6 +46,13 @@ android { ) } } + + buildTypes { + getByName("debug") { + enableUnitTestCoverage = true + enableAndroidTestCoverage = true + } + } } dependencies { diff --git a/maps-compose/build.gradle.kts b/maps-compose/build.gradle.kts index db4ecf19..eeff7c42 100644 --- a/maps-compose/build.gradle.kts +++ b/maps-compose/build.gradle.kts @@ -40,6 +40,13 @@ android { } sourceSets["main"].java.srcDir("build/generated/source/artifactId") + + buildTypes { + getByName("debug") { + enableUnitTestCoverage = true + enableAndroidTestCoverage = true + } + } } composeCompiler { From 865b9d2e4b13c439acfdc5b39688dd423bbd4b5a Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:21:30 -0700 Subject: [PATCH 10/14] build: fix malformed CI command in test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 499f62fd..4c1c0a26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: distribution: 'temurin' - name: Build modules - run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:testDebugSc run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace + run: ./gradlew build jacocoTestReport -x :maps-app:generateDebugScreenshotTestConfig -x :maps-app:generateReleaseScreenshotTestConfig --stacktrace - name: Run Screenshot Tests run: ./gradlew :maps-app:validateDebugScreenshotTest From 0c64a49ecc911db34db097c68611672b650958cc Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:25:33 -0700 Subject: [PATCH 11/14] build: fix deprecated Kotlin compiler argument Replaces deprecated -Xopt-in with -opt-in in build files to resolve Gradle warnings. --- maps-compose-utils/build.gradle.kts | 2 +- maps-compose-widgets/build.gradle.kts | 2 +- maps-compose/build.gradle.kts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/maps-compose-utils/build.gradle.kts b/maps-compose-utils/build.gradle.kts index 86ce8933..41f9ccb8 100644 --- a/maps-compose-utils/build.gradle.kts +++ b/maps-compose-utils/build.gradle.kts @@ -34,7 +34,7 @@ android { jvmTarget.set(JvmTarget.JVM_1_8) freeCompilerArgs.addAll( "-Xexplicit-api=strict", - "-Xopt-in=kotlin.RequiresOptIn" + "-opt-in=kotlin.RequiresOptIn" ) } } diff --git a/maps-compose-widgets/build.gradle.kts b/maps-compose-widgets/build.gradle.kts index f8e96ccc..8fa3cd9f 100644 --- a/maps-compose-widgets/build.gradle.kts +++ b/maps-compose-widgets/build.gradle.kts @@ -42,7 +42,7 @@ android { jvmTarget.set(JvmTarget.JVM_1_8) freeCompilerArgs.addAll( "-Xexplicit-api=strict", - "-Xopt-in=kotlin.RequiresOptIn" + "-opt-in=kotlin.RequiresOptIn" ) } } diff --git a/maps-compose/build.gradle.kts b/maps-compose/build.gradle.kts index eeff7c42..6afbede7 100644 --- a/maps-compose/build.gradle.kts +++ b/maps-compose/build.gradle.kts @@ -34,7 +34,7 @@ android { jvmTarget.set(JvmTarget.JVM_1_8) freeCompilerArgs.addAll( "-Xexplicit-api=strict", - "-Xopt-in=kotlin.RequiresOptIn" + "-opt-in=kotlin.RequiresOptIn" ) } } From f044d95286711fa71616326dab0cd01a8bd884e8 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:53:43 -0700 Subject: [PATCH 12/14] build: add org.apache.http.legacy to maps-app Required for instrumentation tests running on Android 9+ to avoid NoClassDefFoundError: org/apache/http/ProtocolVersion. --- maps-app/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maps-app/build.gradle.kts b/maps-app/build.gradle.kts index 713346b6..ab1e4f54 100644 --- a/maps-app/build.gradle.kts +++ b/maps-app/build.gradle.kts @@ -27,6 +27,8 @@ android { namespace = "com.google.maps.android.compose" compileSdk = libs.versions.androidCompileSdk.get().toInt() + useLibrary("org.apache.http.legacy") + defaultConfig { minSdk = libs.versions.androidMinSdk.get().toInt() targetSdk = libs.versions.androidTargetSdk.get().toInt() From c806e00626ef8f0952de554baa56e0be995e0774 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:04:21 -0700 Subject: [PATCH 13/14] fix: add org.apache.http.legacy to manifest Required for runtime access on Android 9+ devices during instrumentation tests. Missed by useLibrary in build.gradle which only handles compile classpath. --- maps-app/src/main/AndroidManifest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/maps-app/src/main/AndroidManifest.xml b/maps-app/src/main/AndroidManifest.xml index e6904543..5292fb8c 100644 --- a/maps-app/src/main/AndroidManifest.xml +++ b/maps-app/src/main/AndroidManifest.xml @@ -26,6 +26,10 @@ android:supportsRtl="true" android:theme="@style/Theme.AndroidMapsCompose"> + + From e7c3819dc6989279fbffff5b2e81d5155c1d39f8 Mon Sep 17 00:00:00 2001 From: dkhawk <107309+dkhawk@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:51:41 -0700 Subject: [PATCH 14/14] test(widgets): use mockk-android for instrumentation tests Fixes ClassNotFoundException: io.mockk.proxy.android.AndroidMockKAgentFactory during ScaleBarUnitTest execution on Android devices. --- gradle/libs.versions.toml | 4 ++-- maps-app/build.gradle.kts | 16 ++++++++-------- maps-app/src/screenshotTest/AndroidManifest.xml | 11 ++++++++++- maps-compose-widgets/build.gradle.kts | 2 +- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 73904ddd..66df61a8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,6 @@ junit = "4.13.2" junitktx = "1.3.0" leakcanaryAndroid = "2.14" mockk = "1.14.9" -mockkAndroid = "1.14.9" org-jacoco-core = "0.8.14" jacoco-plugin = "0.2.1" robolectric = "4.16.1" @@ -86,7 +85,8 @@ jacoco-android-plugin = { module = "com.mxalbert.gradle:jacoco-android", version kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanaryAndroid" } 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" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } screenshot-validation-api = { group = "com.android.tools.screenshot", name = "screenshot-validation-api", version.ref = "screenshot" } diff --git a/maps-app/build.gradle.kts b/maps-app/build.gradle.kts index ab1e4f54..0654b784 100644 --- a/maps-app/build.gradle.kts +++ b/maps-app/build.gradle.kts @@ -86,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) @@ -102,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 { diff --git a/maps-app/src/screenshotTest/AndroidManifest.xml b/maps-app/src/screenshotTest/AndroidManifest.xml index db93b49b..e366e537 100644 --- a/maps-app/src/screenshotTest/AndroidManifest.xml +++ b/maps-app/src/screenshotTest/AndroidManifest.xml @@ -1 +1,10 @@ - \ No newline at end of file + + + + + + diff --git a/maps-compose-widgets/build.gradle.kts b/maps-compose-widgets/build.gradle.kts index 8fa3cd9f..4330e2da 100644 --- a/maps-compose-widgets/build.gradle.kts +++ b/maps-compose-widgets/build.gradle.kts @@ -71,6 +71,6 @@ dependencies { androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.test.espresso) androidTestImplementation(libs.androidx.test.junit.ktx) - androidTestImplementation(libs.mockk) + androidTestImplementation(libs.mockk.android) androidTestImplementation(libs.truth) }