# =====================================================================================================
# android-sdk-core: R8 / ProGuard CONSUMER rules (merged into every app that uses the Mapsted SDK).
#
# #304: this file used to ship `-keep class com.mapsted.** { *; }`. A library keep rule cannot be
# cancelled by the app, and every Mapsted app lives under com.mapsted.<app>, so that wildcard kept the
# APP's own classes as well: no Mapsted app could obfuscate anything. The rules below keep only what must
# survive renaming, and name the SDK packages one by one so an app's own com.mapsted.<app> package is
# free to be renamed.
#
# What must survive, and why (each block below says which):
#   1. the native engine (JNI binds Java names by string), and SDK classes the engine calls back into;
#   2. the public / protected SDK API (the documented surface; also what client crash reports name);
#   3. state that is serialised by NAME: Gson (field names = JSON keys, enum constant names), Parcelable,
#      Serializable; this is what keeps saved data readable across an SDK update;
#   4. classes and members reached by reflection or by a stored class name.
# The gate that checks the result against a real minified app is tools/r8-gate/ (see its README).
# =====================================================================================================

# ---- 1. Native engine (core-engine AAR, embedded) ----------------------------------------------------
# sdk-core embeds the engine's classes.jar via api(files(engineJar)), so the engine AAR's own
# proguard.txt is NOT applied to apps. Its rules are restated here; without them an app gets an
# UnsatisfiedLinkError at runtime, not a build error. libmapsted_core_engine_jni.so looks up 272 class
# names, all under com/mapsted/core/engine/, and exports 340 Java_com_mapsted_core_engine_* symbols.
-keep class com.mapsted.core.engine.** { *; }
-keep interface com.mapsted.core.engine.** { *; }
-keep class * extends com.mapsted.core.engine.internal.NativeBase { *; }
-keepclasseswithmembernames,includedescriptorclasses class * {
    native <methods>;
}
# The engine calls back into SDK objects through its callback interfaces (PositioningCallback,
# RoutingCallback, DatabaseCallback, ...): JNI resolves the method by name on the interface, so the
# implementing SDK methods must keep the interface's method names.
-keepclassmembers class * implements com.mapsted.core.engine.** {
    <methods>;
}
-keepclassmembers class * extends com.mapsted.core.engine.** {
    <methods>;
}

# ---- 2. Public SDK API of this module (package listed BY NAME) --------------------------------------
-keep public class com.mapsted.positioning.** {
    public protected *;
}
# The two SDK classes in the bare com.mapsted package, by name (a com.mapsted.* wildcard would match the
# app's own classes).
-keep public class com.mapsted.BaseApplication {
    public protected *;
}
-keep public class com.mapsted.MapstedBaseApplication {
    public protected *;
}

# ---- 3. State serialised by name (this module's package) -------------------------------------------
# Gson 2.8.9 ships no R8 rules. Gson reads and writes fields by name, so a renamed field silently drops
# from saved JSON (SharedPreferences, caches, upload queue) and from server responses. Keeping every
# instance field name in the SDK packages is deliberate: there are 171 Gson call sites, a missed model
# fails silently, and data written by an earlier (unobfuscated) SDK must still read back.
# Class NAMES may still be obfuscated; Gson does not use them.
-keep,allowobfuscation class com.mapsted.positioning.** {
    !static !transient <fields>;
    <init>();
}
-keepclassmembers class com.mapsted.positioning.** {
    !static !transient <fields>;
}
# Gson's enum adapter calls Class.getField(constant.name()); a renamed constant throws AssertionError.
-keepclassmembers enum com.mapsted.positioning.** {
    <fields>;
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
-keepclassmembers class com.mapsted.positioning.** implements android.os.Parcelable {
    public static final ** CREATOR;
}
-keepclassmembers class com.mapsted.positioning.** implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}
# Generic signatures for Gson TypeToken / Retrofit, and annotations (@SerializedName, @Keep).
-keepattributes Signature,InnerClasses,EnclosingMethod,*Annotation*
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation class * extends com.google.gson.reflect.TypeToken
-keepclassmembers,allowobfuscation class * {
    @com.google.gson.annotations.SerializedName <fields>;
}

# ---- 4. Reflection and stored class names ------------------------------------------------------------
# WorkManager stores the worker's class NAME in its database and re-instantiates it by that name, also
# after an app update.
-keep class com.mapsted.positioning.MapstedAutoWakeWorker {
    public <init>(android.content.Context, androidx.work.WorkerParameters);
}
# MapstedPrivateApi.setGeofenceEnabled: GeofenceManager.class.getMethod("setEnabled", boolean.class).
# (Covered by block 1; restated because the failure is silent: caught and logged at warn level.)
-keep class com.mapsted.core.engine.GeofenceManager {
    public static void setEnabled(boolean);
}
# FirebaseAnalyticsReflect / FirebaseCrashlyticsReflect / SentryReflect look these up by name at runtime.
# The app usually never calls them itself, so R8 would remove or rename them and the SDK's telemetry
# would silently turn off. These rules do nothing in an app that does not include the library.
-keep class com.google.firebase.analytics.FirebaseAnalytics {
    public static *** getInstance(android.content.Context);
    public void logEvent(java.lang.String, android.os.Bundle);
    public void setDefaultEventParameters(android.os.Bundle);
    public void setUserProperty(java.lang.String, java.lang.String);
    public void setUserId(java.lang.String);
    public void setSessionTimeoutDuration(long);
}
-keep class com.google.firebase.crashlytics.FirebaseCrashlytics {
    public static *** getInstance();
    public void recordException(java.lang.Throwable);
    public void log(java.lang.String);
    public void setCustomKey(java.lang.String, java.lang.String);
}
-keep class io.sentry.Sentry {
    public static *** captureException(java.lang.Throwable);
    public static void addBreadcrumb(java.lang.String);
    public static void setTag(java.lang.String, java.lang.String);
    public static boolean isEnabled();
}
# MapstedForegroundWakeService: Class.forName(details.activityClassName). The name is taken from the
# app's Activity class at runtime, and activities are kept by name through the manifest, so no rule is
# needed; the device test asserts it (R8KeepRulesDeviceTest).

# ---- Warnings ------------------------------------------------------------------------------------------
# Annotation-only references that are not on an app's runtime classpath.
-dontwarn org.jetbrains.annotations.**
-dontwarn org.joda.convert.**
# Firebase is compileOnly in sdk-core (the app brings it, or not). FirebaseAnalyticsReflect.Param/Event/
# UserProperty EXTEND FirebaseAnalytics.Param/Event/UserProperty, so an app without firebase-analytics fails
# R8 with "Missing class com.google.firebase.analytics.FirebaseAnalytics$Param" (measured on the :app
# minified build). The old blanket `-dontwarn com.mapsted.**` hid this; name the optional classes instead.
-dontwarn com.google.firebase.analytics.**
-dontwarn com.google.firebase.crashlytics.**
