Skip to content

Commit 4b59320

Browse files
committed
feat: Add automatic AttributionId initialization at application startup
1 parent ede2526 commit 4b59320

File tree

4 files changed

+122
-1
lines changed

4 files changed

+122
-1
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ lint = "31.13.0"
2424
org-jacoco-core = "0.8.13"
2525
material = "1.13.0"
2626
gradleMavenPublishPlugin = "0.34.0"
27+
androidx-startup-runtime = "1.2.0"
2728

2829
[libraries]
2930
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
31+
androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup-runtime" }
3032
dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka-gradle-plugin" }
3133
gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
3234
jacoco-android = { module = "com.mxalbert.gradle:jacoco-android", version.ref = "jacoco-android" }

library/build.gradle.kts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ android {
6060
unitTests.isReturnDefaultValues = true
6161
}
6262
namespace = "com.google.maps.android"
63+
sourceSets["main"].java.srcDir("build/generated/source/artifactId")
6364
}
6465

6566
dependencies {
@@ -75,6 +76,7 @@ dependencies {
7576
testImplementation(libs.kotlin.test)
7677
testImplementation(libs.truth)
7778
implementation(libs.kotlin.stdlib.jdk8)
79+
implementation(libs.androidx.startup.runtime)
7880

7981
testImplementation(libs.mockk)
8082
testImplementation(libs.kotlinx.coroutines.test)
@@ -89,3 +91,54 @@ tasks.register("instrumentTest") {
8991
if (System.getenv("JITPACK") != null) {
9092
apply(plugin = "maven")
9193
}
94+
95+
// START: Attribution ID Generation Logic
96+
97+
// 1. Define the attribution string format.
98+
// It uses the 'version' property inherited from the root build.gradle.kts.
99+
// Customize the prefix to match your library's identifier.
100+
val attributionId = "gmp_git_androidmapsutils_v$version"
101+
102+
// 2. Register the custom Gradle task.
103+
val generateArtifactIdFile = tasks.register("generateArtifactIdFile") {
104+
description = "Generates an AttributionId object from the project version."
105+
group = "build"
106+
107+
// 3. Define the output directory and file.
108+
val outputDir = layout.buildDirectory.dir("generated/source/artifactId")
109+
val packageName = "com.google.maps.android.utils.meta" // <-- Customize your package name
110+
val packagePath = packageName.replace('.', '/')
111+
val outputFile = outputDir.get().file("$packagePath/ArtifactId.kt").asFile
112+
113+
// 4. Declare the output file so Gradle understands task dependencies.
114+
outputs.file(outputFile)
115+
116+
// 5. Define the action that writes the file.
117+
doLast {
118+
outputFile.parentFile.mkdirs()
119+
outputFile.writeText(
120+
// This is the content of the generated Kotlin file.
121+
// It uses Kotlin's trimIndent() for clean formatting.
122+
"""
123+
package $packageName
124+
125+
/**
126+
* Automatically generated object containing the library's attribution ID.
127+
* This is used to track library usage for analytics.
128+
*/
129+
public object AttributionId {
130+
public const val VALUE: String = "$attributionId"
131+
}
132+
""".trimIndent()
133+
)
134+
println("Generated attribution ID file at: ${outputFile.absolutePath}")
135+
}
136+
}
137+
138+
// 6. Hook the custom task into the build lifecycle.
139+
// This ensures your file is generated before the Kotlin compiler needs it.
140+
tasks.named("preBuild") {
141+
dependsOn(generateArtifactIdFile)
142+
}
143+
144+
// END: Attribution ID Generation Logic

library/src/main/AndroidManifest.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,23 @@
1515
limitations under the License.
1616
-->
1717

18-
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
18+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
19+
xmlns:tools="http://schemas.android.com/tools">
1920
<!-- https://groups.google.com/group/adt-dev/browse_thread/thread/c059c0380998092b/6720093fb40fdaf9 -->
2021
<application>
2122
<meta-data
2223
android:name="com.google.android.gms.version"
2324
android:value="@integer/google_play_services_version" />
2425

26+
<provider
27+
android:name="androidx.startup.InitializationProvider"
28+
android:authorities="${applicationId}.androidx-startup"
29+
android:exported="false"
30+
tools:node="merge">
31+
32+
<meta-data
33+
android:name="com.google.maps.android.utils.attibution.AttributionIdInitializer"
34+
android:value="androidx.startup" />
35+
</provider>
2536
</application>
2637
</manifest>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.google.maps.android.utils.attibution
19+
20+
import android.content.Context
21+
import android.util.Log
22+
import androidx.startup.Initializer
23+
import com.google.android.gms.maps.MapsApiSettings
24+
import com.google.maps.android.utils.meta.AttributionId
25+
import kotlinx.coroutines.CoroutineScope
26+
import kotlinx.coroutines.Dispatchers
27+
import kotlinx.coroutines.launch
28+
29+
/**
30+
* Initializes the AttributionId at application startup.
31+
*/
32+
internal class AttributionIdInitializer : Initializer<Unit> {
33+
override fun create(context: Context) {
34+
Log.d(TAG, "Initializing AttributionId: ${AttributionId.VALUE}")
35+
36+
// 🚨 CRITICAL: Launch the potentially blocking call on the IO thread
37+
CoroutineScope(Dispatchers.IO).launch {
38+
Log.d(TAG, "Running MapsApiSettings.addInternalUsageAttributionId on background thread.")
39+
40+
// This is now safely off the main thread
41+
MapsApiSettings.addInternalUsageAttributionId(
42+
/* context = */ context,
43+
/* internalUsageAttributionId = */ AttributionId.VALUE
44+
)
45+
}
46+
}
47+
48+
override fun dependencies(): List<Class<out Initializer<*>>> {
49+
return emptyList()
50+
}
51+
52+
private companion object {
53+
private val TAG = AttributionIdInitializer::class.java.simpleName
54+
}
55+
}

0 commit comments

Comments
 (0)