skill: add Android skills

This commit is contained in:
2026-07-14 10:34:54 +08:00
parent e260c02629
commit ab82fb7cf0
182 changed files with 62992 additions and 0 deletions

View File

@@ -0,0 +1,416 @@
Android Gradle plugin 9.0 introduces built-in Kotlin support and enables it
by default. That means you no longer have to apply the
`org.jetbrains.kotlin.android` (or `kotlin-android`) plugin in your build files
to compile Kotlin source files.
With built-in Kotlin, your build files are simpler and you can avoid
compatibility issues between AGP and the `kotlin-android` plugin.
> [!NOTE]
> **Note:** Built-in Kotlin replaces the `kotlin-android` plugin only. If you are writing a Kotlin Multiplatform (KMP) library module, you still need to apply the `org.jetbrains.kotlin.multiplatform` plugin and the [`com.android.kotlin.multiplatform.library`](https://developer.android.com/kotlin/multiplatform/plugin) plugin. Also, using the `org.jetbrains.kotlin.multiplatform` plugin together with the `com.android.library` or `com.android.application` plugin is no longer allowed when built-in Kotlin is enabled.
## Enable built-in Kotlin
You need AGP 9.0 or higher to have built-in Kotlin support.
AGP 9.0 already enables built-in Kotlin for all your modules where you apply
AGP, so you don't need to do anything to enable it. However, if you previously
[opted out of built-in Kotlin](https://developer.android.com/build/migrate-to-built-in-kotlin#opt-out-of-built-in-kotlin) by setting `android.builtInKotlin=false`
in the `gradle.properties` file, you need to remove that setting or set it to
`true`.
> [!NOTE]
> **Note:** You can also enable built-in Kotlin for [one module at a time](https://developer.android.com/build/migrate-to-built-in-kotlin#module-by-module-migration).
Built-in Kotlin requires some changes to your project, so after you
have built-in Kotlin enabled, follow the next steps to migrate your project.
## Migration steps
After you upgrade your project from an older AGP version to AGP 9.0 or after
you manually [enable built-in Kotlin](https://developer.android.com/build/migrate-to-built-in-kotlin#enable-built-in-kotlin), you might see the following error
message:
Failed to apply plugin 'org.jetbrains.kotlin.android'.
> Cannot add extension with name 'kotlin', as there is an extension already registered with that name.
...or
Failed to apply plugin 'com.jetbrains.kotlin.android'
> The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
This error occurs because built-in Kotlin requires some changes to your project.
To resolve this error, follow these steps:
> [!NOTE]
> **Note:** If you're not yet ready to migrate your project, you can also [opt out of built-in Kotlin](https://developer.android.com/build/migrate-to-built-in-kotlin#opt-out-of-built-in-kotlin).
1. [Remove the `kotlin-android` plugin](https://developer.android.com/build/migrate-to-built-in-kotlin#migration-steps-remove-kotlin-android-plugin)
2. [Migrate the `kotlin-kapt` plugin if necessary](https://developer.android.com/build/migrate-to-built-in-kotlin#migration-steps-migrate-kotlin-kapt-plugin)
3. [Migrate the `android.kotlinOptions{}` DSL if necessary](https://developer.android.com/build/migrate-to-built-in-kotlin#migration-steps-migrate-kotlin-options)
4. [Migrate the `kotlin.sourceSets{}` DSL if necessary](https://developer.android.com/build/migrate-to-built-in-kotlin#migration-steps-migrate-kotlin-source-sets)
### 1. Remove the `kotlin-android` plugin
Remove the `org.jetbrains.kotlin.android` (or `kotlin-android`) plugin from
the module-level build files where you apply it.
The exact code to remove depends on
whether you use [version catalogs](https://docs.gradle.org/current/userguide/version_catalogs.html) to declare plugins.
### With version catalogs
### Kotlin
```kotlin
// Module-level build file
plugins {
alias(libs.plugins.kotlin.android)
}
```
### Groovy
```groovy
// Module-level build file
plugins {
alias(libs.plugins.kotlin.android)
}
```
### No version catalogs
### Kotlin
```kotlin
// Module-level build file
plugins {
id("org.jetbrains.kotlin.android")
}
```
### Groovy
```groovy
// Module-level build file
plugins {
id 'org.jetbrains.kotlin.android'
}
```
Then, remove the plugin from your top-level build file:
### With version catalogs
### Kotlin
```kotlin
// Top-level build file
plugins {
alias(libs.plugins.kotlin.android) apply false
}
```
### Groovy
```groovy
// Top-level build file
plugins {
alias(libs.plugins.kotlin.android) apply false
}
```
### No version catalogs
### Kotlin
```kotlin
// Top-level build file
plugins {
id("org.jetbrains.kotlin.android") version "KOTLIN_VERSION" apply false
}
```
### Groovy
```groovy
// Top-level build file
plugins {
id 'org.jetbrains.kotlin.android' version 'KOTLIN_VERSION' apply false
}
```
If you use version catalogs, also remove the plugin definition from the
version catalog TOML file (usually `gradle/libs.versions.toml`):
```toml
[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "KOTLIN_VERSION" }
```
### 2. Migrate the `kotlin-kapt` plugin if necessary
The `org.jetbrains.kotlin.kapt` (or `kotlin-kapt`) plugin is incompatible with
built-in Kotlin. If you use `kapt`, we recommend that you
[migrate your project to KSP](https://developer.android.com/build/migrate-to-ksp).
If you can't migrate to KSP yet, replace the `kotlin-kapt` plugin with the
`com.android.legacy-kapt` plugin, using the same version as your Android Gradle
plugin.
For example, with version catalogs, update your version catalog TOML
file as follows:
```toml
[plugins]
android-application = { id = "com.android.application", version.ref = "AGP_VERSION" }
# Add the following plugin definition
legacy-kapt = { id = "com.android.legacy-kapt", version.ref = "AGP_VERSION" }
# Remove the following plugin definition
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "KOTLIN_VERSION" }
```
Then, update your build files:
### Kotlin
```kotlin
// Top-level build file
plugins {
alias(libs.plugins.legacy.kapt) apply false
alias(libs.plugins.kotlin.kapt) apply false
}
```
### Groovy
```groovy
// Top-level build file
plugins {
alias(libs.plugins.legacy.kapt) apply false
alias(libs.plugins.kotlin.kapt) apply false
}
```
### Kotlin
```kotlin
// Module-level build file
plugins {
alias(libs.plugins.legacy.kapt)
alias(libs.plugins.kotlin.kapt)
}
```
### Groovy
```groovy
// Module-level build file
plugins {
alias(libs.plugins.legacy.kapt)
alias(libs.plugins.kotlin.kapt)
}
```
> [!NOTE]
> **Note:** If you declare the `kotlin-kapt` plugin in the `plugins{}` block as `kotlin("kapt") version "<KOTLIN_VERSION>"`, then remove that line instead.
### 3. Migrate the `android.kotlinOptions{}` DSL if necessary
If you use the `android.kotlinOptions{}` DSL, you need to
migrate it to the [`kotlin.compilerOptions{}`](https://kotlinlang.org/docs/gradle-compiler-options.html#migrate-from-kotlinoptions-to-compileroptions) DSL.
For example, update this code:
### Kotlin
```kotlin
android {
kotlinOptions {
languageVersion = "2.0"
jvmTarget = "11"
}
}
```
### Groovy
```groovy
android {
kotlinOptions {
languageVersion = "2.0"
jvmTarget = "11"
}
}
```
...to the new DSL:
### Kotlin
```kotlin
kotlin {
compilerOptions {
languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0
// Optional: Set jvmTarget
// jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}
```
### Groovy
```groovy
kotlin {
compilerOptions {
languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0
// Optional: Set jvmTarget
// jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}
```
> [!NOTE]
> **Note:** With built-in Kotlin, you don't need to set `kotlin.compilerOptions.jvmTarget` because its value defaults to `android.compileOptions.targetCompatibility`.
### 4. Migrate the `kotlin.sourceSets{}` DSL if necessary
When you use the `kotlin-android` plugin, AGP lets you add additional Kotlin
source directories using either the [`android.sourceSets{}`](https://developer.android.com/reference/tools/gradle-api/9.0/com/android/build/api/dsl/AndroidSourceSet) DSL or the
[`kotlin.sourceSets{}`](https://kotlinlang.org/api/kotlin-gradle-plugin/kotlin-gradle-plugin-api/org.jetbrains.kotlin.gradle.plugin/-kotlin-source-set/) DSL.
With the `android.sourceSets{}` DSL, you can add the directories to either the
`AndroidSourceSet.kotlin` set or the `AndroidSourceSet.java` set.
With built-in Kotlin, the only supported option is to add the directories to the
`AndroidSourceSet.kotlin` set using the `android.sourceSets{}` DSL.
If you use unsupported options, migrate them as follows:
### Kotlin
```kotlin
# Adding Kotlin source directories to kotlin.sourceSets is not supported
kotlin.sourceSets.named("main") {
kotlin.srcDir("additionalSourceDirectory/kotlin")
}
# Adding Kotlin source directories to AndroidSourceSet.java is also not supported
android.sourceSets.named("main") {
java.directories += "additionalSourceDirectory/kotlin"
}
# Add Kotlin source directories to AndroidSourceSet.kotlin
android.sourceSets.named("main") {
kotlin.directories += "additionalSourceDirectory/kotlin"
}
```
### Groovy
```groovy
# Adding Kotlin source directories to kotlin.sourceSets is not supported
kotlin.sourceSets.named("main") {
kotlin.srcDir("additionalSourceDirectory/kotlin")
}
# Adding Kotlin source directories to AndroidSourceSet.java is also not supported
android.sourceSets.named("main") {
java.directories.add("additionalSourceDirectory/kotlin")
}
# Add Kotlin source directories to AndroidSourceSet.kotlin
android.sourceSets.named("main") {
kotlin.directories.add("additionalSourceDirectory/kotlin")
}
```
If you want to add a Kotlin source directory to a specific variant or if the
directory is generated by a task, you can use the
[`addStaticSourceDirectory`](https://developer.android.com/reference/tools/gradle-api/9.0/com/android/build/api/variant/SourceDirectories#addStaticSourceDirectory(kotlin.String)) or [`addGeneratedSourceDirectory`](https://developer.android.com/reference/tools/gradle-api/9.0/com/android/build/api/variant/SourceDirectories#addGeneratedSourceDirectory(org.gradle.api.tasks.TaskProvider,kotlin.Function1)) methods
in the [variant API](https://developer.android.com/build/extend-agp#variant-api-artifacts-tasks):
### Kotlin
```kotlin
androidComponents.onVariants { variant ->
variant.sources.kotlin!!.addStaticSourceDirectory("additionalSourceDirectory/kotlin")
variant.sources.kotlin!!.addGeneratedSourceDirectory(TASK_PROVIDER, TASK_OUTPUT)
}
```
### Groovy
```groovy
androidComponents.onVariants { variant ->
variant.sources.kotlin!!.addStaticSourceDirectory("additionalSourceDirectory/kotlin")
variant.sources.kotlin!!.addGeneratedSourceDirectory(TASK_PROVIDER, TASK_OUTPUT)
}
```
## Report issues
If you encounter issues after completing the previous steps,
review the known issues in [issue #438678642](https://issuetracker.google.com/438678642) and give us
feedback if needed.
## Opt out of built-in Kotlin
If you are unable to migrate your project to use built-in Kotlin, set
`android.builtInKotlin=false` in the `gradle.properties` file to temporarily
disable it.
When you do that, the build shows a warning reminding you to migrate to built-in
Kotlin as you won't be able to disable built-in Kotlin in AGP 10.0.
> [!NOTE]
> **Note:** You also need to set `android.newDsl=false` to opt out of the [new DSL](https://developer.android.com/r/tools/new-dsl) because the `kotlin-android` plugin is not compatible with it.
Once you're ready to migrate your project, [enable built-in Kotlin](https://developer.android.com/build/migrate-to-built-in-kotlin#enable-built-in-kotlin)
and follow the [migration steps](https://developer.android.com/build/migrate-to-built-in-kotlin#migration-steps).
## Module-by-module migration
The `android.builtInKotlin` Gradle property lets you enable or disable built-in
Kotlin for all your modules where you apply AGP.
If migrating all your modules at once is challenging, you can migrate one module
at a time:
1. Set `android.builtInKotlin=false` in the `gradle.properties` file to
disable built-in Kotlin for all modules.
2. Apply the `com.android.built-in-kotlin` plugin to the module
you want to enable built-in Kotlin, using the same version as your
Android Gradle plugin.
3. Follow the previous [migration steps](https://developer.android.com/build/migrate-to-built-in-kotlin#migration-steps) to migrate this module to
built-in Kotlin.
4. Once you've migrated all your modules, remove the
`android.builtInKotlin=false` setting in `gradle.properties`
and the `com.android.built-in-kotlin` plugin in your build files.
## Option to selectively disable built-in Kotlin
Android Gradle plugin 9.0 enables built-in Kotlin for all modules where it is
applied.
We recommend disabling built-in Kotlin selectively for modules that don't have
Kotlin sources in large projects.
This removes both the Kotlin compilation task, which has a small build
performance cost, and the automatic dependency on the Kotlin standard library.
To disable built-in Kotlin for a module,
set `enableKotlin = false` in that module's build file:
### Kotlin
```kotlin
android {
enableKotlin = false
}
```
### Groovy
```groovy
android {
enableKotlin = false
}
```

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
When an Android module contains custom BuildConfig fields, the following steps
are necessary to ensure a correct build.
### Step 1: Enable the buildConfig build feature
In a build script:
android {
buildFeatures {
buildConfig = true
}
}
In custom build-logic for an app module:
extensions.configure<com.android.build.api.dsl.ApplicationExtension> {
buildFeatures {
buildConfig = true
}
}
In custom build-logic for a library module:
extensions.configure<com.android.build.api.dsl.LibraryExtension> {
buildFeatures {
buildConfig = true
}
}
In custom build-logic using `CommonExtension`:
extensions.configure<com.android.build.api.dsl.CommonExtension> {
buildFeatures {
buildConfig = true
}
}
### Step 2: Migrate to the new API
Use the **addCustomBuildConfigFields** recipe from the [gradle-recipes](https://developer.android.com/agents/skills/build/agp/agp-9-upgrade/references/recipes)
repository.
**IMPORTANT:** For `BuildConfigField`s with a type of `String`, the `value` field
*must* include quotation marks as part of the String. For example:
BuildConfigField(
type = "String",
value = "\"Some value\"",
comment = "Optional comment",
)
It is an **error** if the `value` field doesn't include quotation marks as
part of the String. For example, `value = "Some value"` **is an error** . This is
because the `value` is written out literally.

View File

@@ -0,0 +1,39 @@
When migrating to built-in Kotlin, it is important to consider usage of `kapt`
and the `org.jetbrains.kotlin.kapt` (also known as the `kotlin("kapt")`) plugin.
The goal is to migrate as many `kapt` usages to `ksp` as possible.
Follow these steps when migrating `kapt`:
## 1. Remove all references to the `org.jetbrains.kotlin.kapt` plugin
The `org.jetbrains.kotlin.kapt` (also known as `kotlin("kapt")`) plugin is
incompatible with built-in Kotlin. Remove it when migrating to built-in Kotlin.
## 2. Check each usage of `kapt`
Check each usage of `kapt` to see if it is compatible with `ksp`. To check if a
dependency is compatible with `ksp`, inspect the dependency's jar. For it to be
compatible with `ksp`, the jar must have a file,
`services/com.google.devtools.ksp.processing.SymbolProcessorProvider`. If it
does not, it is **incompatible** with `ksp`.
For example, the `androidx.room:room-compiler` library is compatible with KSP
since version 2.3.0-beta02. We can verify this by finding the jar file in the
Gradle caches directory, which is typically located at
`~/.gradle/caches/modules-2/files-2.1/` on Linux and Mac. In this specific case,
the `androidx.room:room-compiler` dependency is located at
`~/.gradle/caches/modules-2/files-2.1/androidx.room/room-compiler/`.
More generally, you can find a dependency by looking in
`~/.gradle/caches/modules-2/files-2.1/group-name/artifact-name/`.
## 3. Migrate to KSP where possible
For each usage of `kapt` that is compatible with `ksp`, use `ksp`. The prior
step explains how to check compatibility.
## 4. Apply legacy-kapt
If a Gradle module has `kapt` dependencies that cannot be migrated to `ksp`
because they are incompatible (see step 2), then leave that dependency alone and
apply the `com.android.legacy-kapt` plugin.

View File

@@ -0,0 +1,30 @@
If Paparazzi is used in the project, update it to version 2.0.0-alpha04 or
higher.
Paparazzi version 2.0.0-alpha04 and lower is not fully compatible with Gradle
9, and Gradle 9 is required by AGP 9. This means that, without workarounds,
projects that use Paparazzi v2.0.0-alpha04 and lower cannot migrate to AGP 9.
At time of writing, there are no higher versions of Paparazzi. That is,
v2.0.0-alpha04 is the latest release.
The issue is due to Paparazzi using internal classes from Gradle that tend to
move in breaking ways without warning. This specific issue is related to HTML
test reports. To work around it, disable those HTML test reports. Here
are two examples of how to do this, one for Kotlin DSL and the other for Groovy
DSL. Any module that has the paparazzi plugin (`app.cash.paparazzi`) applied
must apply one of these two workarounds.
Kotlin DSL:
tasks.withType<Test>().configureEach {
// https://github.com/cashapp/paparazzi/issues/2111
reports.html.required = false
}
Groovy DSL:
tasks.withType(Test).configureEach {
// https://github.com/cashapp/paparazzi/issues/2111
reports.html.required = false
}

View File

@@ -0,0 +1,62 @@
When migrating to AGP's new DSL, any Gradle code (plugins or logic in build
scripts) that relied on the old DSL will stop working. Such code must be
migrated.
## Guidelines
- **DO NOT** search the web for examples of how to do this. Use the **gradle-recipes** repository examples **only**.
- **DO NOT** use AGP internals in migrated code.
- **DO** use only public APIs in migrated code.
In some cases, there is a one-to-one replacement for the old code. Some examples
are in [the AGP 9.0.0 release notes](https://developer.android.com/build/releases/agp-9-0-0-release-notes).
In other cases, there is no direct one-to-one replacement. For these situations,
the [gradle-recipes repo](https://github.com/android/gradle-recipes) is a great resource. You can checkout one of its
AGP 9.x branches, such as `agp-9.0`, `agp-9.1`, or `agp-9.2`. These branches
contain recipes for common situations in Android projects. The following table
lists the compatibility for recipes for each version of AGP.
## Compatibility table
| AGP version | gradle-recipes branch |
|---|---|
| 9.0.x | agp-9.0 |
| 9.1.x | agp-9.1 |
| 9.2.x | agp-9.2 |
## Recipes and use-cases
The following table links use-cases to recipes.
| Recipe | Use-case |
|---|---|
| addCustomBuildConfigFields | Add custom BuildConfig fields |
| listenToArtifacts | Rename APK |
Additional details for each use-case follow.
### Add custom BuildConfig fields
See the detailed guide at [BuildConfig](https://developer.android.com/agents/skills/build/agp/agp-9-upgrade/references/buildconfig).
### Renaming an APK
In the old DSL, an APK could be renamed very simply. Here's an example:
android {
applicationVariants.all {
outputs.all {
val output = this as com.android.build.gradle.api.ApkVariantOutput
val fileName = output.outputFileName
if (fileName.contains("release")) {
output.outputFileName = "my-cool-new-name.apk"
}
}
}
}
However, with AGP 9 and the new DSL, `applicationVariants` is no longer
available. You must instead react to artifact creation using the
`androidComponents.onVariants` API. A complete example of this is available in
the **gradle-recipes** repository in the `listenToArtifacts` recipe.