mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
Play Console reports Optimization 13% / Obfuscation 0%, both under the 25%
threshold that restricts store visibility and publishing.
The cause was three blanket rules, present in both amethyst/proguard-rules.pro
and quartz/consumer-rules.pro (the latter merged into every consuming app's R8
config, this one included):
-dontobfuscate
-keepnames class ** { *; }
-keep class com.vitorpamplona.{quartz,amethyst}.** { *; }
-dontobfuscate turns renaming off program-wide, and -keepnames is shorthand for
-keep,allowshrinking: it permits shrinking but neither renaming nor
optimization. Applied to `**` it disabled R8 for the whole program, third-party
libraries included — hence 0% and 13%.
Replaced with keeps scoped to what is actually reached by name at runtime:
- ArtiLogCallback — libarti_android.so does GetMethodID("onLogLine") on it.
(ArtiNative itself is already covered by the default `native <methods>`
rule, which pins the class of a JNI symbol.)
- Jackson's reflective data binding only: nip47WalletConnect.rpc.** and
experimental.clink.**, plus the three on-disk JSON stores
(ScheduledPost/ScheduledPostFile, PowJobsFile/PersistedPoWJob,
ResourceUsageStore$UsageFile). Event, Filter, Message, Command, Rumor,
EventTemplate, TagArray and the NIP-46/NIP-55 messages all go through
hand-written StdSerializer/StdDeserializer pairs that use string literals,
so they need no keep.
- Enum constant FIELD names, kept blanket: the preference stores persist
`enum.name` into DataStore and read it back with valueOf(), so renamed
constants would reset every user's theme/font/Tor/connectivity setting on
upgrade. Only the field names are pinned; the enum classes are still
renamed and their methods still optimized.
- AmethystCastOptionsProvider — AGP generates keeps from manifest component
android:name attributes but not from <meta-data android:value>.
- ListenableWorker subclasses — WorkManager stores the class name in its own
database and instantiates it by name after an update.
- androidx.appfunctions declarations (pre-stable library).
Dropped -keepparameternames and the LocalVariableTable/LocalVariableTypeTable/
MethodParameters attributes: debug metadata nothing reads
(jackson-module-kotlin takes parameter names from @kotlin.Metadata). Added
-renamesourcefileattribute so the retained SourceFile cannot be read back as
the original class name.
Also removed quartz/proguard-rules.pro, commons/proguard-rules.pro and
commonsUI/proguard-rules.pro: no build script references them, and each was a
stale copy of the blanket keeps being removed here.
Verified on the playRelease DEX (:amethyst:minifyPlayReleaseWithR8 then
packagePlayRelease, both green, no R8 warnings):
24,994 classes -> 96.0% renamed (996 kept)
102,555 field refs -> 86.3% renamed
org.webrtc's 376 kept classes come from stream-webrtc-android's own consumer
rules, not from us.
Every one of the 41 kept first-party classes is accounted for: manifest
components, the two Arti JNI types, the JSON file DTOs and the Cast provider.
Enum constants (PENDING, SYSTEM, PURPLE, ALWAYS, ...) confirmed present in the
DEX field table.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEoxktEZyTrAS33vVZBiwm
47 lines
2.2 KiB
Prolog
47 lines
2.2 KiB
Prolog
# =============================================================================
|
|
# Keep rules Quartz contributes to every app that consumes it (wired through
|
|
# `optimization.consumerKeepRules` in build.gradle.kts, so these end up merged
|
|
# into the consumer's own R8 configuration).
|
|
#
|
|
# Scope them tightly. A rule here applies to the CONSUMER's whole program, so
|
|
# the `-keepnames class ** { *; }` that used to sit in this file pinned every
|
|
# name in every app that depends on Quartz — ours included — and blocked R8
|
|
# from optimizing any member anywhere (`-keepnames` is `-keep,allowshrinking`).
|
|
# Only list what breaks at runtime if the name changes.
|
|
# =============================================================================
|
|
|
|
-keepdirectories libs
|
|
|
|
# secp256k1's JNI layer resolves these from native code.
|
|
-keep class fr.acinq.secp256k1.** { *; }
|
|
|
|
# libscrypt
|
|
-keep class com.lambdaworks.codec.** { *; }
|
|
-keep class com.lambdaworks.crypto.** { *; }
|
|
-keep class com.lambdaworks.jni.** { *; }
|
|
|
|
# Jackson data binding, reflective paths only.
|
|
#
|
|
# Nearly all of Quartz's wire format is handled by the hand-written
|
|
# StdSerializer/StdDeserializer pairs registered on JacksonMapper (Event,
|
|
# Filter, Message, Command, Rumor, EventTemplate, TagArray, the NIP-46 Bunker
|
|
# messages) and JsonMapperNip55 (IntentResult, Permission). Those read and
|
|
# write property names as string literals, so their fields are free to be
|
|
# renamed.
|
|
#
|
|
# These two trees are not: they are data-bound reflectively, via
|
|
# `treeToValue(...)` and `OptimizedJsonMapper.fromJsonTo<T>()`, which derive the
|
|
# JSON property names from the Kotlin constructor parameter names.
|
|
-keep class com.vitorpamplona.quartz.nip47WalletConnect.rpc.** { *; }
|
|
-keep class com.vitorpamplona.quartz.experimental.clink.** { *; }
|
|
|
|
# Quartz serialises enums by name (Jackson writes/reads Enum.name, and
|
|
# Enum.valueOf resolves that string against the static field name), so the
|
|
# constants of its own enums have to keep their names. Consumers that persist
|
|
# their OWN enums by name need the equivalent rule in their own configuration.
|
|
-keepclassmembers enum com.vitorpamplona.quartz.** {
|
|
<fields>;
|
|
public static **[] values();
|
|
public static ** valueOf(java.lang.String);
|
|
}
|