diff --git a/ApiDemos/project/common-ui/build.gradle.kts b/ApiDemos/project/common-ui/build.gradle.kts
index dfcd8352b..39a171450 100644
--- a/ApiDemos/project/common-ui/build.gradle.kts
+++ b/ApiDemos/project/common-ui/build.gradle.kts
@@ -65,7 +65,7 @@ android {
}
dependencies {
-
+ implementation(project(":library"))
implementation(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.material)
diff --git a/FireMarkers/app/build.gradle.kts b/FireMarkers/app/build.gradle.kts
index eb56c8aff..79182eb9c 100644
--- a/FireMarkers/app/build.gradle.kts
+++ b/FireMarkers/app/build.gradle.kts
@@ -95,6 +95,7 @@ android {
}
dependencies {
+ implementation(project(":library"))
// ---------------------------------------------------------------------------------------------
// AndroidX & Jetpack
//
diff --git a/README.md b/README.md
index f8c3a5217..929a0df53 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,24 @@ To run instrumentation tests on a connected device or emulator, use:
Alternatively, use the `gradlew build` command to build the project directly or download an APK
under [releases](https://github.com/googlemaps/android-samples/releases).
+## Internal usage attribution ID
+
+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
Contributions are welcome and encouraged! If you'd like to contribute, send us a [pull request] and refer to our [code of conduct] and [contributing guide].
diff --git a/WearOS/Wearable/build.gradle.kts b/WearOS/Wearable/build.gradle.kts
index d7670782a..b62f505b4 100644
--- a/WearOS/Wearable/build.gradle.kts
+++ b/WearOS/Wearable/build.gradle.kts
@@ -57,6 +57,7 @@ dependencies {
implementation(libs.core.ktx)
implementation(platform(libs.kotlin.bom))
implementation(libs.kotlin.stdlib)
+ implementation(project(":library"))
// [END_EXCLUDE]
// Modern Android projects use version catalogs to manage dependencies. To include the necessary dependencies,
// first add the following to your libs.versions.toml file:
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c0f0ae9dd..dcd2deaf2 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -58,6 +58,7 @@ robolectric = "4.16.1"
truth = "1.4.5"
turbine = "1.2.1"
uiautomator = "2.3.0"
+mockk = "1.14.11"
# Firebase
firebaseBom = "34.8.0"
@@ -67,12 +68,16 @@ firebaseDatabase = "22.0.1"
easypermissions = "3.0.0"
volley = "1.2.1"
+#Tracker lib
+startup-runtime = "1.2.0"
+
[libraries]
# Kotlin
kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
kotlin-stdlib-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
+startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "startup-runtime" }
# AndroidX
activity = { module = "androidx.activity:activity", version.ref = "activity" }
@@ -130,6 +135,7 @@ turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" }
ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" }
uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" }
+mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
# Firebase
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
diff --git a/library/.gitignore b/library/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/library/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/library/build.gradle.kts b/library/build.gradle.kts
new file mode 100644
index 000000000..06626fd56
--- /dev/null
+++ b/library/build.gradle.kts
@@ -0,0 +1,117 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+
+/*
+ * 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.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "com.example.library"
+ compileSdk = libs.versions.compileSdk.get().toInt()
+
+ defaultConfig {
+ minSdk = libs.versions.minSdk.get().toInt()
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ lint {
+ abortOnError = false
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ lint {
+ disable += setOf("MissingInflatedId", "OnClick")
+ sarifOutput = layout.buildDirectory.file("reports/lint-results-debug.sarif").get().asFile
+ }
+ kotlin {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_17)
+ javaParameters.set(true)
+ }
+ }
+}
+
+dependencies {
+ implementation(libs.appcompat)
+ implementation(libs.core.ktx)
+ implementation(libs.material)
+ implementation(libs.startup.runtime)
+ implementation(libs.play.services.maps)
+ testImplementation(libs.junit)
+ testImplementation(libs.robolectric)
+ testImplementation(libs.mockk)
+ testImplementation(libs.espresso.core)
+}
+
+
+abstract class GenerateArtifactIdTask : DefaultTask() {
+ @get:OutputDirectory
+ abstract val outputDir: DirectoryProperty
+
+ @get:Input
+ abstract val version: Property
+
+ @TaskAction
+ fun generate() {
+ val dir = outputDir.get().asFile
+ val packageName = "com.example.library.utils.meta"
+ val packagePath = packageName.replace('.', '/')
+ val outputFile = File(dir, "$packagePath/ArtifactId.kt")
+ outputFile.parentFile.mkdirs()
+ val attributionId = "gmp_git_androidsamples_v${version.get()}"
+ outputFile.writeText(
+ """
+ package $packageName
+
+ /**
+ * Automatically generated object containing the library's attribution ID.
+ * This is used to track library usage for analytics.
+ */
+ public object AttributionId {
+ public const val VALUE: String = "$attributionId"
+ }
+ """.trimIndent()
+ )
+ }
+}
+
+val generateArtifactIdFile = tasks.register("generateArtifactIdFile") {
+ outputDir.set(layout.buildDirectory.dir("generated/source/artifactId"))
+ version.set(libs.versions.versionName.get())
+}
+
+androidComponents {
+ onVariants { variant ->
+ variant.sources.java?.addGeneratedSourceDirectory(
+ generateArtifactIdFile,
+ GenerateArtifactIdTask::outputDir
+ )
+ }
+}
diff --git a/library/consumer-rules.pro b/library/consumer-rules.pro
new file mode 100644
index 000000000..e69de29bb
diff --git a/library/proguard-rules.pro b/library/proguard-rules.pro
new file mode 100644
index 000000000..f1b424510
--- /dev/null
+++ b/library/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a0aa501d8
--- /dev/null
+++ b/library/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/java/com/example/library/utils/attribution/AttributionIdInitializer.kt b/library/src/main/java/com/example/library/utils/attribution/AttributionIdInitializer.kt
new file mode 100644
index 000000000..cc426e512
--- /dev/null
+++ b/library/src/main/java/com/example/library/utils/attribution/AttributionIdInitializer.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.example.library.utils.attribution
+
+import android.content.Context
+import androidx.annotation.Keep
+import androidx.startup.Initializer
+import com.example.library.utils.meta.AttributionId
+import com.google.android.gms.maps.MapsApiSettings
+
+/**
+ * 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.
+ */
+@Keep
+internal class AttributionIdInitializer : Initializer {
+ override fun create(context: Context) {
+ MapsApiSettings.addInternalUsageAttributionId(
+ // context =
+ context,
+ // internalUsageAttributionId =
+ AttributionId.VALUE,
+ )
+ }
+
+ override fun dependencies(): List>> = emptyList()
+}
diff --git a/library/src/test/java/com/example/library/utils/AttributionIdInitializerTest.kt b/library/src/test/java/com/example/library/utils/AttributionIdInitializerTest.kt
new file mode 100644
index 000000000..16cfad4a5
--- /dev/null
+++ b/library/src/test/java/com/example/library/utils/AttributionIdInitializerTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.example.library.utils
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import com.example.library.utils.attribution.AttributionIdInitializer
+import com.example.library.utils.meta.AttributionId
+import com.google.android.gms.maps.MapsApiSettings
+import io.mockk.every
+import io.mockk.just
+import io.mockk.mockkStatic
+import io.mockk.runs
+import io.mockk.unmockkStatic
+import io.mockk.verify
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class AttributionIdInitializerTest {
+ @Before
+ fun setUp() {
+ mockkStatic(MapsApiSettings::class)
+ every { MapsApiSettings.addInternalUsageAttributionId(any(), any()) } just runs
+ }
+
+ @After
+ fun tearDown() {
+ unmockkStatic(MapsApiSettings::class)
+ }
+
+ @Test
+ fun `create adds internal usage attribution id`() {
+ val context = ApplicationProvider.getApplicationContext()
+ val initializer = AttributionIdInitializer()
+
+ initializer.create(context)
+
+ verify {
+ MapsApiSettings.addInternalUsageAttributionId(
+ context,
+ AttributionId.VALUE,
+ )
+ }
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index af2968cd4..13b758669 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -70,3 +70,4 @@ project(":snippets:app-utils").projectDir = file("snippets/app-utils")
include(":tutorials:kotlin:Polygons")
project(":tutorials:kotlin:Polygons").projectDir = file("tutorials/kotlin/Polygons/app")
// Add others as needed, starting with these for now
+include(":library")
diff --git a/snippets/app-compose/build.gradle.kts b/snippets/app-compose/build.gradle.kts
index fb7d3134b..ef84a9f29 100644
--- a/snippets/app-compose/build.gradle.kts
+++ b/snippets/app-compose/build.gradle.kts
@@ -84,6 +84,7 @@ dependencies {
implementation(libs.navigation.ui.ktx)
implementation(libs.volley)
implementation(libs.lifecycle.runtime.ktx)
+ implementation(project(":library"))
// [END_EXCLUDE]
// Android Maps Compose composables for the Maps SDK for Android
diff --git a/snippets/app-ktx/build.gradle.kts b/snippets/app-ktx/build.gradle.kts
index 143020ada..10736eccf 100644
--- a/snippets/app-ktx/build.gradle.kts
+++ b/snippets/app-ktx/build.gradle.kts
@@ -78,6 +78,7 @@ dependencies {
implementation(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.lifecycle.runtime.ktx)
+ implementation(project(":library"))
// [END_EXCLUDE]
// KTX for the Maps SDK for Android library
diff --git a/snippets/app-places-ktx/build.gradle.kts b/snippets/app-places-ktx/build.gradle.kts
index 321fce956..02ff4e78b 100644
--- a/snippets/app-places-ktx/build.gradle.kts
+++ b/snippets/app-places-ktx/build.gradle.kts
@@ -79,6 +79,7 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
+ implementation(project(":library"))
// [END_EXCLUDE]
// KTX for the Places SDK for Android library
diff --git a/snippets/app-utils-ktx/build.gradle.kts b/snippets/app-utils-ktx/build.gradle.kts
index dc93bcaf8..aaa298cff 100644
--- a/snippets/app-utils-ktx/build.gradle.kts
+++ b/snippets/app-utils-ktx/build.gradle.kts
@@ -26,7 +26,7 @@ android {
defaultConfig {
applicationId = "com.example.app_utils_ktx"
- minSdk = 23
+ minSdk = libs.versions.minSdk.get().toInt()
targetSdk = 36
versionCode = 1
versionName = libs.versions.versionName.get()
@@ -78,6 +78,7 @@ dependencies {
implementation(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.lifecycle.runtime.ktx)
+ implementation(project(":library"))
// [END_EXCLUDE]
// KTX for the Maps SDK for Android Utility Library
diff --git a/snippets/app-utils/build.gradle.kts b/snippets/app-utils/build.gradle.kts
index 967da3892..8d2f0569b 100644
--- a/snippets/app-utils/build.gradle.kts
+++ b/snippets/app-utils/build.gradle.kts
@@ -70,7 +70,6 @@ android {
// [START maps_android_utils_install_snippet]
dependencies {
-
// [START_EXCLUDE silent]
implementation(libs.core.ktx)
implementation(libs.appcompat)
@@ -78,6 +77,7 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
+ implementation(project(":library"))
// [END_EXCLUDE]
// Utility Library for Maps SDK for Android
diff --git a/snippets/app/build.gradle.kts b/snippets/app/build.gradle.kts
index 940375905..5e46712c9 100644
--- a/snippets/app/build.gradle.kts
+++ b/snippets/app/build.gradle.kts
@@ -88,6 +88,7 @@ dependencies {
implementation(libs.volley)
implementation(libs.lifecycle.runtime.ktx)
implementation(libs.places)
+ implementation(project(":library"))
// [END_EXCLUDE]
// Maps SDK for Android
diff --git a/tutorials/kotlin/Polygons/app/build.gradle.kts b/tutorials/kotlin/Polygons/app/build.gradle.kts
index 0078e4151..bcbf3e73d 100644
--- a/tutorials/kotlin/Polygons/app/build.gradle.kts
+++ b/tutorials/kotlin/Polygons/app/build.gradle.kts
@@ -64,6 +64,7 @@ dependencies {
implementation(libs.play.services.maps)
implementation(libs.core.ktx)
implementation(libs.kotlin.stdlib)
+ implementation(project(":library"))
// Tests
testImplementation(libs.junit)