mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
fix: scope R8 keep rules so Play's DEX optimization checks pass
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
This commit is contained in:
@@ -11,230 +11,108 @@ Proguard configuration for optimizing and obfuscating Android APK while preservi
|
|||||||
|
|
||||||
## Amethyst Proguard Configuration
|
## Amethyst Proguard Configuration
|
||||||
|
|
||||||
**File:** `amethyst/proguard-rules.pro`
|
**Files:**
|
||||||
|
|
||||||
### Keep Kotlin Metadata
|
- `amethyst/proguard-rules.pro` — the app's rules. Read it before adding
|
||||||
|
anything: it is commented rule by rule.
|
||||||
|
- `quartz/consumer-rules.pro` — merged into the R8 configuration of **every**
|
||||||
|
app that depends on Quartz, this one included (wired via
|
||||||
|
`optimization.consumerKeepRules` in `quartz/build.gradle.kts`). A rule added
|
||||||
|
here silently applies to somebody else's whole program.
|
||||||
|
- The AGP default `proguard-android-optimize.txt`, which already contributes the
|
||||||
|
Android-wide basics (Parcelable CREATOR fields, `native <methods>`, the enum
|
||||||
|
`values()`/`valueOf()` pair, …). Don't restate its rules.
|
||||||
|
- `commons/`, `commonsUI/` and the other library modules deliberately have **no**
|
||||||
|
rules files. They are not minified and they wire no consumer rules, so a file
|
||||||
|
there would be dead configuration.
|
||||||
|
|
||||||
|
### The policy: keep only what is reached BY NAME
|
||||||
|
|
||||||
|
Google Play measures how much of a shipped app's DEX R8 actually optimized and
|
||||||
|
renamed, and warns — then restricts store visibility and publishing — below 25%
|
||||||
|
in either category. Amethyst has been on the wrong side of that line: a
|
||||||
|
`-dontobfuscate` plus `-keepnames class ** { *; }` at the top of both
|
||||||
|
`proguard-rules.pro` and `quartz/consumer-rules.pro`, and outright
|
||||||
|
`-keep class com.vitorpamplona.** { *; }` for the app's own code, produced 0%
|
||||||
|
obfuscation and 13% optimization. (`-keepnames` is not the mild rule it looks
|
||||||
|
like: it expands to `-keep,allowshrinking`, which permits shrinking but neither
|
||||||
|
renaming nor optimization — applied to `**` it disables R8 for the entire
|
||||||
|
program, libraries included.)
|
||||||
|
|
||||||
|
So the standing rule is: **a keep needs a named runtime mechanism that reads the
|
||||||
|
name.** In this app those are, exhaustively:
|
||||||
|
|
||||||
|
| Mechanism | Example | Rule shape |
|
||||||
|
|---|---|---|
|
||||||
|
| JNI symbol `Java_<class>_<method>` | `ArtiNative`, secp256k1 | covered by the default `native <methods>` rule |
|
||||||
|
| Native code calling *back* by name | `ArtiLogCallback.onLogLine`, looked up with `GetMethodID` in `tools/arti-build/src/lib.rs` | `-keep class …ArtiLogCallback { *; }` |
|
||||||
|
| JNA struct/callback mapping | lazysodium | `-keep class com.goterl.lazysodium.** { *; }` |
|
||||||
|
| Jackson **reflective** data binding | `nip47WalletConnect.rpc.**`, `experimental.clink.**` | `-keep class <pkg>.** { *; }` |
|
||||||
|
| Enum constant persisted as a string | `UISharedPreferences` writes `enum.name`, reads `Type.valueOf(s)` | `-keepclassmembers enum * { <fields>; … }` |
|
||||||
|
| Class name in a manifest `<meta-data android:value>` | `AmethystCastOptionsProvider` | explicit `-keep` — AGP generates keeps from component `android:name`, **not** from meta-data |
|
||||||
|
| Class name in WorkManager's database | the three `CoroutineWorker`s | `-keep class * extends androidx.work.ListenableWorker { <init>(...); }` |
|
||||||
|
|
||||||
|
What does **not** need a keep, and where the temptation usually comes from:
|
||||||
|
|
||||||
|
- **Quartz events and tags.** `Event`, `Filter`, `Message`, `Command`, `Rumor`,
|
||||||
|
`EventTemplate`, `TagArray`, the NIP-46 Bunker messages and the NIP-55 intent
|
||||||
|
results all go through hand-written `StdSerializer`/`StdDeserializer` pairs
|
||||||
|
registered on `JacksonMapper` / `JsonMapperNip55`. Those read and write
|
||||||
|
property names as string literals, and `EventFactory` dispatches on kind with
|
||||||
|
a `when`, not by reflection. Renaming their fields changes nothing on the wire.
|
||||||
|
- **`@Serializable` (kotlinx) classes**, including the type-safe navigation
|
||||||
|
routes. The compiler plugin generates a descriptor holding the serial name and
|
||||||
|
every property name as **compile-time string literals**, so obfuscation cannot
|
||||||
|
reach them. kotlinx-serialization ships its own consumer rules for the
|
||||||
|
`$$serializer`/`Companion` plumbing.
|
||||||
|
- **Compose, Coil, OkHttp, Media3, Firebase, kotlin-reflect.** Every one of them
|
||||||
|
ships consumer rules inside its own artifact. Check
|
||||||
|
`build/outputs/mapping/<variant>/configuration.txt` — the fully merged
|
||||||
|
configuration — before writing a rule for a third-party library.
|
||||||
|
- **Manifest-declared components** (activities, services, receivers, providers)
|
||||||
|
and classes named in layout/`res/xml`. AGP generates those keeps itself, which
|
||||||
|
is why `Intent().setClassName(ctx, "…NappletBrowserService")` is safe.
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
```proguard
|
```proguard
|
||||||
# Kotlin metadata is required for reflection
|
# Retraceable stack traces from the uploaded mapping.txt, without leaking the
|
||||||
-keep class kotlin.Metadata { *; }
|
# class name back through the file name.
|
||||||
-keep class kotlin.** { *; }
|
|
||||||
-dontwarn kotlin.**
|
|
||||||
|
|
||||||
# Kotlin serialization
|
|
||||||
-keepattributes *Annotation*, InnerClasses
|
|
||||||
-dontnote kotlinx.serialization.AnnotationsKt
|
|
||||||
-dontnote kotlinx.serialization.SerializationKt
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses class com.vitorpamplona.**$$serializer { *; }
|
|
||||||
-keepclassmembers class com.vitorpamplona.** {
|
|
||||||
*** Companion;
|
|
||||||
}
|
|
||||||
-keepclasseswithmembers class com.vitorpamplona.** {
|
|
||||||
kotlinx.serialization.KSerializer serializer(...);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Nostr Event Classes
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Nostr events are serialized/deserialized
|
|
||||||
-keep class com.vitorpamplona.quartz.events.** { *; }
|
|
||||||
-keep class com.vitorpamplona.quartz.encoders.** { *; }
|
|
||||||
|
|
||||||
# Keep event builders
|
|
||||||
-keep class com.vitorpamplona.quartz.builders.** { *; }
|
|
||||||
|
|
||||||
# Keep tag classes
|
|
||||||
-keep class com.vitorpamplona.quartz.nip01Core.tags.** { *; }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Data Classes
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Data classes used in ViewModels and serialization
|
|
||||||
-keep @kotlinx.serialization.Serializable class * { *; }
|
|
||||||
|
|
||||||
# Keep all data classes
|
|
||||||
-keep class com.vitorpamplona.amethyst.model.** { *; }
|
|
||||||
-keep class com.vitorpamplona.amethyst.service.model.** { *; }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Compose Classes
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Jetpack Compose
|
|
||||||
-keep class androidx.compose.** { *; }
|
|
||||||
-dontwarn androidx.compose.**
|
|
||||||
|
|
||||||
# Compose runtime
|
|
||||||
-keep class androidx.compose.runtime.** { *; }
|
|
||||||
|
|
||||||
# Compose UI
|
|
||||||
-keep class androidx.compose.ui.** { *; }
|
|
||||||
|
|
||||||
# Material3
|
|
||||||
-keep class androidx.compose.material3.** { *; }
|
|
||||||
|
|
||||||
# Navigation Compose - Keep serializable routes
|
|
||||||
-keep class * implements java.io.Serializable { *; }
|
|
||||||
-keepclassmembers class * implements java.io.Serializable {
|
|
||||||
static final long serialVersionUID;
|
|
||||||
private static final java.io.ObjectStreamField[] serialPersistentFields;
|
|
||||||
!static !transient <fields>;
|
|
||||||
private void writeObject(java.io.ObjectOutputStream);
|
|
||||||
private void readObject(java.io.ObjectInputStream);
|
|
||||||
java.lang.Object writeReplace();
|
|
||||||
java.lang.Object readResolve();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep OkHttp/Retrofit
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# OkHttp
|
|
||||||
-dontwarn okhttp3.**
|
|
||||||
-dontwarn okio.**
|
|
||||||
-keep class okhttp3.** { *; }
|
|
||||||
-keep class okio.** { *; }
|
|
||||||
|
|
||||||
# OkHttp WebSockets (for Nostr relays)
|
|
||||||
-keep class okhttp3.internal.ws.** { *; }
|
|
||||||
|
|
||||||
# Retrofit (if used)
|
|
||||||
-keepattributes Signature
|
|
||||||
-keepattributes Exceptions
|
|
||||||
-keep class retrofit2.** { *; }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Jackson (JSON)
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Jackson JSON library
|
|
||||||
-keep class com.fasterxml.jackson.** { *; }
|
|
||||||
-keep class org.codehaus.** { *; }
|
|
||||||
-keepclassmembers class * {
|
|
||||||
@com.fasterxml.jackson.annotation.* <methods>;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Jackson polymorphic types
|
|
||||||
-keepattributes RuntimeVisibleAnnotations
|
|
||||||
-keep @com.fasterxml.jackson.annotation.JsonTypeInfo class *
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Secp256k1 (Crypto)
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Secp256k1 native library
|
|
||||||
-keep class fr.acinq.secp256k1.** { *; }
|
|
||||||
|
|
||||||
# Keep native methods
|
|
||||||
-keepclasseswithmembernames class * {
|
|
||||||
native <methods>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Tor
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Tor library
|
|
||||||
-keep class com.msopentech.thali.toronionproxy.** { *; }
|
|
||||||
-dontwarn com.msopentech.thali.toronionproxy.**
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep ExoPlayer (Media)
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# ExoPlayer (Media3)
|
|
||||||
-keep class androidx.media3.** { *; }
|
|
||||||
-dontwarn androidx.media3.**
|
|
||||||
|
|
||||||
-keep class com.google.android.exoplayer2.** { *; }
|
|
||||||
-dontwarn com.google.android.exoplayer2.**
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Coil (Image Loading)
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Coil image loading
|
|
||||||
-keep class coil.** { *; }
|
|
||||||
-keep class coil3.** { *; }
|
|
||||||
-dontwarn coil.**
|
|
||||||
-dontwarn coil3.**
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep ViewModels
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# ViewModel classes
|
|
||||||
-keep class * extends androidx.lifecycle.ViewModel {
|
|
||||||
<init>();
|
|
||||||
}
|
|
||||||
|
|
||||||
# ViewModel factories
|
|
||||||
-keep class * extends androidx.lifecycle.ViewModelProvider$Factory {
|
|
||||||
<init>(...);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Keep ViewModel constructors for reflection
|
|
||||||
-keepclassmembers class * extends androidx.lifecycle.ViewModel {
|
|
||||||
<init>(...);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Parcelable
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Parcelable
|
|
||||||
-keep class * implements android.os.Parcelable {
|
|
||||||
public static final android.os.Parcelable$Creator *;
|
|
||||||
}
|
|
||||||
|
|
||||||
-keepclassmembers class * implements android.os.Parcelable {
|
|
||||||
public <fields>;
|
|
||||||
private <fields>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Enums
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Enums
|
|
||||||
-keepclassmembers enum * {
|
|
||||||
public static **[] values();
|
|
||||||
public static ** valueOf(java.lang.String);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove Logging (Production)
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Remove debug logging in release builds
|
|
||||||
-assumenosideeffects class android.util.Log {
|
|
||||||
public static *** d(...);
|
|
||||||
public static *** v(...);
|
|
||||||
public static *** i(...);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Keep error/warning logs
|
|
||||||
-assumenosideeffects class android.util.Log {
|
|
||||||
public static *** e(...) return false;
|
|
||||||
public static *** w(...) return false;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keep Crashlytics/Firebase
|
|
||||||
|
|
||||||
```proguard
|
|
||||||
# Firebase Crashlytics
|
|
||||||
-keepattributes SourceFile,LineNumberTable
|
-keepattributes SourceFile,LineNumberTable
|
||||||
-keep public class * extends java.lang.Exception
|
-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
# Firebase
|
# jackson-module-kotlin reads @kotlin.Metadata; R8 requires InnerClasses and
|
||||||
-keep class com.google.firebase.** { *; }
|
# EnclosingMethod alongside Signature.
|
||||||
-dontwarn com.google.firebase.**
|
-keepattributes *Annotation*,Signature,Exceptions,InnerClasses,EnclosingMethod
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`LocalVariableTable`, `LocalVariableTypeTable`, `MethodParameters` and
|
||||||
|
`-keepparameternames` are debug metadata that nothing in the app reads —
|
||||||
|
jackson-module-kotlin takes parameter names from `@kotlin.Metadata`, not from
|
||||||
|
`MethodParameters`. They were removed; don't add them back.
|
||||||
|
|
||||||
|
### Verifying a change to these rules
|
||||||
|
|
||||||
|
R8 cannot see reflection, so a wrong keep rule fails **only in a release build,
|
||||||
|
at runtime**. Before changing them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :amethyst:assemblePlayRelease -PdisableAbiSplits=true -PdisableUniversalApk=true
|
||||||
|
```
|
||||||
|
|
||||||
|
then read `amethyst/build/outputs/mapping/playRelease/`:
|
||||||
|
|
||||||
|
- `configuration.txt` — every rule R8 actually saw, including each AAR's
|
||||||
|
consumer rules. This is the file that answers "does library X already keep
|
||||||
|
itself?"
|
||||||
|
- `mapping.txt` — what got renamed. Lines whose left and right sides are equal
|
||||||
|
are classes a keep rule pinned; scan them for anything you did not intend.
|
||||||
|
- `seeds.txt` / `usage.txt` — what the keeps matched, and what was removed.
|
||||||
|
|
||||||
|
Exercise NIP-47 wallet connect, NIP-46 bunker login, Tor, scheduled posts and a
|
||||||
|
settings round-trip (change theme/font, kill, relaunch) on the minified build —
|
||||||
|
those are the paths the keeps above exist for.
|
||||||
|
|
||||||
## Build Configuration
|
## Build Configuration
|
||||||
|
|
||||||
### Enable R8 in build.gradle
|
### Enable R8 in build.gradle
|
||||||
@@ -356,13 +234,15 @@ adb install app/build/outputs/apk/release/app-release.apk
|
|||||||
|
|
||||||
### Issue: Compose Navigation Crashes
|
### Issue: Compose Navigation Crashes
|
||||||
|
|
||||||
**Cause:** @Serializable route classes were obfuscated.
|
**Not** the route classes being obfuscated — that cannot happen. A type-safe
|
||||||
|
route's pattern comes from its kotlinx-serialization descriptor, and the compiler
|
||||||
|
plugin bakes the serial name and every property name in as string literals, so
|
||||||
|
renaming the class leaves the route string untouched. Adding
|
||||||
|
`-keep @kotlinx.serialization.Serializable class …routes.** { *; }` pins a large
|
||||||
|
tree for no reason and hides the real cause.
|
||||||
|
|
||||||
**Solution:**
|
Look instead at whether `navigation-common`'s own consumer rules made it into
|
||||||
```proguard
|
`configuration.txt`, and at the stack trace retraced through `mapping.txt`.
|
||||||
# Keep all route classes
|
|
||||||
-keep @kotlinx.serialization.Serializable class com.vitorpamplona.amethyst.ui.navigation.routes.** { *; }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue: Native Library Crashes
|
### Issue: Native Library Crashes
|
||||||
|
|
||||||
|
|||||||
Vendored
+133
-34
@@ -1,38 +1,70 @@
|
|||||||
# Add project specific ProGuard rules here.
|
# =============================================================================
|
||||||
# You can control the set of applied configuration files using the
|
# R8 configuration for the release build.
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
#
|
||||||
# For more details, see
|
# Two things are balanced here.
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
#
|
||||||
|
# 1. Google Play measures how much of the shipped DEX R8 actually optimized
|
||||||
|
# and renamed, and warns (then restricts visibility/publishing) below 25%
|
||||||
|
# in either category. This file used to open with `-dontobfuscate` plus
|
||||||
|
# `-keepnames class ** { *; }`, and then kept all of `com.vitorpamplona.**`
|
||||||
|
# outright. That is the whole app and every library: `-dontobfuscate`
|
||||||
|
# turns renaming off globally, and `-keepnames` is shorthand for
|
||||||
|
# `-keep,allowshrinking`, which permits shrinking but neither renaming nor
|
||||||
|
# optimization. Hence 0% obfuscation / 13% optimization.
|
||||||
|
#
|
||||||
|
# 2. Anything the runtime reaches by NAME rather than by reference has to keep
|
||||||
|
# that name: JNI symbols, Jackson's reflective data binding, enum constants
|
||||||
|
# persisted into DataStore, class names written into a manifest meta-data
|
||||||
|
# value or into WorkManager's database.
|
||||||
|
#
|
||||||
|
# So every keep below is scoped to (2), and R8 gets everything else. mapping.txt
|
||||||
|
# is uploaded with each release, so stack traces stay retraceable.
|
||||||
|
#
|
||||||
|
# When adding a keep, say in a comment WHAT reads the name at runtime. A keep
|
||||||
|
# without that is usually a keep that is not needed.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
# preserve the line number information for debugging stack traces.
|
# -----------------------------------------------------------------------------
|
||||||
-dontobfuscate
|
# Attributes
|
||||||
-keepattributes LocalVariableTable
|
# -----------------------------------------------------------------------------
|
||||||
-keepattributes LocalVariableTypeTable
|
# SourceFile + LineNumberTable are what let Play (and `retrace`) turn an
|
||||||
-keepattributes *Annotation*
|
# obfuscated stack trace back into real line numbers via mapping.txt.
|
||||||
-keepattributes SourceFile
|
# -renamesourcefileattribute replaces the real file name with a constant so the
|
||||||
-keepattributes LineNumberTable
|
# class name cannot simply be read back off it.
|
||||||
-keepattributes Signature
|
-keepattributes SourceFile,LineNumberTable
|
||||||
-keepattributes Exceptions
|
-renamesourcefileattribute SourceFile
|
||||||
-keepattributes InnerClasses
|
|
||||||
-keepattributes EnclosingMethod
|
# Annotations (jackson-module-kotlin reads @kotlin.Metadata; Jackson mixins and
|
||||||
-keepattributes MethodParameters
|
# kotlinx.serialization read their own), generic signatures, and the
|
||||||
-keepparameternames
|
# inner/enclosing-class links that R8 requires alongside Signature.
|
||||||
|
-keepattributes *Annotation*,Signature,Exceptions,InnerClasses,EnclosingMethod
|
||||||
|
|
||||||
|
# LocalVariableTable, LocalVariableTypeTable, MethodParameters and
|
||||||
|
# -keepparameternames used to be kept here as well. They are debug metadata:
|
||||||
|
# nothing in the app reads them (jackson-module-kotlin takes parameter names
|
||||||
|
# from @kotlin.Metadata, not from MethodParameters), and they are pure DEX
|
||||||
|
# weight in a release build.
|
||||||
|
|
||||||
-keepdirectories libs
|
-keepdirectories libs
|
||||||
|
|
||||||
# Keep all names
|
# -----------------------------------------------------------------------------
|
||||||
-keepnames class ** { *; }
|
# JNI — names that live in a .so, not in the DEX
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# proguard-android-optimize.txt already contributes
|
||||||
|
# -keepclasseswithmembernames class * { native <methods>; }
|
||||||
|
# which pins every class that DECLARES a native method (ArtiNative,
|
||||||
|
# secp256k1's loader, …) together with those methods' names, because the
|
||||||
|
# exported symbol is Java_<class>_<method>. What that does NOT cover is the
|
||||||
|
# traffic in the other direction: Java/Kotlin the native side looks up itself.
|
||||||
|
|
||||||
# Keep All enums
|
# libarti_android.so calls back into this interface by name —
|
||||||
-keep enum ** { *; }
|
# tools/arti-build/src/lib.rs does GetMethodID("onLogLine") on it. Renaming the
|
||||||
|
# method silently kills all Tor log output.
|
||||||
|
-keep class com.vitorpamplona.amethyst.ui.tor.ArtiLogCallback { *; }
|
||||||
|
|
||||||
# preserve access to native classses
|
# secp256k1's JNI layer resolves these from native code.
|
||||||
-keep class fr.acinq.secp256k1.** { *; }
|
-keep class fr.acinq.secp256k1.** { *; }
|
||||||
|
|
||||||
# JNA For Libsodium
|
|
||||||
-keep class com.goterl.lazysodium.** { *; }
|
|
||||||
|
|
||||||
# libscrypt
|
# libscrypt
|
||||||
-keep class com.lambdaworks.codec.** { *; }
|
-keep class com.lambdaworks.codec.** { *; }
|
||||||
-keep class com.lambdaworks.crypto.** { *; }
|
-keep class com.lambdaworks.crypto.** { *; }
|
||||||
@@ -40,6 +72,10 @@
|
|||||||
|
|
||||||
-keep class info.guardianproject.** { *; }
|
-keep class info.guardianproject.** { *; }
|
||||||
|
|
||||||
|
# JNA for Libsodium: JNA maps these types onto the C ABI by reflecting over
|
||||||
|
# their fields and method signatures at runtime.
|
||||||
|
-keep class com.goterl.lazysodium.** { *; }
|
||||||
|
|
||||||
# JNA also requires AWT, which Android does not have. So the classes are broken down to filter AWT out
|
# JNA also requires AWT, which Android does not have. So the classes are broken down to filter AWT out
|
||||||
-keep class com.sun.jna.ToNativeConverter { *; }
|
-keep class com.sun.jna.ToNativeConverter { *; }
|
||||||
-keep class com.sun.jna.NativeMapped { *; }
|
-keep class com.sun.jna.NativeMapped { *; }
|
||||||
@@ -59,13 +95,76 @@
|
|||||||
private static java.lang.Object fromNative(com.sun.jna.FromNativeConverter, java.lang.Object, java.lang.reflect.Method);
|
private static java.lang.Object fromNative(com.sun.jna.FromNativeConverter, java.lang.Object, java.lang.reflect.Method);
|
||||||
}
|
}
|
||||||
|
|
||||||
# JSON parsing
|
# -----------------------------------------------------------------------------
|
||||||
-keep class com.vitorpamplona.quartz.** { *; }
|
# Enum constant names
|
||||||
-keep class com.vitorpamplona.amethyst.** { *; }
|
# -----------------------------------------------------------------------------
|
||||||
|
# An enum constant's NAME is persisted data in this app. The preference stores
|
||||||
# Room generates *_Impl subclasses instantiated reflectively via no-arg constructor.
|
# write `enum.name` into DataStore and read it back with `Type.valueOf(string)`
|
||||||
# -keepnames preserves the name but R8 still strips the unused <init>().
|
# (see model/preferences/UISharedPreferences.kt, TorSharedPreferences.kt,
|
||||||
-keep class * extends androidx.room.RoomDatabase {
|
# NamecoinSharedPreferences.kt), and Jackson serialises enums by name too.
|
||||||
<init>();
|
# Enum.valueOf resolves that string against the static FIELD name, so renaming
|
||||||
|
# the constants would reset every user's theme/font/Tor/connectivity setting on
|
||||||
|
# the first launch after an update.
|
||||||
|
#
|
||||||
|
# Deliberately blanket rather than a list of the enums that happen to be
|
||||||
|
# persisted today: the failure mode is silent, release-only, and one new
|
||||||
|
# `preferences[KEY] = value.name` line away. Only the field names are pinned —
|
||||||
|
# the enum classes themselves are still renamed and their methods still
|
||||||
|
# optimized.
|
||||||
|
-keepclassmembers enum * {
|
||||||
|
<fields>;
|
||||||
|
public static **[] values();
|
||||||
|
public static ** valueOf(java.lang.String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Jackson — reflective data binding only
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Most of Quartz's wire format does NOT need a keep. Event, Filter, Message,
|
||||||
|
# Command, Rumor, EventTemplate, TagArray, the NIP-46 Bunker messages and the
|
||||||
|
# NIP-55 intent results all go through hand-written StdSerializer/StdDeserializer
|
||||||
|
# pairs registered on JacksonMapper / JsonMapperNip55, which read and write
|
||||||
|
# property names as string literals. Renaming their fields changes nothing on
|
||||||
|
# the wire.
|
||||||
|
#
|
||||||
|
# What remains is the code Jackson data-binds REFLECTIVELY — `treeToValue(...)`
|
||||||
|
# and `readValue<T>()` with no custom deserializer. There the JSON property
|
||||||
|
# names come from the Kotlin constructor parameter names, so a renamed field is
|
||||||
|
# a changed wire format.
|
||||||
|
|
||||||
|
# NIP-47 Wallet Connect RPC: every *Method / *Params / *SuccessResponse plus
|
||||||
|
# NwcError, NwcTransaction and the NwcMethod/NwcErrorCode enums are reached via
|
||||||
|
# treeToValue() from RequestDeserializer / ResponseDeserializer /
|
||||||
|
# NotificationDeserializer.
|
||||||
|
-keep class com.vitorpamplona.quartz.nip47WalletConnect.rpc.** { *; }
|
||||||
|
|
||||||
|
# CLINK (experimental NIP-XX offers/debits/manage): parsed with
|
||||||
|
# OptimizedJsonMapper.fromJsonTo<OfferRequest>() and friends — reflective.
|
||||||
|
-keep class com.vitorpamplona.quartz.experimental.clink.** { *; }
|
||||||
|
|
||||||
|
# On-disk JSON written and re-read by the app itself. The field names are the
|
||||||
|
# file format, so renaming them makes every existing file unreadable.
|
||||||
|
-keep class com.vitorpamplona.amethyst.commons.scheduledposts.ScheduledPost { *; }
|
||||||
|
-keep class com.vitorpamplona.amethyst.commons.scheduledposts.ScheduledPostFile { *; }
|
||||||
|
-keep class com.vitorpamplona.amethyst.service.pow.PowJobsFile { *; }
|
||||||
|
-keep class com.vitorpamplona.amethyst.commons.service.pow.PersistedPoWJob { *; }
|
||||||
|
-keep class com.vitorpamplona.amethyst.service.resourceusage.ResourceUsageStore$UsageFile { *; }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Names referenced from outside the DEX
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# AGP generates keeps from the merged manifest's component `android:name`
|
||||||
|
# attributes, but NOT from <meta-data android:value>. The Cast framework reads
|
||||||
|
# this one out of the manifest and Class.forName()s it.
|
||||||
|
-keep class com.vitorpamplona.amethyst.service.cast.chromecast.AmethystCastOptionsProvider { *; }
|
||||||
|
|
||||||
|
# WorkManager stores the worker's class name in its own database at enqueue
|
||||||
|
# time and instantiates it by name on a later process start — including after
|
||||||
|
# an app update, when R8 has produced a different mapping.
|
||||||
|
-keep class * extends androidx.work.ListenableWorker { <init>(...); }
|
||||||
|
|
||||||
|
# androidx.appfunctions: the KSP-generated invokers and the app_functions.xml
|
||||||
|
# the system reads are keyed off these declarations. One class plus its
|
||||||
|
# generated neighbours — cheap enough not to be worth proving unnecessary
|
||||||
|
# against a pre-stable (alpha) library.
|
||||||
|
-keep class com.vitorpamplona.amethyst.appfunctions.** { *; }
|
||||||
|
|||||||
Vendored
-24
@@ -1,24 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|
||||||
-keep class com.vitorpamplona.quartz.** { *; }
|
|
||||||
-keep class com.vitorpamplona.amethyst.** { *; }
|
|
||||||
Vendored
-24
@@ -1,24 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|
||||||
-keep class com.vitorpamplona.quartz.** { *; }
|
|
||||||
-keep class com.vitorpamplona.amethyst.** { *; }
|
|
||||||
+35
-14
@@ -1,19 +1,18 @@
|
|||||||
# Add project specific ProGuard rules here.
|
# =============================================================================
|
||||||
# You can control the set of applied configuration files using the
|
# Keep rules Quartz contributes to every app that consumes it (wired through
|
||||||
# proguardFiles setting in build.gradle.
|
# `optimization.consumerKeepRules` in build.gradle.kts, so these end up merged
|
||||||
|
# into the consumer's own R8 configuration).
|
||||||
#
|
#
|
||||||
# For more details, see
|
# Scope them tightly. A rule here applies to the CONSUMER's whole program, so
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
# 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
|
-keepdirectories libs
|
||||||
|
|
||||||
# Keep all names
|
# secp256k1's JNI layer resolves these from native code.
|
||||||
-keepnames class ** { *; }
|
|
||||||
|
|
||||||
# Keep All enums
|
|
||||||
-keep enum ** { *; }
|
|
||||||
|
|
||||||
# preserve access to native classses
|
|
||||||
-keep class fr.acinq.secp256k1.** { *; }
|
-keep class fr.acinq.secp256k1.** { *; }
|
||||||
|
|
||||||
# libscrypt
|
# libscrypt
|
||||||
@@ -21,5 +20,27 @@
|
|||||||
-keep class com.lambdaworks.crypto.** { *; }
|
-keep class com.lambdaworks.crypto.** { *; }
|
||||||
-keep class com.lambdaworks.jni.** { *; }
|
-keep class com.lambdaworks.jni.** { *; }
|
||||||
|
|
||||||
# JSON parsing
|
# Jackson data binding, reflective paths only.
|
||||||
-keep class com.vitorpamplona.quartz.** { *; }
|
#
|
||||||
|
# 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);
|
||||||
|
}
|
||||||
|
|||||||
Vendored
-40
@@ -1,40 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# preserve the line number information for debugging stack traces.
|
|
||||||
-dontobfuscate
|
|
||||||
-keepattributes LocalVariableTable
|
|
||||||
-keepattributes LocalVariableTypeTable
|
|
||||||
-keepattributes *Annotation*
|
|
||||||
-keepattributes SourceFile
|
|
||||||
-keepattributes LineNumberTable
|
|
||||||
-keepattributes Signature
|
|
||||||
-keepattributes Exceptions
|
|
||||||
-keepattributes InnerClasses
|
|
||||||
-keepattributes EnclosingMethod
|
|
||||||
-keepattributes MethodParameters
|
|
||||||
-keepparameternames
|
|
||||||
|
|
||||||
-keepdirectories libs
|
|
||||||
|
|
||||||
# Keep all names
|
|
||||||
-keepnames class ** { *; }
|
|
||||||
|
|
||||||
# Keep All enums
|
|
||||||
-keep enum ** { *; }
|
|
||||||
|
|
||||||
# preserve access to native classses
|
|
||||||
-keep class fr.acinq.secp256k1.** { *; }
|
|
||||||
|
|
||||||
# libscrypt
|
|
||||||
-keep class com.lambdaworks.codec.** { *; }
|
|
||||||
-keep class com.lambdaworks.crypto.** { *; }
|
|
||||||
-keep class com.lambdaworks.jni.** { *; }
|
|
||||||
|
|
||||||
# JSON parsing
|
|
||||||
-keep class com.vitorpamplona.quartz.** { *; }
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user