Skip to content

build: drop the AGP 9 built-in Kotlin opt-out - #4120

Merged
AlvaroBrey merged 4 commits into
11.0-devfrom
build/agp9-drop-compat-flags
Aug 28, 2026
Merged

build: drop the AGP 9 built-in Kotlin opt-out#4120
AlvaroBrey merged 4 commits into
11.0-devfrom
build/agp9-drop-compat-flags

Conversation

@AlvaroBrey

@AlvaroBrey AlvaroBrey commented Aug 27, 2026

Copy link
Copy Markdown
Member
  • Removes the android.builtInKotlin=false and android.newDsl=false opt-out that build: update to AGP 9 (AGP 9.2.1, Gradle 9.4.1, Kotlin 2.2.21) #3964 added, from the root build and from both standalone sample builds, and takes kotlin-android out of 11 build scripts and the 3 convention plugins.
  • ui/debugview/api.txt loses InternalDebugRevenueCatScreenViewModelFactory. Metalava now analyses the release variant, and that class lives in src/debug/kotlin. The release AAR contains zero occurrences of it, so it never shipped and the entry was wrong
  • Fixes :dokkaGenerate, which is already broken on 11.0-dev. Built-in Kotlin registers Dokka source sets per Android variant instead of per Kotlin source set, and registers them lazily, so both :purchases and :ui:revenuecatui now select the documented variant through configureEach.
  • Bumps androidx.navigation 2.5.3 to 2.9.8. safeargs 2.5.3 detects AGP through the old DSL and hard fails, so the navigation bump is a prerequisite for the new DSL rather than an independent upgrade.

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids
Agent description

Motivation

AGP 9 enables built-in Kotlin and rejects the org.jetbrains.kotlin.android plugin outright:

The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.

Opting out needs two flags together, android.builtInKotlin=false and android.newDsl=false, because the kotlin-android plugin casts the android extension to BaseExtension, which the new DSL drops. #3964 took that opt-out so the version bump stayed reviewable. AGP 10 removes the opt-out, so the migration has to happen regardless; doing it separately keeps the two diffs legible.

Description

Four things blocked the migration, each hidden behind the previous.

The old DSL extension types are gone. com.android.build.gradle.BaseExtension and com.android.build.gradle.LibraryExtension do not exist once the new DSL is active. The convention plugins now configure com.android.build.api.dsl.ApplicationExtension and com.android.build.api.dsl.LibraryExtension, and set compileSdk directly rather than calling compileSdkVersion(). KotlinAndroidProjectExtension stays as is: AGP's built-in Kotlin still registers it.

safeargs 2.5.3 cannot see AGP 9. It fails with safeargs plugin must be used with android plugin, because its detection goes through the old DSL. The 2.5.3 jar references com/android/build/gradle/BaseExtension, AppExtension and gradle/api/BaseVariant; the 2.9.8 jar references only com.android.build.api.variant.*. examples/purchase-tester is the only module applying the plugin, and it gets its own commit.

Dokka source sets changed shape. With the kotlin-android plugin, Dokka saw the Kotlin source sets and the config addressed main, defaults and customEntitlementComputation. Built-in Kotlin instead registers one source set per Android variant, and registers them after the dokka { } block runs, so named("defaultsRelease") throws even though the name is valid later. Both modules now select through matching { }.configureEach. :purchases keeps reportUndocumented, skipDeprecated, the Android external documentation link, both source links and the paywalls.components suppression, all moved onto the selected variant. The explicit customEntitlementComputation suppression is load-bearing, not redundant: Dokka defaults suppress to !isPublishable and customEntitlementComputationRelease is published, so it would otherwise be documented.

kotlin-test stops carrying JUnit. :feature:galaxy names kotlin-test-junit instead. Verified rather than inherited: with plain kotlin-test, org.junit.Test is unresolved across all 25 test methods.

Not visible in the diff: ./gradlew :dokkaGenerate fails on the base commit with Dokka cannot generate documentation for Android projects with multiple enabled variants that have common source roots. The docs-deploy job that runs it is gated on release tags, so #3964 never exercised it. This PR is what makes it pass again, which is a bigger deal than the diff makes it look.

Worth noting separately: AGP 9.2.1 declares kotlin-gradle-plugin:2.2.10, so built-in Kotlin takes its compiler from buildscript classpath resolution rather than from our catalog. Confirmed the published contract from #3964 is intact rather than assumed:

check result
resolved KGP 2.2.21, not AGP's bundled 2.2.10
:purchases POM kotlin-stdlib:2.2.21 at compile scope
release AAR metadata mv=[1,8,0], so languageVersion 1.8 survives

