Skip to content

[DO NOT MERGE] 11.0 dev branch - #4125

Draft
AlvaroBrey wants to merge 10 commits into
mainfrom
11.0-dev
Draft

AlvaroBrey wants to merge 10 commits into
mainfrom
11.0-dev

Conversation

@AlvaroBrey

@AlvaroBrey AlvaroBrey commented Aug 28, 2026

Copy link
Copy Markdown
Member

PR grouping all of the upcoming 11.0 breaking release changes


Note

High Risk
Major toolchain and version-line release (Kotlin metadata floor, AGP 9) affects every consumer build; integrators on Kotlin < 2.1 will fail until they upgrade.

Overview
v11 bumps the SDK to 11.0.0-SNAPSHOT and retargets the whole repo on AGP 9.2.1, Kotlin 2.2.21, and Gradle 9.4.1, with documented Kotlin 2.1.0+ minimum for integrators (migrations/v11-MIGRATION.md, README).

Build conventions move to AGP 9 APIs (ApplicationExtension / LibraryExtension), drop explicit org.jetbrains.kotlin.android from convention plugins and root apply false wiring, and adjust libraries for AGP 9 (aarMetadata.minCompileSdk = 1, enableUnitTest via variant API). Compose samples switch to the Kotlin Compose compiler plugin and remove composeOptions.kotlinCompilerExtensionVersion; JVM targets move from kotlinOptions to kotlin { compilerOptions }. Dokka source sets use lazy matching on flavor variants instead of fixed named sets.

CI/docs: removes the shared android-dependencies Gradle prefetch in favor of restore_gradle_cache on sample builds, publishes docs to 11.0.0-SNAPSHOT, and runs snapshot + sample-app workflow on 11.0-dev as well as main (temporary TODO). ui/debugview public API dump drops InternalDebugRevenueCatScreenViewModelFactory.

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

@AlvaroBrey AlvaroBrey self-assigned this Aug 28, 2026
@AlvaroBrey
AlvaroBrey requested a review from a team as a code owner August 28, 2026 10:05
@socket-security

socket-security Bot commented Aug 28, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: maven org.jetbrains.kotlin:kotlin-daemon-client is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: examples/CustomEntitlementComputationSample/gradle/libs.versions.tomlmaven/org.jetbrains.kotlin/kotlin-gradle-plugin@2.2.21maven/org.jetbrains.kotlin/kotlin-daemon-client@2.2.21

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore maven/org.jetbrains.kotlin/kotlin-daemon-client@2.2.21. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: maven org.jetbrains.kotlin:kotlin-gradle-plugin is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: examples/CustomEntitlementComputationSample/gradle/libs.versions.tomlmaven/org.jetbrains.kotlin/kotlin-gradle-plugin@2.2.21

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore maven/org.jetbrains.kotlin/kotlin-gradle-plugin@2.2.21. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: maven org.jetbrains.kotlin:kotlin-gradle-plugin is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: examples/CustomEntitlementComputationSample/gradle/libs.versions.tomlmaven/org.jetbrains.kotlin/kotlin-gradle-plugin@2.2.21

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore maven/org.jetbrains.kotlin/kotlin-gradle-plugin@2.2.21. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 351c2b5. Configure here.

Comment thread gradle/libs.versions.toml
@emerge-tools

emerge-tools Bot commented Aug 28, 2026

Copy link
Copy Markdown

📸 Snapshot Test

54 modified, 578 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility Paparazzi
com.revenuecat.testpurchasesuiandroidcompatibility.paparazzi
0 0 33 0 252 0 ⏳ Needs approval
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
0 0 21 0 326 0 ⏳ Needs approval

🛸 Powered by Emerge Tools

@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Aug 28, 2026

Copy link
Copy Markdown
1 Warning
⚠️ This PR increases the size of the repo by more than 100.00 KB (increased by 139.31 KB).
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

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.72%. Comparing base (6b1cc29) to head (107e81e).

Files with missing lines Patch % Lines
...rchases/paywalls/components/common/Localization.kt 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4125      +/-   ##
==========================================
+ Coverage   83.69%   83.72%   +0.03%     
==========================================
  Files         461      461              
  Lines       18831    18826       -5     
  Branches     2854     2853       -1     
==========================================
+ Hits        15760    15763       +3     
+ Misses       2155     2146       -9     
- Partials      916      917       +1     

☔ 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 marked this pull request as draft August 31, 2026 13:36
@AlvaroBrey
AlvaroBrey force-pushed the 11.0-dev branch 2 times, most recently from e6d3d49 to 10f2532 Compare September 7, 2026 14:37
@emerge-tools

emerge-tools Bot commented Sep 8, 2026

Copy link
Copy Markdown

2 builds increased size

Name Version Download Change Install Change Approval
⚠️ TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
1.0 (1) 74.9 MB ⬆️ 2.4 MB (3.36%) 123.8 MB ⬆️ 6.2 MB (5.26%) N/A
⚠️ SDKSizeTesting
com.revenuecat.testapps.sdksizetesting
1.0 (1) 13.8 MB ⬆️ 1.1 MB (8.82%) 39.6 MB ⬆️ 2.9 MB (7.9%) ⏳ Needs approval

TestPurchasesUIAndroidCompatibility 1.0 (1)
com.revenuecat.testpurchasesuiandroidcompatibility

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 6.2 MB (5.26%)
Total download size change: ⬆️ 2.4 MB (3.36%)

Largest size changes

Item Install Size Change Download Size Change
🗑 androidx.compose.material.icons ⬇️ -378.3 kB ⬇️ -126.2 kB
🗑 androidx.compose.runtime.changelist ⬇️ -66.1 kB ⬇️ -22.1 kB
📝 androidx.compose.runtime.LinkComposer ⬆️ 60.8 kB ⬆️ 20.6 kB
📝 androidx.compose.runtime.GapComposer ⬆️ 49.0 kB ⬆️ 16.6 kB
📝 androidx.compose.material3.SearchBarKt ⬆️ 46.3 kB ⬆️ 15.7 kB
View Treemap

Image of diff

SDKSizeTesting 1.0 (1)
com.revenuecat.testapps.sdksizetesting

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 2.9 MB (7.9%)
Total download size change: ⬆️ 1.1 MB (8.82%)

Largest size changes

Item Install Size Change Download Size Change
🗑 androidx.compose.material.icons ⬇️ -226.4 kB ⬇️ -76.8 kB
🗑 androidx.compose.runtime.changelist ⬇️ -37.8 kB ⬇️ -12.8 kB
resources.arsc ⬆️ 51.4 kB ⬆️ 12.5 kB
com.google.android.gms.internal.play_billing.zzhe ⬇️ -28.0 kB ⬇️ -10.5 kB
com.google.android.gms.internal.play_billing.zzhu ⬆️ 27.8 kB ⬆️ 10.4 kB
View Treemap

Image of diff


🛸 Powered by Emerge Tools

Comment trigger: Size diff threshold of 100.00kB exceeded

@AlvaroBrey
AlvaroBrey force-pushed the 11.0-dev branch 2 times, most recently from e5fc244 to 3ce501a Compare September 17, 2026 15:18
> [!NOTE]
> This targets branch `11.0-dev` which will become the epic branch for
the upcoming major release. Once this first PR is merged, I'll open a
continuous one from that one to `main` until we are ready to release it
altogether.

- Moves the SDK's build toolchain to AGP 9:

| Dependency | From | To |
|---|---|---|
| AGP | 8.13.2 | **9.2.1** |
| Gradle | 8.14.5 | **9.4.1** |
| Kotlin | 2.0.21 | **2.2.21** |
| Poko | 0.17.2 | **0.20.2** |
| Paparazzi | 2.0.0-alpha02 | **2.0.0-alpha05** |

