WorkManager periodic worker hits the repo's index-v2.json, parses the highest versionCode for our package, compares with BuildConfig.VERSION_CODE. When newer: posts a notification with ACTION_VIEW on the APK URL — Android's DownloadManager picks it up and the system installer takes over. No INSTALL_PACKAGES perm needed. Settings: - Check for updates toggle (default on — closing NewPipe's silent staleness gap is the explicit motivation) - Interval picker (1h / 6h / 24h, default 6h — WorkManager has a 15-min periodic floor anyway) - Last-checked timestamp + 'update available' tag when caught-up state is dirty - Check now button — runs the same path as the worker so behaviors stay identical Cold start fires one check too so users see pending updates without waiting a full interval. R8 keep-rule for UpdateCheckWorker added — WorkManager instantiates workers by name via reflection.
89 lines
4 KiB
Prolog
89 lines
4 KiB
Prolog
# SPDX-FileCopyrightText: 2026 Sulkta-Coop
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
#
|
|
# R8 keep rules for the Straw app module. The legacy `app/proguard-rules.pro`
|
|
# is for the upstream NewPipe module — different namespaces, different
|
|
# rules. This file is OURS.
|
|
#
|
|
# AGP's getDefaultProguardFile("proguard-android-optimize.txt") handles
|
|
# the Android framework + AndroidX + Compose runtime defaults via
|
|
# consumer rules shipped with each library. We only need to spell out
|
|
# what those defaults can't see:
|
|
#
|
|
# * UniFFI bindings — reflective FFI dispatch from generated code.
|
|
# * JNA — reflects on every class extending com.sun.jna.Library
|
|
# (that's how the loadLibrary glue works).
|
|
# * Our kotlinx-serialization @Serializable types — their generated
|
|
# $$serializer companions get tree-shaken without explicit keeps.
|
|
# * Media3 session metadata Parcelables.
|
|
|
|
# -- UniFFI -------------------------------------------------------------
|
|
# Generated bindings live under uniffi.strawcore.*. The Rust side calls
|
|
# them via JNI symbol name; if R8 renames the class or methods, every
|
|
# extractor call NPEs.
|
|
-keep class uniffi.strawcore.** { *; }
|
|
-keep class uniffi.** { *; }
|
|
|
|
# -- JNA ---------------------------------------------------------------
|
|
# JNA looks up Library subclasses by Class.forName + reflection at
|
|
# load time. Anything that extends Library or has @FieldOrder must
|
|
# survive.
|
|
-keep class * extends com.sun.jna.Library { *; }
|
|
-keep class com.sun.jna.** { *; }
|
|
-dontwarn com.sun.jna.**
|
|
|
|
# -- kotlinx-serialization ---------------------------------------------
|
|
# Every @Serializable type gets a synthetic Companion + $$serializer
|
|
# class. R8 will strip the $$serializer if nothing visibly calls it
|
|
# (the lookup goes through reflection on the Companion).
|
|
-keepattributes *Annotation*, InnerClasses
|
|
-dontwarn kotlinx.serialization.**
|
|
|
|
-keep,includedescriptorclasses class com.sulkta.straw.**$$serializer { *; }
|
|
-keepclassmembers class com.sulkta.straw.** {
|
|
*** Companion;
|
|
}
|
|
-keepclasseswithmembers class com.sulkta.straw.** {
|
|
kotlinx.serialization.KSerializer serializer(...);
|
|
}
|
|
|
|
# Same dance for our top-level @Serializable types defined outside
|
|
# `com.sulkta.straw.**` (Rust DTOs, etc.). Belt + suspenders.
|
|
-keepclassmembers @kotlinx.serialization.Serializable class * {
|
|
static **$Companion Companion;
|
|
public static <1>$Companion Companion;
|
|
}
|
|
-keepclasseswithmembers @kotlinx.serialization.Serializable class * {
|
|
kotlinx.serialization.KSerializer serializer(...);
|
|
}
|
|
-keep class **$$serializer { *; }
|
|
|
|
# -- Media3 / ExoPlayer ------------------------------------------------
|
|
# Most of Media3 ships consumer rules but session-related Parcelables
|
|
# are reflectively reconstructed across process boundaries (the
|
|
# MediaController talks to PlaybackService via Binder). Keep their
|
|
# field names.
|
|
-keep class androidx.media3.session.** { *; }
|
|
-keep class androidx.media3.common.MediaItem { *; }
|
|
-keep class androidx.media3.common.MediaItem$* { *; }
|
|
-keep class androidx.media3.common.MediaMetadata { *; }
|
|
|
|
# -- Strawcore exceptions / DTOs reflected by UniFFI --------------------
|
|
# StrawcoreError is a sealed Throwable hierarchy exposed via UniFFI.
|
|
# Keep all subclasses + their fields so the Kotlin pattern-match works
|
|
# after minification.
|
|
-keep class com.sulkta.straw.feature.player.** { *; }
|
|
|
|
# -- Reflection-via-Class.forName paths from Compose --------------------
|
|
# Compose's runtime does some Class.forName for its own bootstrap; the
|
|
# AGP consumer rules cover this, but documenting the dependency here
|
|
# so a future bump doesn't surprise us.
|
|
-keep class androidx.compose.runtime.** { *; }
|
|
|
|
# -- WorkManager Worker classes ----------------------------------------
|
|
# WorkManager instantiates Worker subclasses by class name via
|
|
# reflection (`Class.forName(workerSpec.workerClassName)`). If R8
|
|
# renames our UpdateCheckWorker the scheduler enqueues it but the
|
|
# instantiation fails silently and no checks ever run.
|
|
-keep class com.sulkta.straw.feature.update.UpdateCheckWorker { *; }
|
|
-keep class * extends androidx.work.ListenableWorker { *; }
|