Regression gates

  • ./gradlew :dokkaGenerate for the Dokka rework. Generates 1367 pages with com.revenuecat.purchases.paywalls.components still suppressed.
  • :feature:galaxy:testDefaultsDebugUnitTest for the kotlin-test-junit swap. Needs --rerun-tasks: a cached run passes even with the dependency reverted, which is how this could slip through.
  • :examples:purchase-tester:assembleDebug for the navigation bump.

Rejected:

  • Wiring the Dokka variant selector to ANDROID_VARIANT_TO_PUBLISH instead of hardcoding "defaultsRelease" twice. fastlane/Fastfile:295 rewrites that property per publish, so a customEntitlementComputation publish would point Dokka at exactly the variant :purchases suppresses.
  • Hoisting the shared Dokka config into ConfigureDokka.kt. Dokka's types are not on build-logic's compile classpath, so it needs the reflection pattern from ConfigureMetalava.kt, and sharing reportUndocumented.set(true) would newly turn it on for the four public-library modules that have no dokka { } block.
  • Moving the five hand-rolled app modules onto revenuecat-android-application, which would collapse 5 of the 6 per-module edits. Each keeps 2 to 3 overrides, and both testpurchases* modules deliberately pin old compileSdk/targetSdk to test compatibility. Worth a separate PR, not this one.

Limitations:

  • examples/MagicWeather and test-apps/sdksizetesting still apply kotlin-android. They are separate builds pinned to AGP 8.13.2, so they keep the plugin correctly and are untouched.
  • purchases/build.gradle.kts:120 has a dead androidComponents { onVariants } block guarding a "free" product flavor that does not exist in the repo. Pre-existing and unrelated, left alone.

Note

Medium Risk
Wide Gradle/AGP convention changes affect every Android module’s compile and doc generation; navigation and Dokka behavior are prerequisites for CI release docs, with limited runtime SDK impact.

Overview
Completes the AGP 9 migration by removing the android.builtInKotlin=false / android.newDsl=false opt-outs and dropping org.jetbrains.kotlin.android across convention plugins, app modules, samples, and the version catalog.

Build-logic now configures ApplicationExtension / LibraryExtension from the new DSL (e.g. compileSdk instead of BaseExtension). :purchases and :ui:revenuecatui Dokka blocks use matching { }.configureEach so documentation targets defaultsRelease and suppresses customEntitlementComputation variants after AGP registers per-variant source sets lazily.

androidx.navigation is bumped 2.5.3 → 2.9.8 so Safe Args works with the new DSL. :feature:galaxy switches unit tests to kotlin-test-junit. ui/debugview/api.txt drops InternalDebugRevenueCatScreenViewModelFactory (debug-only; not in the release API Metalava analyzes).

Reviewed by Cursor Bugbot for commit e45c589. Bugbot is set up for automated code reviews on this repo. Configure here.

@AlvaroBrey AlvaroBrey self-assigned this Aug 27, 2026
@socket-security

socket-security Bot commented Aug 27, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedmaven/​org.jetbrains.kotlin/​kotlin-test-junit@​2.2.2110010090100100

View full report

@RevenueCat-Danger-Bot

