From 43740d8b2e0088e5132422bcd9cabdbb08ffef96 Mon Sep 17 00:00:00 2001 From: darken Date: Sat, 5 Sep 2026 20:40:56 +0200 Subject: [PATCH] chore(build): Enable R8 obfuscation for the Google Play flavor Google Play scores the obfuscation share of uploaded bundles and flags listings below its threshold ("App optimisation is below our threshold", fix by Feb 2027). Minify and shrink were already on; the shared -dontobfuscate was the only thing keeping the score at 0%. -dontobfuscate moves to a FOSS-only rule file; the Play rule file keeps SourceFile/LineNumberTable for retracing and pins names only where they reach users or logs: AapSetting/AapCommand subclasses (session logs), ViewModel1 subclasses (log tag), Throwables (error dialog label). All reflective targets (BuildConfig, ArtMirror, NeverCall, InvokeStub, AncModeActionCallback) already carry @Keep or explicit keeps and are identity-mapped in the gplayRelease mapping. The release workflow archives the mapping next to the Play upload. --- .github/workflows/release-tag.yml | 13 ++++++++++++- app/build.gradle.kts | 2 ++ app/proguard-rules-foss.pro | 3 +++ app/proguard-rules-gplay.pro | 21 +++++++++++++++++++++ app/proguard/proguard-rules.pro | 1 - 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 app/proguard-rules-foss.pro create mode 100644 app/proguard-rules-gplay.pro diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 812c338c..340ae0d4 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -193,4 +193,15 @@ jobs: env: STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }} KEY_ALIAS: ${{ secrets.KEY_ALIAS }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} \ No newline at end of file + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + + # The Play flavor is obfuscated; the bundle embeds this mapping, but keep it reachable + # for retracing user-submitted logs without going through Play Console. + - name: Archive R8 mapping + if: always() && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1 + with: + name: r8-mapping-${{ github.ref_name }} + path: app/build/outputs/mapping/gplay*/mapping.txt + if-no-files-found: warn + overwrite: true diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 39303505..026d4c61 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -55,10 +55,12 @@ android { includeInApk = false includeInBundle = false } + proguardFiles("proguard-rules-foss.pro") } create("gplay") { dimension = "version" signingConfig = signingConfigs["releaseGplay"] + proguardFiles("proguard-rules-gplay.pro") } } diff --git a/app/proguard-rules-foss.pro b/app/proguard-rules-foss.pro new file mode 100644 index 00000000..96e2685d --- /dev/null +++ b/app/proguard-rules-foss.pro @@ -0,0 +1,3 @@ +# The FOSS build is open source and ships on GitHub/F-Droid; readable stack traces are worth more +# than smaller identifiers there. Only the Google Play flavor obfuscates (proguard-rules-gplay.pro). +-dontobfuscate diff --git a/app/proguard-rules-gplay.pro b/app/proguard-rules-gplay.pro new file mode 100644 index 00000000..ed741ac3 --- /dev/null +++ b/app/proguard-rules-gplay.pro @@ -0,0 +1,21 @@ +# Google Play scores the obfuscation share of every uploaded bundle and restricts listings that +# stay below its threshold, so the Play flavor obfuscates. The FOSS flavor does not +# (proguard-rules-foss.pro). + +# Keep stack traces retraceable with the mapping file that the bundle embeds. +-keepattributes SourceFile,LineNumberTable +-renamesourcefileattribute SourceFile + +# AAP session logs identify settings and commands by their simple class name +# (AapSessionEngine, AapAncController, AapOutboundController). Names only; unused classes are +# still removed. +-keepnames class eu.darken.capod.pods.core.apple.aap.protocol.AapSetting +-keepnames class * extends eu.darken.capod.pods.core.apple.aap.protocol.AapSetting +-keepnames class eu.darken.capod.pods.core.apple.aap.protocol.AapCommand +-keepnames class * extends eu.darken.capod.pods.core.apple.aap.protocol.AapCommand + +# ViewModel1 derives its log tag from the subclass name (VM:OverviewViewModel). +-keepnames class * extends eu.darken.capod.common.uix.ViewModel1 + +# Error dialogs and log summaries show exceptions by class name (LocalizedError, asLogSummary). +-keepnames class * extends java.lang.Throwable diff --git a/app/proguard/proguard-rules.pro b/app/proguard/proguard-rules.pro index 5a768afb..79926940 100644 --- a/app/proguard/proguard-rules.pro +++ b/app/proguard/proguard-rules.pro @@ -1,5 +1,4 @@ -keep class eu.darken.capod.BuildConfig { *; } --dontobfuscate # work-runtime 2.7.1 (pulled by Glance) uses Class.newInstance() reflection throughout. # R8 full mode strips no-arg constructors not reachable by static analysis.