Enable R8 and add release, store and F-Droid infrastructure
Turning on R8 immediately surfaced a latent runtime bug: the ffmpeg-kit-next wrapper references com.arthenica.smartexception.java.Exceptions from AbstractSession.fail() in eighteen places, but a local .aar carries no transitive dependencies, so nothing was pulling it in. Debug builds tolerate this through lazy class loading -- the class is only touched on an error path -- so it would have shipped as a crash the first time an FFmpeg conversion failed. Declared explicitly now. Keep rules cover the JNI boundary. The native library resolves classes and methods by name, which R8 cannot see, so without them the FFmpeg calls fail with NoSuchMethodError in release builds only. Workers are kept too, since WorkManager reconstructs them reflectively from a class name persisted in its database, and a rename breaks jobs enqueued before the update. Verified on the produced artifacts rather than assumed: all 22 native libraries survive minification and every one is still 16 KB aligned inside the APK. Release is 82 MB against 115 MB for debug; the AAB is 40 MB and Play splits it per ABI. The privacy policy lists every permission, including the three WorkManager adds automatically (WAKE_LOCK, RECEIVE_BOOT_COMPLETED, ACCESS_NETWORK_STATE). Checking the merged manifest showed those, and a policy that omitted them would look dishonest to anyone who inspected the app. INTERNET is genuinely absent, so "files stay on the device" is enforced by the OS rather than a promise. CI runs unit tests on every push and builds the FFmpeg AAR only for release tags, since that is a full cross-compile. Releases attach the FFmpeg corresponding source next to the APK: GPL-3.0 requires it, and FFmpeg's instruction to host it "on the same webserver" cannot be satisfied by a Play listing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,8 +35,10 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
optimization {
|
||||
// Left off until the JNI keep rules land with FFmpeg (see plan, Phase 6).
|
||||
enable = false
|
||||
// R8 full mode. Keep rules for the JNI boundary live in
|
||||
// src/main/keepRules/rules.keep -- without them the native FFmpeg
|
||||
// calls break at runtime in release builds only.
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +80,9 @@ dependencies {
|
||||
// archived and delisted, and ffmpeg-kit-next is source-only by design.
|
||||
// The AAR is gitignored; see app/libs/README.md to produce it.
|
||||
implementation(files("libs/ffmpeg-kit-next-8.1.1.aar"))
|
||||
// A local .aar carries no transitive dependencies, so the wrapper's own runtime
|
||||
// dependency has to be declared here explicitly.
|
||||
implementation(libs.smart.exception.java)
|
||||
|
||||
// Durable job queue. WorkManager survives process death, which is what makes the
|
||||
// queue resumable after the foreground-service timeout fires.
|
||||
|
||||
@@ -1,12 +1,39 @@
|
||||
# Add project specific R8 rules here.
|
||||
# AGP will combine all keep rule files in src/main/keepRules to pass to R8
|
||||
#
|
||||
# For more details, see
|
||||
# https://d.android.com/r/tools/r8/keep-rules
|
||||
# R8 keep rules.
|
||||
# AGP combines every file under src/<variant>/keepRules and passes them to R8.
|
||||
|
||||
# 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 *;
|
||||
#}
|
||||
# --- FFmpegKit JNI boundary -------------------------------------------------
|
||||
# The native library looks these classes and members up by name through JNI.
|
||||
# R8 cannot see those references, so without explicit keeps it will rename or
|
||||
# remove them and the native calls fail at runtime with NoSuchMethodError --
|
||||
# only in release builds, and only once a conversion is actually attempted.
|
||||
-keep class com.arthenica.ffmpegkit.** { *; }
|
||||
-keep class com.arthenica.smartexception.** { *; }
|
||||
|
||||
# Callback types the native layer instantiates and invokes.
|
||||
-keep interface com.arthenica.ffmpegkit.FFmpegSessionCompleteCallback { *; }
|
||||
-keep interface com.arthenica.ffmpegkit.FFprobeSessionCompleteCallback { *; }
|
||||
-keep interface com.arthenica.ffmpegkit.MediaInformationSessionCompleteCallback { *; }
|
||||
-keep interface com.arthenica.ffmpegkit.LogCallback { *; }
|
||||
-keep interface com.arthenica.ffmpegkit.StatisticsCallback { *; }
|
||||
|
||||
# Any method the native side calls back into.
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# --- WorkManager ------------------------------------------------------------
|
||||
# Workers are constructed reflectively from a class name stored in the WorkManager
|
||||
# database, so a renamed worker breaks jobs that were enqueued before the update.
|
||||
-keep class * extends androidx.work.ListenableWorker {
|
||||
public <init>(android.content.Context, androidx.work.WorkerParameters);
|
||||
}
|
||||
|
||||
# --- Media3 -----------------------------------------------------------------
|
||||
# Transformer selects codecs and muxers reflectively in places.
|
||||
-keep class androidx.media3.** { *; }
|
||||
-dontwarn androidx.media3.**
|
||||
|
||||
# Keep the source file and line numbers so release crash reports stay readable,
|
||||
# then hide the original file name.
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
Reference in New Issue
Block a user