Copy link
Copy Markdown
Public API changes in ui:debugview (2 removed)
- public final class InternalDebugRevenueCatScreenViewModelFactory extends androidx.lifecycle.ViewModelProvider.NewInstanceFactory
- ctor public InternalDebugRevenueCatScreenViewModelFactory(kotlin.jvm.functions.Function1<? super com.revenuecat.purchases.models.StoreTransaction,kotlin.Unit> onPurchaseCompleted, kotlin.jvm.functi…

Generated by 🚫 Danger

@AlvaroBrey
AlvaroBrey force-pushed the build/agp9-drop-compat-flags branch from 4f55318 to 9a6cad3 Compare August 27, 2026 18:48
@emerge-tools

emerge-tools Bot commented Aug 27, 2026

Copy link
Copy Markdown

📸 Snapshot Test

31 modified, 590 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility Paparazzi
com.revenuecat.testpurchasesuiandroidcompatibility.paparazzi
0 0 31 0 251 0 ✅ Approved
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
0 0 0 0 339 0 N/A

🛸 Powered by Emerge Tools

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.25%. Comparing base (08343dc) to head (e45c589).

Additional details and impacted files
@@            Coverage Diff            @@
##           11.0-dev    #4120   +/-   ##
=========================================
  Coverage     83.25%   83.25%           
=========================================
  Files           448      448           
  Lines         18110    18110           
  Branches       2744     2744           
=========================================
  Hits          15077    15077           
  Misses         2140     2140           
  Partials        893      893           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AlvaroBrey
AlvaroBrey force-pushed the build/agp9-drop-compat-flags branch from 9a6cad3 to c97fc41 Compare August 28, 2026 10:12
@AlvaroBrey
AlvaroBrey requested a review from tonidero August 28, 2026 11:03
@AlvaroBrey
AlvaroBrey marked this pull request as ready for review August 28, 2026 11:10
@AlvaroBrey
AlvaroBrey requested a review from a team as a code owner August 28, 2026 11:10
Base automatically changed from build/agp9-samples to 11.0-dev August 28, 2026 11:26
AlvaroBrey added a commit that referenced this pull request Aug 28, 2026
…e to AGP 9 (#4115)

- Moves the two standalone sample builds, `MagicWeatherCompose` and
`CustomEntitlementComputationSample`, to AGP 9.2.1 and Gradle 9.4.1,
catching them up with the root build.
- Both take the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out. #4120 removes it, stacked on top of this.

Targets integration branch (`11.0-dev`)



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Build-only version and Gradle property changes in example projects; no
runtime or library code changes.
> 
> **Overview**
> Aligns the **MagicWeatherCompose** and
**CustomEntitlementComputationSample** example builds with the root
toolchain by bumping **AGP** from `8.13.2` to **`9.2.1`** and the Gradle
wrapper from **`8.14.5`** to **`9.4.1`**.
> 
> Each sample’s `gradle.properties` adds
**`android.builtInKotlin=false`** and **`android.newDsl=false`** so
existing **`kotlin-android`** plugin usage keeps working until a
follow-up removes those opt-outs for AGP 10.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
16f5db0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
AlvaroBrey and others added 4 commits August 28, 2026 13:26
ui/debugview/api.txt loses InternalDebugRevenueCatScreenViewModelFactory. The class lives in
src/debug/kotlin, so it was never in the published defaultsRelease AAR and the signature was
wrong; confirmed zero occurrences in debugview-defaults-release.aar.
@AlvaroBrey
AlvaroBrey force-pushed the build/agp9-drop-compat-flags branch from d579358 to e45c589 Compare August 28, 2026 11:26

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you!

Comment thread ui/revenuecatui/build.gradle.kts
@AlvaroBrey
AlvaroBrey merged commit 4e0c9c2 into 11.0-dev Aug 28, 2026
36 checks passed
@AlvaroBrey
AlvaroBrey deleted the build/agp9-drop-compat-flags branch August 28, 2026 13:57
AlvaroBrey added a commit that referenced this pull request Aug 31, 2026
…e to AGP 9 (#4115)

- Moves the two standalone sample builds, `MagicWeatherCompose` and
`CustomEntitlementComputationSample`, to AGP 9.2.1 and Gradle 9.4.1,
catching them up with the root build.
- Both take the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out. #4120 removes it, stacked on top of this.

Targets integration branch (`11.0-dev`)



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Build-only version and Gradle property changes in example projects; no
runtime or library code changes.
> 
> **Overview**
> Aligns the **MagicWeatherCompose** and
**CustomEntitlementComputationSample** example builds with the root
toolchain by bumping **AGP** from `8.13.2` to **`9.2.1`** and the Gradle
wrapper from **`8.14.5`** to **`9.4.1`**.
> 
> Each sample’s `gradle.properties` adds
**`android.builtInKotlin=false`** and **`android.newDsl=false`** so
existing **`kotlin-android`** plugin usage keeps working until a
follow-up removes those opt-outs for AGP 10.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
16f5db0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
AlvaroBrey added a commit that referenced this pull request Aug 31, 2026
- Removes the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out that #3964 added, from the root build and from both standalone
sample builds, and takes `kotlin-android` out of 11 build scripts and
the 3 convention plugins.
- `ui/debugview/api.txt` loses
`InternalDebugRevenueCatScreenViewModelFactory`. Metalava now analyses
the release variant, and that class lives in `src/debug/kotlin`. The
release AAR contains zero occurrences of it, so it never shipped and the
entry was wrong
- Fixes `:dokkaGenerate`, which is already broken on `11.0-dev`.
Built-in Kotlin registers Dokka source sets per Android variant instead
of per Kotlin source set, and registers them lazily, so both
`:purchases` and `:ui:revenuecatui` now select the documented variant
through `configureEach`.
- Bumps `androidx.navigation` 2.5.3 to 2.9.8. safeargs 2.5.3 detects AGP
through the old DSL and hard fails, so the navigation bump is a
prerequisite for the new DSL rather than an independent upgrade.

### Checklist
- [ ] If applicable, unit tests
- [ ] If applicable, create follow-up issues for `purchases-ios` and
hybrids

<details><summary>Agent description</summary>

### Motivation

AGP 9 enables built-in Kotlin and rejects the
`org.jetbrains.kotlin.android` plugin outright:

```
The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
```

Opting out needs two flags together, `android.builtInKotlin=false` and
`android.newDsl=false`, because the kotlin-android plugin casts the
android extension to `BaseExtension`, which the new DSL drops. #3964
took that opt-out so the version bump stayed reviewable. AGP 10 removes
the opt-out, so the migration has to happen regardless; doing it
separately keeps the two diffs legible.

### Description

Four things blocked the migration, each hidden behind the previous.

**The old DSL extension types are gone.**
`com.android.build.gradle.BaseExtension` and
`com.android.build.gradle.LibraryExtension` do not exist once the new
DSL is active. The convention plugins now configure
`com.android.build.api.dsl.ApplicationExtension` and
`com.android.build.api.dsl.LibraryExtension`, and set `compileSdk`
directly rather than calling `compileSdkVersion()`.
`KotlinAndroidProjectExtension` stays as is: AGP's built-in Kotlin still
registers it.

**safeargs 2.5.3 cannot see AGP 9.** It fails with `safeargs plugin must
be used with android plugin`, because its detection goes through the old
DSL. The 2.5.3 jar references `com/android/build/gradle/BaseExtension`,
`AppExtension` and `gradle/api/BaseVariant`; the 2.9.8 jar references
only `com.android.build.api.variant.*`. `examples/purchase-tester` is
the only module applying the plugin, and it gets its own commit.

**Dokka source sets changed shape.** With the kotlin-android plugin,
Dokka saw the Kotlin source sets and the config addressed `main`,
`defaults` and `customEntitlementComputation`. Built-in Kotlin instead
registers one source set per Android variant, and registers them after
the `dokka { }` block runs, so `named("defaultsRelease")` throws even
though the name is valid later. Both modules now select through
`matching { }.configureEach`. `:purchases` keeps `reportUndocumented`,
`skipDeprecated`, the Android external documentation link, both source
links and the `paywalls.components` suppression, all moved onto the
selected variant. The explicit `customEntitlementComputation`
suppression is load-bearing, not redundant: Dokka defaults `suppress` to
`!isPublishable` and `customEntitlementComputationRelease` is published,
so it would otherwise be documented.

**`kotlin-test` stops carrying JUnit.** `:feature:galaxy` names
`kotlin-test-junit` instead. Verified rather than inherited: with plain
`kotlin-test`, `org.junit.Test` is unresolved across all 25 test
methods.

**Not visible in the diff:** `./gradlew :dokkaGenerate` fails on the
base commit with `Dokka cannot generate documentation for Android
projects with multiple enabled variants that have common source roots`.
The `docs-deploy` job that runs it is gated on release tags, so #3964
never exercised it. This PR is what makes it pass again, which is a
bigger deal than the diff makes it look.

**Worth noting separately:** AGP 9.2.1 declares
`kotlin-gradle-plugin:2.2.10`, so built-in Kotlin takes its compiler
from buildscript classpath resolution rather than from our catalog.
Confirmed the published contract from #3964 is intact rather than
assumed:

| check | result |
| --- | --- |
| resolved KGP | `2.2.21`, not AGP's bundled `2.2.10` |
| `:purchases` POM | `kotlin-stdlib:2.2.21` at `compile` scope |
| release AAR metadata | `mv=[1,8,0]`, so `languageVersion` 1.8 survives
|

### Regression gates

- `./gradlew :dokkaGenerate` for the Dokka rework. Generates 1367 pages
with `com.revenuecat.purchases.paywalls.components` still suppressed.
- `:feature:galaxy:testDefaultsDebugUnitTest` for the
`kotlin-test-junit` swap. Needs `--rerun-tasks`: a cached run passes
even with the dependency reverted, which is how this could slip through.
- `:examples:purchase-tester:assembleDebug` for the navigation bump.

**Rejected:**

- Wiring the Dokka variant selector to `ANDROID_VARIANT_TO_PUBLISH`
instead of hardcoding `"defaultsRelease"` twice. `fastlane/Fastfile:295`
rewrites that property per publish, so a `customEntitlementComputation`
publish would point Dokka at exactly the variant `:purchases`
suppresses.
- Hoisting the shared Dokka config into `ConfigureDokka.kt`. Dokka's
types are not on build-logic's compile classpath, so it needs the
reflection pattern from `ConfigureMetalava.kt`, and sharing
`reportUndocumented.set(true)` would newly turn it on for the four
public-library modules that have no `dokka { }` block.
- Moving the five hand-rolled app modules onto
`revenuecat-android-application`, which would collapse 5 of the 6
per-module edits. Each keeps 2 to 3 overrides, and both `testpurchases*`
modules deliberately pin old `compileSdk`/`targetSdk` to test
compatibility. Worth a separate PR, not this one.

**Limitations:**

- `examples/MagicWeather` and `test-apps/sdksizetesting` still apply
`kotlin-android`. They are separate builds pinned to AGP 8.13.2, so they
keep the plugin correctly and are untouched.
- `purchases/build.gradle.kts:120` has a dead `androidComponents {
onVariants }` block guarding a `"free"` product flavor that does not
exist in the repo. Pre-existing and unrelated, left alone.

</details>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Wide Gradle/AGP convention changes affect every Android module’s
compile and doc generation; navigation and Dokka behavior are
prerequisites for CI release docs, with limited runtime SDK impact.
> 
> **Overview**
> Completes the AGP 9 migration by removing the
**`android.builtInKotlin=false`** / **`android.newDsl=false`** opt-outs
and dropping **`org.jetbrains.kotlin.android`** across convention
plugins, app modules, samples, and the version catalog.
> 
> Build-logic now configures **`ApplicationExtension`** /
**`LibraryExtension`** from the new DSL (e.g. **`compileSdk`** instead
of **`BaseExtension`**). **`:purchases`** and **`:ui:revenuecatui`**
Dokka blocks use **`matching { }.configureEach`** so documentation
targets **`defaultsRelease`** and suppresses
**`customEntitlementComputation`** variants after AGP registers
per-variant source sets lazily.
> 
> **`androidx.navigation`** is bumped **2.5.3 → 2.9.8** so Safe Args
works with the new DSL. **`:feature:galaxy`** switches unit tests to
**`kotlin-test-junit`**. **`ui/debugview/api.txt`** drops
**`InternalDebugRevenueCatScreenViewModelFactory`** (debug-only; not in
the release API Metalava analyzes).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e45c589. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
AlvaroBrey added a commit that referenced this pull request Sep 7, 2026
…e to AGP 9 (#4115)

- Moves the two standalone sample builds, `MagicWeatherCompose` and
`CustomEntitlementComputationSample`, to AGP 9.2.1 and Gradle 9.4.1,
catching them up with the root build.
- Both take the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out. #4120 removes it, stacked on top of this.

Targets integration branch (`11.0-dev`)



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Build-only version and Gradle property changes in example projects; no
runtime or library code changes.
> 
> **Overview**
> Aligns the **MagicWeatherCompose** and
**CustomEntitlementComputationSample** example builds with the root
toolchain by bumping **AGP** from `8.13.2` to **`9.2.1`** and the Gradle
wrapper from **`8.14.5`** to **`9.4.1`**.
> 
> Each sample’s `gradle.properties` adds
**`android.builtInKotlin=false`** and **`android.newDsl=false`** so
existing **`kotlin-android`** plugin usage keeps working until a
follow-up removes those opt-outs for AGP 10.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
16f5db0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
AlvaroBrey added a commit that referenced this pull request Sep 7, 2026
- Removes the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out that #3964 added, from the root build and from both standalone
sample builds, and takes `kotlin-android` out of 11 build scripts and
the 3 convention plugins.
- `ui/debugview/api.txt` loses
`InternalDebugRevenueCatScreenViewModelFactory`. Metalava now analyses
the release variant, and that class lives in `src/debug/kotlin`. The
release AAR contains zero occurrences of it, so it never shipped and the
entry was wrong
- Fixes `:dokkaGenerate`, which is already broken on `11.0-dev`.
Built-in Kotlin registers Dokka source sets per Android variant instead
of per Kotlin source set, and registers them lazily, so both
`:purchases` and `:ui:revenuecatui` now select the documented variant
through `configureEach`.
- Bumps `androidx.navigation` 2.5.3 to 2.9.8. safeargs 2.5.3 detects AGP
through the old DSL and hard fails, so the navigation bump is a
prerequisite for the new DSL rather than an independent upgrade.

### Checklist
- [ ] If applicable, unit tests
- [ ] If applicable, create follow-up issues for `purchases-ios` and
hybrids

<details><summary>Agent description</summary>

### Motivation

AGP 9 enables built-in Kotlin and rejects the
`org.jetbrains.kotlin.android` plugin outright:

```
The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
```

Opting out needs two flags together, `android.builtInKotlin=false` and
`android.newDsl=false`, because the kotlin-android plugin casts the
android extension to `BaseExtension`, which the new DSL drops. #3964
took that opt-out so the version bump stayed reviewable. AGP 10 removes
the opt-out, so the migration has to happen regardless; doing it
separately keeps the two diffs legible.

### Description

Four things blocked the migration, each hidden behind the previous.

**The old DSL extension types are gone.**
`com.android.build.gradle.BaseExtension` and
`com.android.build.gradle.LibraryExtension` do not exist once the new
DSL is active. The convention plugins now configure
`com.android.build.api.dsl.ApplicationExtension` and
`com.android.build.api.dsl.LibraryExtension`, and set `compileSdk`
directly rather than calling `compileSdkVersion()`.
`KotlinAndroidProjectExtension` stays as is: AGP's built-in Kotlin still
registers it.

**safeargs 2.5.3 cannot see AGP 9.** It fails with `safeargs plugin must
be used with android plugin`, because its detection goes through the old
DSL. The 2.5.3 jar references `com/android/build/gradle/BaseExtension`,
`AppExtension` and `gradle/api/BaseVariant`; the 2.9.8 jar references
only `com.android.build.api.variant.*`. `examples/purchase-tester` is
the only module applying the plugin, and it gets its own commit.

**Dokka source sets changed shape.** With the kotlin-android plugin,
Dokka saw the Kotlin source sets and the config addressed `main`,
`defaults` and `customEntitlementComputation`. Built-in Kotlin instead
registers one source set per Android variant, and registers them after
the `dokka { }` block runs, so `named("defaultsRelease")` throws even
though the name is valid later. Both modules now select through
`matching { }.configureEach`. `:purchases` keeps `reportUndocumented`,
`skipDeprecated`, the Android external documentation link, both source
links and the `paywalls.components` suppression, all moved onto the
selected variant. The explicit `customEntitlementComputation`
suppression is load-bearing, not redundant: Dokka defaults `suppress` to
`!isPublishable` and `customEntitlementComputationRelease` is published,
so it would otherwise be documented.

**`kotlin-test` stops carrying JUnit.** `:feature:galaxy` names
`kotlin-test-junit` instead. Verified rather than inherited: with plain
`kotlin-test`, `org.junit.Test` is unresolved across all 25 test
methods.

**Not visible in the diff:** `./gradlew :dokkaGenerate` fails on the
base commit with `Dokka cannot generate documentation for Android
projects with multiple enabled variants that have common source roots`.
The `docs-deploy` job that runs it is gated on release tags, so #3964
never exercised it. This PR is what makes it pass again, which is a
bigger deal than the diff makes it look.

**Worth noting separately:** AGP 9.2.1 declares
`kotlin-gradle-plugin:2.2.10`, so built-in Kotlin takes its compiler
from buildscript classpath resolution rather than from our catalog.
Confirmed the published contract from #3964 is intact rather than
assumed:

| check | result |
| --- | --- |
| resolved KGP | `2.2.21`, not AGP's bundled `2.2.10` |
| `:purchases` POM | `kotlin-stdlib:2.2.21` at `compile` scope |
| release AAR metadata | `mv=[1,8,0]`, so `languageVersion` 1.8 survives
|

### Regression gates

- `./gradlew :dokkaGenerate` for the Dokka rework. Generates 1367 pages
with `com.revenuecat.purchases.paywalls.components` still suppressed.
- `:feature:galaxy:testDefaultsDebugUnitTest` for the
`kotlin-test-junit` swap. Needs `--rerun-tasks`: a cached run passes
even with the dependency reverted, which is how this could slip through.
- `:examples:purchase-tester:assembleDebug` for the navigation bump.

**Rejected:**

- Wiring the Dokka variant selector to `ANDROID_VARIANT_TO_PUBLISH`
instead of hardcoding `"defaultsRelease"` twice. `fastlane/Fastfile:295`
rewrites that property per publish, so a `customEntitlementComputation`
publish would point Dokka at exactly the variant `:purchases`
suppresses.
- Hoisting the shared Dokka config into `ConfigureDokka.kt`. Dokka's
types are not on build-logic's compile classpath, so it needs the
reflection pattern from `ConfigureMetalava.kt`, and sharing
`reportUndocumented.set(true)` would newly turn it on for the four
public-library modules that have no `dokka { }` block.
- Moving the five hand-rolled app modules onto
`revenuecat-android-application`, which would collapse 5 of the 6
per-module edits. Each keeps 2 to 3 overrides, and both `testpurchases*`
modules deliberately pin old `compileSdk`/`targetSdk` to test
compatibility. Worth a separate PR, not this one.

**Limitations:**

- `examples/MagicWeather` and `test-apps/sdksizetesting` still apply
`kotlin-android`. They are separate builds pinned to AGP 8.13.2, so they
keep the plugin correctly and are untouched.
- `purchases/build.gradle.kts:120` has a dead `androidComponents {
onVariants }` block guarding a `"free"` product flavor that does not
exist in the repo. Pre-existing and unrelated, left alone.

</details>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Wide Gradle/AGP convention changes affect every Android module’s
compile and doc generation; navigation and Dokka behavior are
prerequisites for CI release docs, with limited runtime SDK impact.
> 
> **Overview**
> Completes the AGP 9 migration by removing the
**`android.builtInKotlin=false`** / **`android.newDsl=false`** opt-outs
and dropping **`org.jetbrains.kotlin.android`** across convention
plugins, app modules, samples, and the version catalog.
> 
> Build-logic now configures **`ApplicationExtension`** /
**`LibraryExtension`** from the new DSL (e.g. **`compileSdk`** instead
of **`BaseExtension`**). **`:purchases`** and **`:ui:revenuecatui`**
Dokka blocks use **`matching { }.configureEach`** so documentation
targets **`defaultsRelease`** and suppresses
**`customEntitlementComputation`** variants after AGP registers
per-variant source sets lazily.
> 
> **`androidx.navigation`** is bumped **2.5.3 → 2.9.8** so Safe Args
works with the new DSL. **`:feature:galaxy`** switches unit tests to
**`kotlin-test-junit`**. **`ui/debugview/api.txt`** drops
**`InternalDebugRevenueCatScreenViewModelFactory`** (debug-only; not in
the release API Metalava analyzes).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e45c589. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
AlvaroBrey added a commit that referenced this pull request Sep 7, 2026
…e to AGP 9 (#4115)

- Moves the two standalone sample builds, `MagicWeatherCompose` and
`CustomEntitlementComputationSample`, to AGP 9.2.1 and Gradle 9.4.1,
catching them up with the root build.
- Both take the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out. #4120 removes it, stacked on top of this.

Targets integration branch (`11.0-dev`)



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Build-only version and Gradle property changes in example projects; no
runtime or library code changes.
> 
> **Overview**
> Aligns the **MagicWeatherCompose** and
**CustomEntitlementComputationSample** example builds with the root
toolchain by bumping **AGP** from `8.13.2` to **`9.2.1`** and the Gradle
wrapper from **`8.14.5`** to **`9.4.1`**.
> 
> Each sample’s `gradle.properties` adds
**`android.builtInKotlin=false`** and **`android.newDsl=false`** so
existing **`kotlin-android`** plugin usage keeps working until a
follow-up removes those opt-outs for AGP 10.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
16f5db0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
AlvaroBrey added a commit that referenced this pull request Sep 7, 2026
- Removes the `android.builtInKotlin=false` and `android.newDsl=false`
opt-out that #3964 added, from the root build and from both standalone
sample builds, and takes `kotlin-android` out of 11 build scripts and
the 3 convention plugins.
- `ui/debugview/api.txt` loses
`InternalDebugRevenueCatScreenViewModelFactory`. Metalava now analyses
the release variant, and that class lives in `src/debug/kotlin`. The
release AAR contains zero occurrences of it, so it never shipped and the
entry was wrong
- Fixes `:dokkaGenerate`, which is already broken on `11.0-dev`.
Built-in Kotlin registers Dokka source sets per Android variant instead
of per Kotlin source set, and registers them lazily, so both
`:purchases` and `:ui:revenuecatui` now select the documented variant
through `configureEach`.
- Bumps `androidx.navigation` 2.5.3 to 2.9.8. safeargs 2.5.3 detects AGP
through the old DSL and hard fails, so the navigation bump is a
prerequisite for the new DSL rather than an independent upgrade.

### Checklist
- [ ] If applicable, unit tests
- [ ] If applicable, create follow-up issues for `purchases-ios` and
hybrids

<details><summary>Agent description</summary>

### Motivation

AGP 9 enables built-in Kotlin and rejects the
`org.jetbrains.kotlin.android` plugin outright:

```
The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
```

Opting out needs two flags together, `android.builtInKotlin=false` and
`android.newDsl=false`, because the kotlin-android plugin casts the
android extension to `BaseExtension`, which the new DSL drops. #3964
took that opt-out so the version bump stayed reviewable. AGP 10 removes
the opt-out, so the migration has to happen regardless; doing it
separately keeps the two diffs legible.

### Description

Four things blocked the migration, each hidden behind the previous.

**The old DSL extension types are gone.**
`com.android.build.gradle.BaseExtension` and
`com.android.build.gradle.LibraryExtension` do not exist once the new
DSL is active. The convention plugins now configure
`com.android.build.api.dsl.ApplicationExtension` and
`com.android.build.api.dsl.LibraryExtension`, and set `compileSdk`
directly rather than calling `compileSdkVersion()`.
`KotlinAndroidProjectExtension` stays as is: AGP's built-in Kotlin still
registers it.

**safeargs 2.5.3 cannot see AGP 9.** It fails with `safeargs plugin must
be used with android plugin`, because its detection goes through the old
DSL. The 2.5.3 jar references `com/android/build/gradle/BaseExtension`,
`AppExtension` and `gradle/api/BaseVariant`; the 2.9.8 jar references
only `com.android.build.api.variant.*`. `examples/purchase-tester` is
the only module applying the plugin, and it gets its own commit.

**Dokka source sets changed shape.** With the kotlin-android plugin,
Dokka saw the Kotlin source sets and the config addressed `main`,
`defaults` and `customEntitlementComputation`. Built-in Kotlin instead
registers one source set per Android variant, and registers them after
the `dokka { }` block runs, so `named("defaultsRelease")` throws even
though the name is valid later. Both modules now select through
`matching { }.configureEach`. `:purchases` keeps `reportUndocumented`,
`skipDeprecated`, the Android external documentation link, both source
links and the `paywalls.components` suppression, all moved onto the
selected variant. The explicit `customEntitlementComputation`
suppression is load-bearing, not redundant: Dokka defaults `suppress` to
`!isPublishable` and `customEntitlementComputationRelease` is published,
so it would otherwise be documented.

**`kotlin-test` stops carrying JUnit.** `:feature:galaxy` names
`kotlin-test-junit` instead. Verified rather than inherited: with plain
`kotlin-test`, `org.junit.Test` is unresolved across all 25 test
methods.

**Not visible in the diff:** `./gradlew :dokkaGenerate` fails on the
base commit with `Dokka cannot generate documentation for Android
projects with multiple enabled variants that have common source roots`.
The `docs-deploy` job that runs it is gated on release tags, so #3964
never exercised it. This PR is what makes it pass again, which is a
bigger deal than the diff makes it look.

**Worth noting separately:** AGP 9.2.1 declares
`kotlin-gradle-plugin:2.2.10`, so built-in Kotlin takes its compiler
from buildscript classpath resolution rather than from our catalog.
Confirmed the published contract from #3964 is intact rather than
assumed:

| check | result |
| --- | --- |
| resolved KGP | `2.2.21`, not AGP's bundled `2.2.10` |
| `:purchases` POM | `kotlin-stdlib:2.2.21` at `compile` scope |
| release AAR metadata | `mv=[1,8,0]`, so `languageVersion` 1.8 survives
|

### Regression gates

- `./gradlew :dokkaGenerate` for the Dokka rework. Generates 1367 pages
with `com.revenuecat.purchases.paywalls.components` still suppressed.
- `:feature:galaxy:testDefaultsDebugUnitTest` for the
`kotlin-test-junit` swap. Needs `--rerun-tasks`: a cached run passes
even with the dependency reverted, which is how this could slip through.
- `:examples:purchase-tester:assembleDebug` for the navigation bump.

**Rejected:**

- Wiring the Dokka variant selector to `ANDROID_VARIANT_TO_PUBLISH`
instead of hardcoding `"defaultsRelease"` twice. `fastlane/Fastfile:295`
rewrites that property per publish, so a `customEntitlementComputation`
publish would point Dokka at exactly the variant `:purchases`
suppresses.
- Hoisting the shared Dokka config into `ConfigureDokka.kt`. Dokka's
types are not on build-logic's compile classpath, so it needs the
reflection pattern from `ConfigureMetalava.kt`, and sharing
`reportUndocumented.set(true)` would newly turn it on for the four
public-library modules that have no `dokka { }` block.
- Moving the five hand-rolled app modules onto
`revenuecat-android-application`, which would collapse 5 of the 6
per-module edits. Each keeps 2 to 3 overrides, and both `testpurchases*`
modules deliberately pin old `compileSdk`/`targetSdk` to test
compatibility. Worth a separate PR, not this one.

**Limitations:**

- `examples/MagicWeather` and `test-apps/sdksizetesting` still apply
`kotlin-android`. They are separate builds pinned to AGP 8.13.2, so they
keep the plugin correctly and are untouched.
- `purchases/build.gradle.kts:120` has a dead `androidComponents {
onVariants }` block guarding a `"free"` product flavor that does not
exist in the repo. Pre-existing and unrelated, left alone.

</details>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Wide Gradle/AGP convention changes affect every Android module’s
compile and doc generation; navigation and Dokka behavior are
prerequisites for CI release docs, with limited runtime SDK impact.
> 
> **Overview**
> Completes the AGP 9 migration by removing the
**`android.builtInKotlin=false`** / **`android.newDsl=false`** opt-outs
and dropping **`org.jetbrains.kotlin.android`** across convention
plugins, app modules, samples, and the version catalog.
> 
> Build-logic now configures **`ApplicationExtension`** /
**`LibraryExtension`** from the new DSL (e.g. **`compileSdk`** instead
of **`BaseExtension`**). **`:purchases`** and **`:ui:revenuecatui`**
Dokka blocks use **`matching { }.configureEach`** so documentation
targets **`defaultsRelease`** and suppresses
**`customEntitlementComputation`** variants after AGP registers
per-variant source sets lazily.
> 
> **`androidx.navigation`** is bumped **2.5.3 → 2.9.8** so Safe Args
works with the new DSL. **`:feature:galaxy`** switches unit tests to
**`kotlin-test-junit`**. **`ui/debugview/api.txt`** drops
**`InternalDebugRevenueCatScreenViewModelFactory`** (debug-only; not in
the release API Metalava analyzes).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e45c589. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants