fix(app): add R8 keep rules for Compose animation/runtime/ui#5146
Merged
Conversation
R8 (AGP 9.x) aggressively optimizes Compose animation internals, causing all animations to silently freeze on their first frame in release builds — indeterminate progress spinners, crossfade transitions, AnimatedVisibility, animateFloatAsState, etc. The bundled animation-core consumer rules only protect throw*Exception methods; no keep rules exist for the actual animation classes, frame clock, or recomposer. Add explicit keeps for compose.runtime, compose.ui, compose.animation.core, and compose.animation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jamesarich
added a commit
that referenced
this pull request
Apr 15, 2026
CMP 1.11 consumer rules ship -assumenosideeffects on Composer.<clinit>() and ComposerImpl.<clinit>(), plus -assumevalues on ComposeRuntimeFlags and ComposeStackTraceMode. These optimization directives let R8 rewrite call sites even when target classes are preserved by -keep rules — the result is that the recomposer/frame-clock/animation state machines silently freeze on their first frame in release builds. -keep rules (added in #5146) only prevent shrinking and obfuscation; they do NOT cancel -assumenosideeffects/-assumevalues processing. -dontoptimize is the only directive that disables these optimization passes while still allowing tree-shaking for APK size reduction. Also adds foundation and material3 to the keep rules as defence-in-depth against tree-shaking of Compose classes referenced through compiler- generated state machines. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jamesarich
pushed a commit
that referenced
this pull request
Apr 17, 2026
Removes explicit -keep/-dontwarn wildcards whose behavior is already
provided by the library's own bundled consumer-rules.pro, by Room's
generated static references, or by narrower annotation-targeted keeps.
The merged R8 configuration shrinks by 34 lines (1981→1947 googleRelease,
1653→1619 fdroidRelease), giving R8 more tree-shaking freedom.
Removed (covered by library-bundled consumer rules):
- kotlin.Metadata, kotlin.reflect.**, kotlin.coroutines.Continuation,
kotlinx.coroutines.** (kotlin-stdlib + kotlinx-coroutines-core)
- androidx.datastore.**, androidx.paging.**, androidx.lifecycle.**,
androidx.navigation3.**, androidx.sqlite.**
- coil3.**, okio.**, co.touchlab.kermit.**,
com.mikepenz.aboutlibraries.**, com.mikepenz.markdown.**,
com.juul.kable.**, io.ktor.**, io.ktor.client.engine.java.**
(HttpClientEngineFactory ServiceLoader keep retained)
Room (room3) narrowed:
- Dropped org.meshtastic.core.database.{dao,entity,Converters}.** and
**_Impl wildcards. RoomDatabaseConstructor +
MeshtasticDatabaseConstructor/MeshtasticDatabase keeps retained.
Room 3.0 KMP generates static references rather than reflective
lookups, so reachable DAO/entity/_Impl code survives tree-shaking.
Wire protobuf narrowed:
- Dropped com.squareup.wire.**, org.meshtastic.proto.**, meshtastic.**
wildcards and deleted core/proto/consumer-rules.pro (20 per-class keeps).
- Replaced with targeted -keepclassmembers for ADAPTER fields on
Message subclasses and ProtoAdapter member preservation. Verified
no Class.forName lookups into org.meshtastic.proto namespace.
Meshtastic model + DI:
- Dropped org.meshtastic.core.model.** wildcard and deleted
core/model/consumer-rules.pro (DataPacket is @CommonParcelize'd;
no reflective access to core.model).
- Replaced org.meshtastic.**.di.** wildcard with a narrow
@KoinViewModel class annotation keep (the @module, @componentscan,
@single, @factory keeps above already cover the rest of Koin).
Compose resources refinement:
- Narrowed org.meshtastic.core.resources.** to Res + Res$* members
only (sufficient for the fdroid startup-crash workaround in #5146).
Intentionally retained (documented workarounds / policy):
- -dontoptimize, -dontobfuscate, -printconfiguration
- All Koin annotation keeps + org.koin.**
- kotlinx-serialization @serializable keeps
- Room RoomDatabaseConstructor + MeshtasticDatabase(-Constructor)
- Ktor HttpClientEngineFactory ServiceLoader keep
- Desktop MainKt + org.meshtastic.desktop.** entry points
- All -dontwarn rules for JVM/Android platform gaps
- app/proguard-rules.pro Compose runtime/ui/animation/foundation/
material3 keeps (defence-in-depth with -dontoptimize; may interact
with the parallel CMP freeze RCA investigation).
Verified:
- ./gradlew :app:assembleFdroidRelease — SUCCESS (after each batch)
- ./gradlew :app:assembleGoogleRelease — SUCCESS
- ./gradlew spotlessApply detekt — SUCCESS
- :desktop:createReleaseDistributable — pre-existing failure on
origin/main (Vico/Skia warnings), unrelated to these changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jamesarich
pushed a commit
that referenced
this pull request
Apr 17, 2026
Removes explicit -keep/-dontwarn wildcards whose behavior is already
provided by the library's own bundled consumer-rules.pro, by Room's
generated static references, or by narrower annotation-targeted keeps.
The merged R8 configuration shrinks by 34 lines (1981→1947 googleRelease,
1653→1619 fdroidRelease), giving R8 more tree-shaking freedom.
Removed (covered by library-bundled consumer rules):
- kotlin.Metadata, kotlin.reflect.**, kotlin.coroutines.Continuation,
kotlinx.coroutines.** (kotlin-stdlib + kotlinx-coroutines-core)
- androidx.datastore.**, androidx.paging.**, androidx.lifecycle.**,
androidx.navigation3.**, androidx.sqlite.**
- coil3.**, okio.**, co.touchlab.kermit.**,
com.mikepenz.aboutlibraries.**, com.mikepenz.markdown.**,
com.juul.kable.**, io.ktor.**, io.ktor.client.engine.java.**
(HttpClientEngineFactory ServiceLoader keep retained)
Room (room3) narrowed:
- Dropped org.meshtastic.core.database.{dao,entity,Converters}.** and
**_Impl wildcards. RoomDatabaseConstructor +
MeshtasticDatabaseConstructor/MeshtasticDatabase keeps retained.
Room 3.0 KMP generates static references rather than reflective
lookups, so reachable DAO/entity/_Impl code survives tree-shaking.
Wire protobuf narrowed:
- Dropped com.squareup.wire.**, org.meshtastic.proto.**, meshtastic.**
wildcards and deleted core/proto/consumer-rules.pro (20 per-class keeps).
- Replaced with targeted -keepclassmembers for ADAPTER fields on
Message subclasses and ProtoAdapter member preservation. Verified
no Class.forName lookups into org.meshtastic.proto namespace.
Meshtastic model + DI:
- Dropped org.meshtastic.core.model.** wildcard and deleted
core/model/consumer-rules.pro (DataPacket is @CommonParcelize'd;
no reflective access to core.model).
- Replaced org.meshtastic.**.di.** wildcard with a narrow
@KoinViewModel class annotation keep (the @module, @componentscan,
@single, @factory keeps above already cover the rest of Koin).
Compose resources refinement:
- Narrowed org.meshtastic.core.resources.** to Res + Res$* members
only (sufficient for the fdroid startup-crash workaround in #5146).
Intentionally retained (documented workarounds / policy):
- -dontoptimize, -dontobfuscate, -printconfiguration
- All Koin annotation keeps + org.koin.**
- kotlinx-serialization @serializable keeps
- Room RoomDatabaseConstructor + MeshtasticDatabase(-Constructor)
- Ktor HttpClientEngineFactory ServiceLoader keep
- Desktop MainKt + org.meshtastic.desktop.** entry points
- All -dontwarn rules for JVM/Android platform gaps
- app/proguard-rules.pro Compose runtime/ui/animation/foundation/
material3 keeps (defence-in-depth with -dontoptimize; may interact
with the parallel CMP freeze RCA investigation).
Verified:
- ./gradlew :app:assembleFdroidRelease — SUCCESS (after each batch)
- ./gradlew :app:assembleGoogleRelease — SUCCESS
- ./gradlew spotlessApply detekt — SUCCESS
- :desktop:createReleaseDistributable — pre-existing failure on
origin/main (Vico/Skia warnings), unrelated to these changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
R8 (AGP 9.x) aggressively optimizes Compose animation internals, causing all animations to silently freeze on their first frame in release builds — indeterminate progress spinners, crossfade transitions, AnimatedVisibility, animateFloatAsState, etc.
The bundled
animation-coreconsumer rules only protectthrow*Exceptionmethods; no keep rules exist for the actual animation classes, frame clock, or recomposer.Adds explicit keeps for
compose.runtime,compose.ui,compose.animation.core, andcompose.animation.