- No public API change and no `api*.txt` diff.
- **Why this becomes a major**: this transitively raises the miminum
kotlin version for consumers to 2.1.0. up from 1.8.0.
- Kotlin lands on **2.2.21, the minimum required by AGP 9**,
- AGP is capped at **9.2.1** rather than latest: AGP 9.3.x requires
Gradle 9.5+, and Gradle 9.5+ embeds a Kotlin that a 2.2 compiler cannot
read, bumping consumers to an even higher minimum Kotlin version.
- Nothing downstream needs AGP 9 or Gradle 9, just the Kotlin bump. The
published AAR metadata is unchanged (`minCompileSdk=1`,
`minAndroidGradlePluginVersion=1.0.0`).

Companion docs PR: RevenueCat/docs PR 1942
Companion PHC PR:
RevenueCat/purchases-hybrid-common PR 1844

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

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

AGP 8 is increasingly behind, and AGP 9 removes a number of DSL surfaces
we still used. The
prerequisite work already landed separately in #3931, #3961 and #3963.

AGP 9 has a runtime dependency on Kotlin Gradle Plugin **2.2.10 or
higher**, so a Kotlin bump is not
optional here. Everything above that floor is a choice, and this PR
deliberately takes the minimum.

An earlier revision of this branch used Kotlin 2.3.21. Measuring the
cost changed the decision:

- **It breaks every React Native consumer.** We publish `kotlin-stdlib`
at `compile` scope and Gradle
resolves consumers onto it. A compiler reads metadata at most one minor
ahead. Kotlin 2.3.x
publishes metadata 2.3.0, and React Native's own Gradle plugin pins the
compiler: 2.0.21 through
RN 0.79, 2.1.20 through RN 0.86, with only RN `main` on 2.2.0.
Reproduced on a real RN 0.86 app:
`metadata version is 2.3.0 / compiler version 2.1.0 can read versions up
to 2.2.0`. Every RN app
would have needed an explicit `kotlin-gradle-plugin` version in
`android/build.gradle`.
- **It forces the K2 migration.** Kotlin 2.3.21 rejects `languageVersion
1.8` outright. 2.2.x still
accepts it, so the frontend migration stays out of a version bump where
it does not belong.
- **It does not avoid a second bump anyway.** Kotlin 2.4 removes
`languageVersion 1.8` regardless.

At Kotlin 2.2.21 the published metadata is 2.2.0, which RN 0.81+ reads
untouched.

This is the non-obvious constraint, and CI found it rather than local
builds.

`:codegen` is a `java-gradle-plugin` module, so it compiles against the
Kotlin that **Gradle itself
embeds**. Embedded Kotlin by Gradle release:

| Gradle | Embedded Kotlin | Readable by a 2.2 compiler (max 2.3.0) |
|---|---|---|
| 9.3.1 | 2.2.21 | yes |
| 9.4.1 | 2.3.0 | yes, exactly at the limit |
| 9.5.1 | 2.3.20 | no |
| 9.7.0 | 2.4.0 | no |

Gradle 9.7.0 therefore failed `prepare-tests` with
`metadata version is 2.4.0, but the compiler version 2.2.0 can read
versions up to 2.3.0`.

AGP 9.3.x requires Gradle 9.5.0 or higher (`Minimum supported Gradle
version is 9.5.0`), which is
exactly the range we cannot read. **AGP 9.2.1 is the newest release that
runs on Gradle 9.4.1**;
9.1.1 and 9.0.1 were also confirmed to work, 9.3.1 was confirmed not to.

So the AGP ceiling here is a consequence of the Kotlin floor, not an
independent preference. When the
deferred Kotlin 2.3 bump happens, AGP and Gradle can move up with it.

