From 9ac02cf851c4467e1bc897e40f08f570aaf7df7f Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 15 Apr 2026 12:45:27 -0500 Subject: [PATCH] fix(app): disable R8 optimization to fix Compose animation freeze (#5150) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/proguard-rules.pro | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 8340fdd10..d443e173a 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,18 +1,31 @@ # ============================================================================ # Meshtastic Android — ProGuard / R8 rules for release minification # ============================================================================ -# Open-source project: obfuscation is disabled. We rely on tree-shaking and -# code optimization for APK size reduction. +# Open-source project: obfuscation and optimization are disabled. We rely on +# tree-shaking (unused code removal) for APK size reduction. # ============================================================================ # ---- General ---------------------------------------------------------------- -# Preserve line numbers for meaningful crash stack traces +# Preserve line numbers for meaningful crash traces -keepattributes SourceFile,LineNumberTable # Open-source — no need to obfuscate -dontobfuscate +# Disable R8 optimization passes. Tree-shaking (unused code removal) still +# runs — only method-body rewrites and call-site transformations are suppressed. +# +# Why: CMP 1.11 ships consumer rules with -assumenosideeffects on +# Composer.() and ComposerImpl.(), plus -assumevalues on +# ComposeRuntimeFlags and ComposeStackTraceMode. These optimization directives +# let R8 rewrite *call sites* (class-init triggers, flag reads) even when the +# target classes are preserved by -keep rules. The result is that the Compose +# recomposer/frame-clock/animation state machines silently freeze on their +# first frame in release builds. -dontoptimize is the only directive that +# disables processing of -assumenosideeffects/-assumevalues. See #5146. +-dontoptimize + # Dump the full merged R8 configuration (app rules + all library consumer rules) # for auditing. Inspect this file after a release build to see what libraries inject. -printconfiguration build/outputs/mapping/r8-merged-config.txt @@ -41,19 +54,16 @@ # ---- Compose Runtime & Animation -------------------------------------------- -# R8's optimization passes (bundled with AGP 9.x) can inline and dead-code- -# eliminate parts of the Compose frame-clock / recomposer / animation state -# machines, causing every animation to silently freeze on its first frame in -# release builds — indeterminate progress spinners, crossfade transitions, -# animateFloatAsState, AnimatedVisibility, etc. -# -# The frame clock lives in compose.runtime, the draw loop in compose.ui, -# and the animation drivers in compose.animation.core. Keep all three so -# R8 does not break the chain. +# Defence-in-depth: prevent R8 tree-shaking of Compose infrastructure classes +# that are referenced indirectly through compiler-generated state machines. +# With -dontoptimize above these are largely redundant, but they provide a +# safety net against future toolchain changes. -keep class androidx.compose.runtime.** { *; } -keep class androidx.compose.ui.** { *; } -keep class androidx.compose.animation.core.** { *; } -keep class androidx.compose.animation.** { *; } +-keep class androidx.compose.foundation.** { *; } +-keep class androidx.compose.material3.** { *; } # ---- Compose Multiplatform --------------------------------------------------