- Standalone sample apps (`CustomEntitlementComputationSample`,
`MagicWeather`, `MagicWeatherCompose`)
move to Kotlin 2.2.21 and gain the `compose-compiler` plugin alias. They
consume our published
  artifacts, so they need a compiler that can read our metadata.
- Unit tests are re-enabled on the release variant via
`HasUnitTestBuilder.enableUnitTest`, because
AGP 9 only creates unit tests for the `testBuildType` variant and the
published variant is release.
- The `androidDependencies` cache-warm CI step is dropped; AGP 9 removed
that task.
- `migrations/v11-MIGRATION.md` documents the new Kotlin floor,
mirroring the v9 guide.
- README `Requirements` now says Kotlin 2.1.0+.

- **Kotlin 2.3 and the K1 to K2 migration**, until React Native ships
the KGP 2.2 pin already on its
`main` branch. Doing it sooner buys nothing and costs every RN app a
migration step.
- **AGP 9 built-in Kotlin** (previously #3965). Not breaking and not
required; the
`android.builtInKotlin=false` / `android.newDsl=false` opt-out only has
to go at AGP 10.

- `assembleDebug` across all modules; release AARs for `:purchases` and
`:ui:revenuecatui`.
- `:purchases` unit tests, `:ui:debugview:verifyPaparazziDefaultsDebug`,
`detektAll`.
- `scripts/api-check.sh` with **zero** `api*.txt` diff.
- Published contract checked directly: POM carries
`kotlin-stdlib:2.2.21` at `compile` scope, the
release AAR still reports `minCompileSdk=1` and
`minAndroidGradlePluginVersion=1.0.0`, and our own
  classes carry metadata `mv = {1, 8, 0}`.
- Consumer floor measured against the real release AAR plus the stdlib
we publish: Kotlin 2.1.21
compiles; 2.0.21 and 1.9.24 fail. That is what the 2.1.0 claim in the
README rests on.

The Kotlin floor is what propagates, not AGP. Nothing downstream needs
AGP 9 or Gradle 9.

The only direct consumer is purchases-hybrid-common, which re-exposes us
at compile scope via
`api(libs.purchases)`. RevenueCat/purchases-hybrid-common PR 1844 raises
its Kotlin accordingly, and the
hybrid SDKs pick the floor up from there rather than from this repo.

Documentation follows separately:

- RevenueCat/docs PR 1942 adds the Kotlin minimum to the Android, Flutter
and React Native installation
pages, and adds the 9.x-to-10.x and 10.x-to-11.x migration guides the
site was missing.
- RevenueCat/purchases-flutter PR 1876 states the Kotlin minimum in that
repo's README. Flutter applies
the version from the app's `android/settings.gradle`, so the requirement
lands on the consuming app
  and the Flutter version itself does not move.
- RevenueCat/react-native-purchases PR 1925 raises the React Native floor
to 0.80.0, the first release
  whose Gradle plugin pins a Kotlin that can read our metadata.

</details>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Major toolchain and minimum Kotlin version changes affect all
consumers and CI, though public API and published AAR AGP/minSdk
metadata are intentionally unchanged.
>
> **Overview**
> This PR moves the SDK build to **AGP 9.2.1**, **Gradle 9.4.1**, and
**Kotlin 2.2.21**, and bumps related tooling (Poko, Paparazzi, baseline
profile plugin). **`gradle.properties`** opts out of AGP 9’s built-in
Kotlin and new DSL for now.
>
> The **consumer-facing change** is a higher Kotlin floor: README and
**`migrations/v11-MIGRATION.md`** document **Kotlin 2.1.0+** (up from
1.8.0), driven by published stdlib metadata—not a public API change.
>
> **Build-logic** re-enables unit tests on library variants under AGP 9
via **`enableUnitTest`**, and keeps **`aarMetadata.minCompileSdk = 1`**
so published AAR requirements don’t tighten.
>
> Compose sample apps switch to the **Kotlin Compose compiler plugin**
and drop **`composeOptions.kotlinCompilerExtensionVersion`**. Several
modules replace deprecated **`kotlinOptions`** with
**`kotlin.compilerOptions.jvmTarget`**.
>
> **CircleCI** drops the removed **`androidDependencies`** warmup and
uses Gradle cache restore on standalone sample builds instead.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1b6b4df. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Title says it all.

This is to test downstream integrations

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Mechanical version string updates with no logic changes; only affects
snapshot publishing, docs paths, and sample/test dependency pins for
downstream integration testing.
>
> **Overview**
> Bumps the Android SDK development version from **10.20.0-SNAPSHOT** to
**11.0.0-SNAPSHOT** so the **11.0** branch publishes and documents under
the new major line.
>
> **`gradle.properties`** `VERSION_NAME`, **`.version`**, and
**`Config.frameworkVersion`** are updated together so Maven artifacts,
runtime reporting, and release metadata stay aligned. CircleCI
**docs-deploy** now syncs **`docs/11.0.0-SNAPSHOT`** to the matching S3
path.
>
> Example apps (Magic Weather, Magic Weather Compose, Custom Entitlement
Computation) and **`test-apps/sdksizetesting`** pin **`purchases =
"11.0.0-SNAPSHOT"`** in their version catalogs for integration and size
testing against this snapshot.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8101201. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…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 -->
- 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 -->
Moves `sdksizetesting` to the new AGP version. Targets `11.0-dev`
integration branch

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a
summary for commit afd111d. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Temporary while 11.0-dev is WIP, so we can test it out in PHC and
hybrids. we'll remove it before merge

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> CI-only branch filter change for snapshot publishing; no application
or release logic changes, and the extra branch is explicitly temporary.
> 
> **Overview**
> **Temporarily** extends the `snapshot-deploy-sample-app-tests`
CircleCI workflow so it runs on **`11.0-dev`** as well as **`main`**,
not only on `main`.
> 
> That workflow still deploys Maven snapshots (`deploy-snapshot`) and
then builds sample apps that depend on those artifacts. The branch gate
is now an `or` on `main` / `11.0-dev`, with a **TODO** to remove the
`11.0-dev` branch before merging that line into `main`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
41cdc50. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
- Bumps Play Billing Library from 8.3.0 to 9.1.0. Targets `11.0-dev`.
- **BC9 pulls in `androidx.core` 1.15.0, which requires every consuming
module to use compileSdk 35 or higher.** This breaks compilation for
consumers. I've also updated it in `libs.versions.toml` to be explicit.
- Only behaviour change: When the Play Store is blocked by the system
(kids mode), BC9 returns `BILLING_UNAVAILABLE` instead of `ERROR`, so
apps now get `PurchaseNotAllowedError` instead of `StoreProblemError`
there. I couldn't reproduce this since it's not configurable on
devices/emulator, would require a custom OS build.
- No public API changes. 
- Tested (license tester):
    - Consumable purchase
    - Consumable repurchase
    - Non-consumable
    - Subscription
    - Prorated switch
    - Pending purchase
    - User cancel
    - Restore 
    - Store unavailable
    - Own-IAP-code mode (SDK only syncs)
    

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

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

### Motivation

Play Billing Library 9.0.0 (May 2026) and 9.1.0 (June 2026) are out. The
release notes list no removals, but diffing the 8.3.0 and 9.1.0 AARs
shows `SkuDetails`, `SkuDetailsParams`, `SkuDetailsResponseListener`,
`BillingClient.SkuType`, `BillingFlowParams.Builder.setSkuDetails` and
`QueryPurchaseHistoryParams` are gone, and the POM gains
`androidx.core:core:1.15.0` (`minCompileSdk=35` in its aar-metadata).
That makes the bump breaking for consumers on compileSdk 34, so it rides
the v11 major.

### Description

- `billingClient` 8.3.0 to 9.1.0 in the version catalog.
- Removed `buildQueryPurchaseHistoryParams` (no callers since the BC8
migration) and the unused `SkuDetails` parameter in the api-tester.
- BC 9.x changed the public `InAppMessageResult(int, String)`
constructor to ignore its arguments and always yield `NO_ACTION_NEEDED`.
The SDK never constructs that class, so this is test-only; the two
in-app message tests in `BillingWrapperTest` now mock the result.
- Compatibility test apps and `baselineprofile` move from compileSdk 34
to 35, the new floor they should verify.
- `migrations/v11-MIGRATION.md` gains sections for BC9, the compileSdk
requirement (with the `checkDebugAarMetadata` error text, captured by
building a test app at compileSdk 34), the blocked-Play-Store error
change, and the own-IAP-code note.
- Out of scope: Billing Choice program and dynamic product token APIs
added in 9.x.

</details>
…7.3 (#4258)

Paywalls whose localizations contain images fail to parse on `11.0-dev`.
This fixes that and makes the catalog say what consumers actually
resolve.

- `11.0-dev` is affected today, `main` is not. `androidx.savedstate`
already forces kotlinx-serialization 1.7.3 onto any consumer app
classpath on the major branch, while `main` still resolves 1.5.1. No
released version is affected.
- 1.7.3 no longer rewinds the streaming decoder after a failed
`decodeSerializableValue`. `LocalizationDataSerializer` picks between
`Text` and `Image` by trial and error, so the failed `Text` attempt left
the lexer past the opening brace and every `Image` retry failed.
- The catalog pinned 1.5.1, which is why no test caught it: `:purchases`
resolves 1.5.1 in isolation, so its own test run never saw the version
apps get.
- The trial and error is deliberately unchanged. Only the source it
reads from changed, from the stream to a re-readable `JsonElement`.
- Found while raising the same pin in #4253. That PR does not cause this
break and no longer carries the fix.

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

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

### Motivation

`LocalizationData` is a make-shift union: a localization value is either
a plain string or a `ThemeImageUrls` object, with no `type`
discriminator on the wire. The serializer resolved that by trying `Text`
first and falling back to `Image` on `SerializationException`.

That works only if a failed attempt leaves the decoder where it started.
kotlinx-serialization 1.7.3 does not put it back, so the fallback starts
mid-token. For input beginning `{ "light": ...`, the `Text` attempt
consumes the `{` and the `Image` retry fails at offset 4 on the `"` of
`"light"`.

Verified by resolving `examples/paywall-tester`'s
`debugRuntimeClasspath`:

| ref | kotlinx-serialization |
| :--- | :--- |
| `main` | 1.5.1 |
| `11.0-dev` | 1.7.3 |

### Description

- Decodes the element once through `JsonDecoder.decodeJsonElement()`,
then runs the same two serializers against it. A `JsonElement` can be
read repeatedly, so the fallback sees the whole value.
- Guards the decoder cast with `as?` and a `SerializationException`,
matching the other custom serializers in the module rather than risking
a `ClassCastException` from a bare cast.
- Pins `kotlinxSerializationJSON` to 1.7.3. This is what makes
`LocalizationDataTests > Should properly deserialize
LocalizationData[ThemeImageUrls]` a regression gate: with the pin and
without the fix it fails, which is how the break was confirmed on a
branch carrying no other change.
- Updates the `WebViewContextSnapshotTest` fixture, since 1.7.3
pretty-prints empty objects as `{}` rather than `{\n}`. Production does
not pretty-print, so the wire payload is unchanged. #4253 carries the
same fixture change because it reaches 1.7.3 by a different route;
whichever lands second will find it already applied.

Worth a follow-up, pre-existing and untouched here:
`LocalizationDataSerializer` never attempts `LocalizationData.Video`,
only `Text` then `Image`, though `Video` is a member of the sealed
interface.

</details>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches paywall JSON deserialization for all localizations and aligns
the build to 1.7.3; behavior change is limited to fixing broken image
parsing, but parsing is on the critical paywall load path.
> 
> **Overview**
> Fixes paywall parsing when localizations include **image** values
under **kotlinx-serialization 1.7.3**, which apps on `11.0-dev` already
resolve via transitive deps while the catalog still pinned 1.5.1.
> 
> **`LocalizationDataSerializer`** still picks `Text` vs `Image` by
trial and error, but it now reads a full **`JsonElement`** once and
retries against that buffer. In 1.7.3 a failed stream decode no longer
rewinds, so the old fallback started mid-object and broke
`ThemeImageUrls` payloads.
> 
> The version catalog bumps **`kotlinxSerializationJSON` to 1.7.3** so
module tests exercise the same serializer behavior as consumer apps.
**`WebViewContextSnapshotTest`** updates expected pretty-printed JSON
for an empty `inputs` object (`{}` vs multiline `{\n}`); wire format is
unchanged.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a530a5f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
> this targets the next major branch (11.0-dev)

Updates the Compose BOM from `2024.09.00` to `2026.06.01` (Compose
1.11.4) for SDK-4243.

- `material-icons` now needs explicit declaration. Compose stopped
publishing it after 1.7.8 and `material` 1.8.0 dropped its dependency on
it, so apps that reached `Icons.Default.*` transitively through
RevenueCatUI now have to declare it themselves. That is the small
breaking part of this PR.
- Stops at Compose 1.11.4, not the latest. The 1.12 line requires
compileSdk 37 and JVM target 11, which we are not tackling in this
major.
- Compose 1.11 ships lint checks this module does not satisfy so they
are baselined


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

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

### Motivation

The BOM had not moved in two years. It is pinned in the `11.0-dev` major
branch, which is the only place the consumer-facing fallout is
acceptable.

Three separate removals land at once, which is why the diff touches more
than a version number:

1. `materialIcon` and `materialPath`, the builders the SDK's own
hand-drawn vectors were written against, went away with
`material-icons`.
2. `Icons.*` stopped arriving transitively through `compose-material`.
3. New lint checks turned previously clean code into 33 errors.

### Description

- Reimplements `materialIcon` and `materialPath` locally in
`MaterialIcon.kt`, so the 7 icon files in `revenuecatui` keep their path
DSL bodies and only lose an import. `revenuecatui` has no reference to
`androidx.compose.material.icons` left.
- Declares `material-icons-core` on the 6 sample and test apps that use
`Icons.*` for their own chrome, mostly back arrows and toolbar
affordances. No version: the BOM still pins it at 1.7.8, its last
published version, so it will not move again.
- Updates one test fixture. The BOM pulls kotlinx-serialization to 1.7.3
through `androidx.savedstate`, and 1.7.3 pretty-prints empty objects as
`{}` instead of `{\n}`, which changes `WebViewContextSnapshotTest`.
Production does not pretty-print, so the wire payload is unchanged.
#4258 carries the same fixture change, since it reaches 1.7.3 by a
different route.
- Adds `ui/revenuecatui/lint-baseline.xml`. Lint is clean on `11.0-dev`
without this PR, so all 33 errors are new checks rather than
pre-existing debt.

Split into commits so the two icon problems read separately from the
bump itself.

The same 1.7.3 jump breaks paywall image localization parsing. That is
already broken on `11.0-dev` without this PR, so it is fixed separately
in #4258 rather than here.

</details>






<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Wide Compose stack upgrade can affect paywall UI and serialization
behavior; consumer apps must add `material-icons-core` if they relied on
transitive icons from RevenueCatUI.
> 
> **Overview**
> Bumps the **Compose BOM** from `2024.09.00` to **`2026.06.01`**
(Compose 1.11.x) across the repo via `gradle/libs.versions.toml`.
> 
> **Material icons fallout:** Compose no longer pulls in
`material-icons` transitively, so the PR adds a
**`compose-material-icons-core`** catalog entry and declares it on
sample/e2e apps that use `Icons.*` in their own UI. **`revenuecatui`**
drops `androidx.compose.material.icons` entirely by adding local
**`MaterialIcon.kt`** helpers (`materialIcon` / `materialPath`) so
existing hand-drawn icon vectors keep the same path DSL with only import
changes.
> 
> **Lint:** `ui/revenuecatui` wires **`lint-baseline.xml`** and checks
in a large baseline so new Compose 1.11 lint rules do not fail the
module (existing patterns like WebView APIs, `Configuration.screen*Dp`,
preview ViewModels, etc. are grandfathered—not fixed in this PR).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2c2b626. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Raises the kotlinx-coroutines pin from 1.6.4 to 1.9.0, so the SDK is
tested against the version its users actually run.

- Raises the floor for apps that use `purchases` alone (without ui),
from 1.6.4 to 1.9.0. That is the one consumer-facing consequence, and
the reason this belongs in a major.
- Apps including RevenueCatUI resolve 1.7.3 today, and 1.9.0 once the
Compose BOM moves in #4253. For them this changes nothing, it only makes
the catalog say so.
- Same class of problem as #4258, found the same way, but this one
doesn't have a bug, just putting it in the correct version to prevent
one in the future.

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

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

### Motivation

`#4258` fixed a paywall parsing break caused by a dependency the catalog
claimed to pin at one version while consumers resolved another. Auditing
the rest of the graph for the same shape turned up coroutines as the
largest remaining case: the catalog said 1.6.4, a two-year-old version,
while real apps ran something newer.

Resolved `debugRuntimeClasspath` on `11.0-dev`:

| consumer | kotlinx-coroutines |
| :--- | :--- |
| `test-apps/testpurchasesandroidcompatibility`, purchases only | 1.6.4
|
| `examples/paywall-tester`, includes RevenueCatUI | 1.7.3 |
| the same app with the Compose BOM of #4253 | 1.9.0 |

Unlike #4258 the catalog was not lying to every consumer: a
purchases-only app really does get 1.6.4. The gap is that the SDK's unit
tests have never run against what the majority of apps resolve, which is
exactly the blind spot that hid the #4258 break until the pin was
corrected.

### Description

- One line in `gradle/libs.versions.toml`. `coroutines-core`,
`coroutines-android` and `coroutines-test` all share the version ref, so
the test dependency moves with the runtime one.
- Verified by running `purchases:testDefaultsDebugUnitTest`,
`purchases:testCustomEntitlementComputationDebugUnitTest`,
`ui:revenuecatui:testDefaultsDebugUnitTest`, `detektAll`, `lint` and
`testClasses` against 1.9.0 on an otherwise unmodified `11.0-dev`.

Independent of #4253 and #4258. All three branch from `11.0-dev` and
touch different lines; they can land in any order.

</details>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Raising the pinned coroutines version affects runtime and test
dependencies across `purchases` and raises the minimum for
purchases-only apps, though the change is limited to one catalog entry
and was validated with the usual test/lint tasks.
> 
> **Overview**
> Aligns the Gradle version catalog with the coroutines version many
apps already resolve, and moves SDK unit tests onto that same line.
> 
> The **`coroutines`** pin in `gradle/libs.versions.toml` goes from
**1.6.4 → 1.9.0**, which also updates **`coroutines-core`**,
**`coroutines-android`**, and **`coroutines-test`** because they share
that ref. For integrators that depend on **`purchases` only** (no
RevenueCat UI), the effective minimum coroutines version the SDK pulls
in rises to 1.9.0—a consumer-facing floor change called out for a major
release. Apps that already get a newer coroutines transitively via
UI/Compose should mostly see the catalog catching up, not a behavior
change.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9c498ca. 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.

2 participants