From f6e9faa2d896a15a96e27dd3c4e7b05ca27a06d5 Mon Sep 17 00:00:00 2001 From: Simon Kiarie <696759+qiarie@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:27:40 +0300 Subject: [PATCH] Release APK Minification and Shrinking (#3520) * Configure Sentry extension - Add flags to enable/disable minification and resource shrinking * Ignore sentry.properties file - Update proguard-rules.pro * Update proguard rules * Update Proguard keep rules * Update proguard keep rules * Enable obfuscation for quest * Update proguard rules to fix JEXL logger crassh * Update proguard rules to keep log4j, remove crash - Run spotlessApply * Add sentry.properties file present check --------- Co-authored-by: Peter Lubell-Doughtie --- android/.gitignore | 2 + .../main/kotlin/project-properties.gradle.kts | 38 ++- android/engine/proguard-rules.pro | 239 +++++++++++++++++- android/geowidget/proguard-rules.pro | 232 ++++++++++++++++- android/gradle.properties | 9 + android/gradle/libs.versions.toml | 2 + android/quest/build.gradle.kts | 170 ++++++++++++- android/quest/proguard-rules.pro | 239 +++++++++++++++++- android/quest/src/main/assets/logback.xml | 11 + 9 files changed, 923 insertions(+), 19 deletions(-) create mode 100644 android/quest/src/main/assets/logback.xml diff --git a/android/.gitignore b/android/.gitignore index 5d2733b29a..4e7b5aafa0 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -21,3 +21,5 @@ keystore.properties # Gradle Properties gradle.properties + +/sentry.properties diff --git a/android/buildSrc/src/main/kotlin/project-properties.gradle.kts b/android/buildSrc/src/main/kotlin/project-properties.gradle.kts index 7bcb04d8f5..47b9fb752c 100644 --- a/android/buildSrc/src/main/kotlin/project-properties.gradle.kts +++ b/android/buildSrc/src/main/kotlin/project-properties.gradle.kts @@ -9,12 +9,10 @@ fun readProperties(file: String): Properties { val properties = Properties() val localProperties = File(file) if (localProperties.isFile) { - InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use { reader - -> + InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use { reader -> properties.load(reader) } - } - else throw FileNotFoundException("\u001B[34mFile $file not found\u001B[0m") + } else throw FileNotFoundException("\u001B[34mFile $file not found\u001B[0m") return properties } @@ -45,15 +43,35 @@ requiredFhirProperties.forEach { property -> // Set required keystore properties val requiredKeystoreProperties = listOf("KEYSTORE_ALIAS", "KEY_PASSWORD", "KEYSTORE_PASSWORD") -val keystoreProperties = try{ readProperties((project.properties["keystorePropertiesFile"] ?: "${rootProject.projectDir}/keystore.properties").toString()) } catch (e:FileNotFoundException){ - - if (project.properties["keystorePropertiesFile"] != null){ +val keystoreProperties = try { + readProperties((project.properties["keystorePropertiesFile"] ?: "${rootProject.projectDir}/keystore.properties").toString()) +} catch (e:FileNotFoundException) { + if (project.properties["keystorePropertiesFile"] != null) { throw e - }else Properties() - - + } else Properties() } requiredKeystoreProperties.forEach { property -> project.extra.set(property, keystoreProperties.getProperty(property, "sample_$property")) } + +// Set Sentry properties +val requiredSentryProperties = + listOf( + "org", + "project", + "auth.token", + "url" + ) + +val sentryProperties = try { + readProperties((project.properties["sentryPropertiesFile"] ?: "${rootProject.projectDir}/sentry.properties").toString()) +} catch (e: FileNotFoundException) { + if (project.properties["sentryPropertiesFile"] != null) { + throw e + } else Properties() +} + +requiredSentryProperties.forEach { property -> + project.extra.set(property, sentryProperties.getProperty(property, "sentry_$property")) +} diff --git a/android/engine/proguard-rules.pro b/android/engine/proguard-rules.pro index ff59496d81..0817a58419 100644 --- a/android/engine/proguard-rules.pro +++ b/android/engine/proguard-rules.pro @@ -14,8 +14,243 @@ # Uncomment this to preserve the line number information for # debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +-renamesourcefileattribute SourceFile + +#-dontshrink +#-dontobfuscate +#-dontoptimize +-printmapping +-verbose + +-keepattributes Signature, InnerClasses, EnclosingMethod +-keepattributes RuntimeVisibleAnnotations +-keepattributes Exceptions +-keepattributes *Annotation* + +-keep class org.smartregister.fhircore.engine.** { *; } + +# Keep Gson's data model classes +-keep class sun.misc.Unsafe { *; } +-keepclassmembers class org.smartregister.fhircore.engine.** { + @com.google.gson.annotations.SerializedName ; +} + +# Keep Retrofit-related classes +-keep class retrofit2.** { *; } +-keepclassmembers interface * { + @retrofit2.http.* ; +} +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +## Glide uses annotations +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public enum com.bumptech.glide.load.ResourceDecoder { *; } + +# Keep all Kotlin data classes +-keep class kotlin.**Kt$Data { *; } + +# Keep all classes that implement Serializable +-keep class * implements java.io.Serializable { *; } + +## Remove all Android log calls (Log.e, Log.d, Log.w, etc.) +#-assumenosideeffects class android.util.Log { +# public static int d(...); +# public static int e(...); +# public static int i(...); +# public static int v(...); +# public static int w(...); +# public static int wtf(...); +#} + +# This is generated automatically by the Android Gradle plugin. +-dontwarn androidx.test.platform.app.AppComponentFactoryRegistry +-dontwarn androidx.test.platform.concurrent.DirectExecutor +-dontwarn com.sun.jna.FunctionMapper +-dontwarn com.sun.jna.JNIEnv +-dontwarn com.sun.jna.LastErrorException +-dontwarn com.sun.jna.Library +-dontwarn com.sun.jna.Memory +-dontwarn com.sun.jna.Native +-dontwarn com.sun.jna.NativeLibrary +-dontwarn com.sun.jna.Platform +-dontwarn com.sun.jna.Pointer +-dontwarn com.sun.jna.Structure +-dontwarn com.sun.jna.platform.win32.Advapi32 +-dontwarn com.sun.jna.platform.win32.Kernel32 +-dontwarn com.sun.jna.platform.win32.Win32Exception +-dontwarn com.sun.jna.platform.win32.WinBase$OVERLAPPED +-dontwarn com.sun.jna.platform.win32.WinBase$SECURITY_ATTRIBUTES +-dontwarn com.sun.jna.platform.win32.WinDef$DWORD +-dontwarn com.sun.jna.platform.win32.WinDef$LPVOID +-dontwarn com.sun.jna.platform.win32.WinNT$ACL +-dontwarn com.sun.jna.platform.win32.WinNT$HANDLE +-dontwarn com.sun.jna.platform.win32.WinNT$SECURITY_DESCRIPTOR +-dontwarn com.sun.jna.ptr.IntByReference +-dontwarn com.sun.jna.win32.StdCallLibrary +-dontwarn com.sun.jna.win32.W32APIOptions +-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings +-dontwarn java.lang.instrument.ClassDefinition +-dontwarn java.lang.instrument.ClassFileTransformer +-dontwarn java.lang.instrument.IllegalClassFormatException +-dontwarn java.lang.instrument.Instrumentation +-dontwarn java.lang.instrument.UnmodifiableClassException +-dontwarn org.apiguardian.api.API$Status +-dontwarn org.apiguardian.api.API + +-keep class java.lang.instrument.** { *; } +-dontwarn java.lang.instrument.** +-keep class net.bytebuddy.** { *; } +-dontwarn net.bytebuddy.** +-keep class androidx.test.** { *; } +-dontwarn androidx.test.** + +-keep class com.sun.jna.** { *; } +-dontwarn com.sun.jna.** +-keep class edu.umd.cs.findbugs.annotations.** { *; } +-dontwarn edu.umd.cs.findbugs.annotations.** +-keep class org.apiguardian.** { *; } +-dontwarn org.apiguardian.** + +-keep interface com.google.android.gms.location.FusedLocationProviderClient +-keep class com.google.android.gms.location.LocationCallback.** { *; } +-dontwarn com.google.android.gms.** + +-keep class ** implements java.lang.reflect.ParameterizedType { *; } + +-keep class com.auth0.jwt.interfaces.** { *; } + +-keep class com.fasterxml.jackson.core.type.** { *; } + +# Keep Jackson ObjectMapper and related serializers/deserializers +-keep class com.fasterxml.jackson.databind.ObjectMapper { *; } +-keep class com.fasterxml.jackson.databind.ObjectMapper$* { *; } +-keep class com.fasterxml.jackson.databind.** { *; } + +# Keep TypeReference (used for generic types) +-keep class com.fasterxml.jackson.core.type.TypeReference { *; } + +-keep class com.auth0.jwt.** { *; } + +-keep class com.google.gson.** { *; } + +-keep class org.apache.logging.log4j.** { *; } + +-keep class org.apache.commons.** { *; } +-keep class org.apache.commons.logging.** { *; } +-keep interface org.apache.commons.logging.Log +-keep class org.apache.commons.logging.impl.** { *; } +-keep class org.apache.commons.logging.impl.LogFactoryImpl { *; } +-keep class org.apache.commons.logging.impl.Log4JLogger { *; } +-keep class org.apache.commons.logging.impl.Jdk14Logger { *; } +#-keep class org.apache.commons.logging.impl.Jdk13LumberjackLogger { *; } +-keep class org.apache.commons.logging.impl.SimpleLog { *; } +-keep class org.apache.commons.logging.LogFactory.** { *; } +-keep class org.apache.commons.logging.LogConfigurationException +-keep class java.lang.ExceptionInInitializerError + +-keepclassmembers class ** { + static java.lang.ClassLoader getClassLoader(); +} + +# Keep all classes with references to reflection (necessary for LogFactory) +-keepclassmembers class * { + public void set*(***); + public *** get*(***); +} + +# Keep Apache Commons BeanUtils classes, in case they’re needed +-keep class org.apache.commons.beanutils.** { *; } + +-keep class org.apache.log4j.** { *; } +-keep class org.slf4j.** { *; } + +-keep enum * { *; } + +-keepclassmembers class * { + *; +} + +# Keep constructors for logging classes +-keepclassmembers class org.apache.commons.logging.** { + public (...); +} + +# Keep all class members that could be accessed via reflection +-keep class * extends java.lang.reflect.** { *; } +-keepclassmembers class * { + *; +} + +-keep class org.jeasy.rules.jexl.** { *; } +-keep class org.jeasy.rules.jexl.JexlRule { *; } +-keep class org.jeasy.rules.core.** { *; } + +-keep class org.apache.commons.jexl3.** { *; } +-dontwarn org.apache.commons.jexl3.** +-keep class org.apache.commons.jexl3.JexlBuilder { *; } +-keep class org.apache.commons.jexl3.internal.** { *; } +-keep class org.apache.commons.jexl3.internal.Engine { *; } +-keep class org.apache.commons.jexl3.introspection.** { *; } +-keep class org.apache.commons.jexl3.introspection.JexlSandbox { *; } +-keep class org.apache.commons.jexl3.JexlEngine { *; } +-keep class org.apache.commons.jexl3.internal.introspection.Uberspect { *; } +-keep interface org.apache.commons.jexl3.introspection.JexlUberspect +-keep class org.apache.commons.jexl3.introspection.JexlUberspect$** { *; } + +# Keep constructors for JEXL-related classes +-keepclassmembers class org.apache.commons.jexl3.** { + public (...); +} + +-keepclassmembers class org.apache.commons.jexl3.internal.Engine { + (); + void getUberspect(); +} + +-keep class org.apache.commons.lang3.StringUtils { *; } +-keep class org.apache.commons.lang3.RegExUtils { *; } + +-keepclasseswithmembers class ** { + @kotlin.Metadata public final class *; +} + +-keep class kotlinx.coroutines.** { *; } + +-keep class javax.script.** { *; } +-dontwarn javax.script.** +-keep class java.beans.** { *; } +-dontwarn java.beans.** + +-keep class org.apache.commons.jexl3.introspection.JexlSandbox { *; } +-keep interface org.apache.commons.jexl3.introspection.JexlUberspect + +-keep class java.util.Map { *; } +-keep class java.nio.charset.Charset { *; } + +-keep class kotlin.Metadata + +-keep class timber.log.Timber { *; } + +-keep class org.apache.log4j.** { *; } +-keep class org.apache.commons.logging.** { *; } +-dontwarn java.beans.** +-dontwarn org.apache.log4j.** + +-assumenosideeffects class org.apache.log4j.Logger { + public static *; + public *; +} + +# Keep Logback classes +-keep class ch.qos.logback.** { *; } + +# Keep the logback.xml configuration file +-keep class * { + public static final java.lang.String LOGBACK_CONFIG_FILE; +} diff --git a/android/geowidget/proguard-rules.pro b/android/geowidget/proguard-rules.pro index ff59496d81..998ff9cb23 100644 --- a/android/geowidget/proguard-rules.pro +++ b/android/geowidget/proguard-rules.pro @@ -14,8 +14,236 @@ # Uncomment this to preserve the line number information for # debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +-renamesourcefileattribute SourceFile + +#-dontshrink +#-dontobfuscate +#-dontoptimize +-printmapping +-verbose + +-keepattributes Signature, InnerClasses, EnclosingMethod +-keepattributes RuntimeVisibleAnnotations +-keepattributes Exceptions +-keepattributes *Annotation* + +-keep class org.smartregister.fhircore.geowidget.** { *; } + +# Keep Gson's data model classes +-keep class sun.misc.Unsafe { *; } +-keepclassmembers class org.smartregister.fhircore.geowidget.** { + @com.google.gson.annotations.SerializedName ; +} + +# Keep Retrofit-related classes +-keep class retrofit2.** { *; } +-keepclassmembers interface * { + @retrofit2.http.* ; +} +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +## Glide uses annotations +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public enum com.bumptech.glide.load.ResourceDecoder { *; } + +# Keep all Kotlin data classes +-keep class kotlin.**Kt$Data { *; } + +# Keep all classes that implement Serializable +-keep class * implements java.io.Serializable { *; } + +# Remove all Android log calls (Log.e, Log.d, Log.w, etc.) +-assumenosideeffects class android.util.Log { + public static int d(...); + public static int e(...); + public static int i(...); + public static int v(...); + public static int w(...); + public static int wtf(...); +} + +# This is generated automatically by the Android Gradle plugin. +-dontwarn androidx.test.platform.app.AppComponentFactoryRegistry +-dontwarn androidx.test.platform.concurrent.DirectExecutor +-dontwarn com.sun.jna.FunctionMapper +-dontwarn com.sun.jna.JNIEnv +-dontwarn com.sun.jna.LastErrorException +-dontwarn com.sun.jna.Library +-dontwarn com.sun.jna.Memory +-dontwarn com.sun.jna.Native +-dontwarn com.sun.jna.NativeLibrary +-dontwarn com.sun.jna.Platform +-dontwarn com.sun.jna.Pointer +-dontwarn com.sun.jna.Structure +-dontwarn com.sun.jna.platform.win32.Advapi32 +-dontwarn com.sun.jna.platform.win32.Kernel32 +-dontwarn com.sun.jna.platform.win32.Win32Exception +-dontwarn com.sun.jna.platform.win32.WinBase$OVERLAPPED +-dontwarn com.sun.jna.platform.win32.WinBase$SECURITY_ATTRIBUTES +-dontwarn com.sun.jna.platform.win32.WinDef$DWORD +-dontwarn com.sun.jna.platform.win32.WinDef$LPVOID +-dontwarn com.sun.jna.platform.win32.WinNT$ACL +-dontwarn com.sun.jna.platform.win32.WinNT$HANDLE +-dontwarn com.sun.jna.platform.win32.WinNT$SECURITY_DESCRIPTOR +-dontwarn com.sun.jna.ptr.IntByReference +-dontwarn com.sun.jna.win32.StdCallLibrary +-dontwarn com.sun.jna.win32.W32APIOptions +-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings +-dontwarn java.lang.instrument.ClassDefinition +-dontwarn java.lang.instrument.ClassFileTransformer +-dontwarn java.lang.instrument.IllegalClassFormatException +-dontwarn java.lang.instrument.Instrumentation +-dontwarn java.lang.instrument.UnmodifiableClassException +-dontwarn org.apiguardian.api.API$Status +-dontwarn org.apiguardian.api.API + +-keep class java.lang.instrument.** { *; } +-dontwarn java.lang.instrument.** +-keep class net.bytebuddy.** { *; } +-dontwarn net.bytebuddy.** +-keep class androidx.test.** { *; } +-dontwarn androidx.test.** + +-keep class com.sun.jna.** { *; } +-dontwarn com.sun.jna.** +-keep class edu.umd.cs.findbugs.annotations.** { *; } +-dontwarn edu.umd.cs.findbugs.annotations.** +-keep class org.apiguardian.** { *; } +-dontwarn org.apiguardian.** + +-keep interface com.google.android.gms.location.FusedLocationProviderClient +-keep class com.google.android.gms.location.LocationCallback.** { *; } +-dontwarn com.google.android.gms.** + +-keep class ** implements java.lang.reflect.ParameterizedType { *; } + +-keep class com.auth0.jwt.interfaces.** { *; } + +-keep class com.fasterxml.jackson.core.type.** { *; } + +# Keep Jackson ObjectMapper and related serializers/deserializers +-keep class com.fasterxml.jackson.databind.ObjectMapper { *; } +-keep class com.fasterxml.jackson.databind.ObjectMapper$* { *; } +-keep class com.fasterxml.jackson.databind.** { *; } + +# Keep TypeReference (used for generic types) +-keep class com.fasterxml.jackson.core.type.TypeReference { *; } + +-keep class com.auth0.jwt.** { *; } + +-keep class com.google.gson.** { *; } + +-keep class org.apache.logging.log4j.** { *; } + +-keep class org.apache.commons.** { *; } +-keep class org.apache.commons.logging.** { *; } +-keep interface org.apache.commons.logging.Log +-keep class org.apache.commons.logging.impl.** { *; } +-keep class org.apache.commons.logging.impl.LogFactoryImpl { *; } +-keep class org.apache.commons.logging.impl.Log4JLogger { *; } +-keep class org.apache.commons.logging.impl.Jdk14Logger { *; } +#-keep class org.apache.commons.logging.impl.Jdk13LumberjackLogger { *; } +-keep class org.apache.commons.logging.impl.SimpleLog { *; } +-keep class org.apache.commons.logging.LogFactory.** { *; } +-keep class org.apache.commons.logging.LogConfigurationException +-keep class java.lang.ExceptionInInitializerError + +-keepclassmembers class ** { + static java.lang.ClassLoader getClassLoader(); +} + +# Keep all classes with references to reflection (necessary for LogFactory) +-keepclassmembers class * { + public void set*(***); + public *** get*(***); +} + +# Keep Apache Commons BeanUtils classes, in case they’re needed +-keep class org.apache.commons.beanutils.** { *; } + +-keep class org.apache.log4j.** { *; } +-keep class org.slf4j.** { *; } + +-keep enum * { *; } + +-keepclassmembers class * { + *; +} + +# Keep constructors for logging classes +-keepclassmembers class org.apache.commons.logging.** { + public (...); +} + +# Keep all class members that could be accessed via reflection +-keep class * extends java.lang.reflect.** { *; } +-keepclassmembers class * { + *; +} + +-keep class org.jeasy.rules.jexl.** { *; } +-keep class org.jeasy.rules.jexl.JexlRule { *; } +-keep class org.jeasy.rules.core.** { *; } + +-keep class org.apache.commons.jexl3.** { *; } +-dontwarn org.apache.commons.jexl3.** +-keep class org.apache.commons.jexl3.JexlBuilder { *; } +-keep class org.apache.commons.jexl3.internal.** { *; } +-keep class org.apache.commons.jexl3.internal.Engine { *; } +-keep class org.apache.commons.jexl3.introspection.** { *; } +-keep class org.apache.commons.jexl3.introspection.JexlSandbox { *; } +-keep class org.apache.commons.jexl3.JexlEngine { *; } +-keep class org.apache.commons.jexl3.internal.introspection.Uberspect { *; } +-keep interface org.apache.commons.jexl3.introspection.JexlUberspect +-keep class org.apache.commons.jexl3.introspection.JexlUberspect$** { *; } + +# Keep constructors for JEXL-related classes +-keepclassmembers class org.apache.commons.jexl3.** { + public (...); +} + +-keepclassmembers class org.apache.commons.jexl3.internal.Engine { + (); + void getUberspect(); +} + +-keep class org.apache.commons.lang3.StringUtils { *; } +-keep class org.apache.commons.lang3.RegExUtils { *; } + +-keepclasseswithmembers class ** { + @kotlin.Metadata public final class *; +} + +-keep class kotlinx.coroutines.** { *; } + +-keep class javax.script.** { *; } +-dontwarn javax.script.** +-keep class java.beans.** { *; } +-dontwarn java.beans.** + +-keep class org.apache.commons.jexl3.introspection.JexlSandbox { *; } +-keep interface org.apache.commons.jexl3.introspection.JexlUberspect + +-keep class java.util.Map { *; } +-keep class java.nio.charset.Charset { *; } + +-keep class kotlin.Metadata + +-keep class timber.log.Timber { *; } + +-keep class org.apache.log4j.** { *; } +-keep class org.apache.commons.logging.** { *; } +-dontwarn java.beans.** +-dontwarn org.apache.log4j.** + +-assumenosideeffects class org.apache.log4j.Logger { + public static *; + public *; +} + diff --git a/android/gradle.properties b/android/gradle.properties index 30b7024f0d..262a8849e7 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -22,3 +22,12 @@ android.defaults.buildfeatures.buildconfig=true android.suppressUnsupportedCompileSdk=34 android.jetifier.ignorelist=jackson-core org.gradle.warning.mode=all + +org.gradle.logging.level=INFO + +org.gradle.caching=false +org.gradle.parallel=true +org.gradle.configureondemand=true + +android.enableR8.fullMode=true +android.enableR8.verbose=true diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index a0127deac9..b4e18b95e4 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -54,6 +54,7 @@ kujaku-library = "0.10.8-SNAPSHOT" kujaku-mapbox-sdk-turf = "7.2.0" leakcanary-android = "2.10" lifecycle= "2.8.5" +log4j = "1.2.17" logback-android = "3.0.0" material = "1.12.0" mlkit-barcode-scanning = "17.3.0" @@ -158,6 +159,7 @@ leakcanary-android = { group = "com.squareup.leakcanary", name = "leakcanary-and lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" } lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } +log4j = { module = "log4j:log4j", version.ref = "log4j" } logback-android = { module = "com.github.tony19:logback-android", version.ref = "logback-android" } mapbox-sdk-turf = { group = "com.mapbox.mapboxsdk", name = "mapbox-sdk-turf", version.ref = "kujaku-mapbox-sdk-turf" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } diff --git a/android/quest/build.gradle.kts b/android/quest/build.gradle.kts index 82015b9ea4..ff2834411a 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -1,5 +1,7 @@ import android.databinding.tool.ext.capitalizeUS import com.android.build.api.variant.FilterConfiguration.FilterType +import io.sentry.android.gradle.extensions.InstrumentationFeature +import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel import java.io.FileReader import java.text.SimpleDateFormat import java.util.Date @@ -21,7 +23,7 @@ plugins { id("dagger.hilt.android.plugin") id("androidx.navigation.safeargs") id("org.sonarqube") version "3.5.0.2730" - id("io.sentry.android.gradle") version "3.5.0" + id("io.sentry.android.gradle") version "3.11.1" } sonar { @@ -114,7 +116,8 @@ android { } getByName("release") { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", @@ -150,6 +153,18 @@ android { "META-INF/INDEX.LIST", ), ) + + resources.excludes.addAll( + listOf( + "META-INF/*.kotlin_module", + ), + ) + resources.pickFirsts.addAll( + listOf( + "META-INF/services/org.apache.commons.logging.LogFactory", + "org/apache/commons/logging/commons-logging.properties", + ), + ) } compileOptions { @@ -469,6 +484,7 @@ dependencies { implementation(libs.mlkit.barcode.scanning) implementation(libs.androidx.fragment.compose) implementation(libs.bundles.cameraX) + implementation(libs.log4j) // Annotation processors kapt(libs.hilt.compiler) @@ -491,7 +507,7 @@ dependencies { // To run only on debug builds debugImplementation(libs.ui.test.manifest) debugImplementation(libs.fragment.testing) - // debugImplementation(libs.leakcanary.android) + // debugImplementation(libs.leakcanary.android) // Annotation processors for test kaptTest(libs.dagger.hilt.android.compiler) @@ -592,6 +608,154 @@ task("evaluatePerformanceBenchmarkResults") { } } +sentry { + // Disables or enables debug log output, e.g. for for sentry-cli. + // Default is disabled. + debug.set(true) + + // The slug of the Sentry organization to use for uploading proguard mappings/source contexts. + org.set("""${project.extra["org"]}""") + + // The slug of the Sentry project to use for uploading proguard mappings/source contexts. + projectName.set("""${project.extra["project"]}""") + + // The authentication token to use for uploading proguard mappings/source contexts. + // WARNING: Do not expose this token in your build.gradle files, but rather set an environment + // variable and read it into this property. + authToken.set("""${project.extra["auth.token"]}""") + + // The url of your Sentry instance. If you're using SAAS (not self hosting) you do not have to + // set this. If you are self hosting you can set your URL here + // url = null + + // Disables or enables the handling of Proguard mapping for Sentry. + // If enabled the plugin will generate a UUID and will take care of + // uploading the mapping to Sentry. If disabled, all the logic + // related to proguard mapping will be excluded. + // Default is enabled. + includeProguardMapping.set(false) + + // Whether the plugin should attempt to auto-upload the mapping file to Sentry or not. + // If disabled the plugin will run a dry-run and just generate a UUID. + // The mapping file has to be uploaded manually via sentry-cli in this case. + // Default is enabled. + autoUploadProguardMapping.set(false) + + // Experimental flag to turn on support for GuardSquare's tools integration (Dexguard and + // External Proguard). + // If enabled, the plugin will try to consume and upload the mapping file produced by Dexguard + // and External Proguard. + // Default is disabled. + // dexguardEnabled.set(false) + + // Disables or enables the automatic configuration of Native Symbols + // for Sentry. This executes sentry-cli automatically so + // you don't need to do it manually. + // Default is disabled. + uploadNativeSymbols.set(false) + + // Whether the plugin should attempt to auto-upload the native debug symbols to Sentry or not. + // If disabled the plugin will run a dry-run. + // Default is enabled. + autoUploadNativeSymbols.set(true) + + // Does or doesn't include the source code of native code for Sentry. + // This executes sentry-cli with the --include-sources param. automatically so + // you don't need to do it manually. + // Default is disabled. + includeNativeSources.set(false) + + // Generates a JVM (Java, Kotlin, etc.) source bundle and uploads your source code to Sentry. + // This enables source context, allowing you to see your source + // code as part of your stack traces in Sentry. + includeSourceContext.set(false) + + // Configure additional directories to be included in the source bundle which is used for + // source context. The directories should be specified relative to the Gradle module/project's + // root. For example, if you have a custom source set alongside 'main', the parameter would be + // 'src/custom/java'. + additionalSourceDirsForSourceContext.set(emptySet()) + + // Enable or disable the tracing instrumentation. + // Does auto instrumentation for specified features through bytecode manipulation. + // Default is enabled. + tracingInstrumentation { + enabled.set(true) + + // Specifies a set of instrumentation features that are eligible for bytecode manipulation. + // Defaults to all available values of InstrumentationFeature enum class. + features.set( + setOf( + InstrumentationFeature.DATABASE, + InstrumentationFeature.FILE_IO, + InstrumentationFeature.OKHTTP, + InstrumentationFeature.COMPOSE, + ), + ) + + // Enable or disable logcat instrumentation through bytecode manipulation. + // Default is enabled. + logcat { + enabled.set(true) + + // Specifies a minimum log level for the logcat breadcrumb logging. + // Defaults to LogcatLevel.WARNING. + minLevel.set(LogcatLevel.WARNING) + } + + // The set of glob patterns to exclude from instrumentation. Classes matching any of these + // patterns in the project's sources and dependencies JARs won't be instrumented by the + // Sentry Gradle plugin. + // + // Don't include the file extension. Filtering is done on compiled classes and + // the .class suffix isn't included in the pattern matching. + // + // Example usage: + // ``` + // excludes.set(setOf("com/example/donotinstrument/**", "**/*Test")) + // ``` + // + // Only supported when using Android Gradle plugin (AGP) version 7.4.0 and above. + // excludes.set(emptySet()) + } + + // Enable auto-installation of Sentry components (sentry-android SDK and okhttp, timber, fragment + // and compose integrations). + // Default is enabled. + // Only available v3.1.0 and above. + autoInstallation { + enabled.set(true) + + // Specifies a version of the sentry-android SDK and fragment, timber and okhttp integrations. + // + // This is also useful, when you have the sentry-android SDK already included into a transitive + // dependency/module and want to + // align integration versions with it (if it's a direct dependency, the version will be + // inferred). + // + // NOTE: if you have a higher version of the sentry-android SDK or integrations on the + // classpath, this setting will have no effect + // as Gradle will resolve it to the latest version. + // + // Defaults to the latest published Sentry version. + sentryVersion.set("7.14.0") + } + + // Disables or enables dependencies metadata reporting for Sentry. + // If enabled, the plugin will collect external dependencies and + // upload them to Sentry as part of events. If disabled, all the logic + // related to the dependencies metadata report will be excluded. + // + // Default is enabled. + includeDependenciesReport.set(true) + + // Whether the plugin should send telemetry data to Sentry. + // If disabled the plugin won't send telemetry data. + // This is auto disabled if running against a self hosted instance of Sentry. + // Default is enabled. + // telemetry.set(true) +} + fun JSONObject.getTestName(): String { val className = getString("className").substringAfterLast(".") val methodName = getString("name").substringAfterLast("_") diff --git a/android/quest/proguard-rules.pro b/android/quest/proguard-rules.pro index ff59496d81..03f3add856 100644 --- a/android/quest/proguard-rules.pro +++ b/android/quest/proguard-rules.pro @@ -14,8 +14,243 @@ # Uncomment this to preserve the line number information for # debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +-renamesourcefileattribute SourceFile + +#-dontshrink +#-dontobfuscate +#-dontoptimize +-printmapping +-verbose + +-keepattributes Signature, InnerClasses, EnclosingMethod +-keepattributes RuntimeVisibleAnnotations +-keepattributes Exceptions +-keepattributes *Annotation* + +-keep class org.smartregister.fhircore.quest.** { *; } + +# Keep Gson's data model classes +-keep class sun.misc.Unsafe { *; } +-keepclassmembers class org.smartregister.fhircore.quest.** { + @com.google.gson.annotations.SerializedName ; +} + +# Keep Retrofit-related classes +-keep class retrofit2.** { *; } +-keepclassmembers interface * { + @retrofit2.http.* ; +} +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +# Glide uses annotations +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public enum com.bumptech.glide.load.ResourceDecoder { *; } + +# Keep all Kotlin data classes +-keep class kotlin.**Kt$Data { *; } + +# Keep all classes that implement Serializable +-keep class * implements java.io.Serializable { *; } + +## Remove all Android log calls (Log.e, Log.d, Log.w, etc.) +#-assumenosideeffects class android.util.Log { +# public static int d(...); +# public static int e(...); +# public static int i(...); +# public static int v(...); +# public static int w(...); +# public static int wtf(...); +#} + +# This is generated automatically by the Android Gradle plugin. +-dontwarn androidx.test.platform.app.AppComponentFactoryRegistry +-dontwarn androidx.test.platform.concurrent.DirectExecutor +-dontwarn com.sun.jna.FunctionMapper +-dontwarn com.sun.jna.JNIEnv +-dontwarn com.sun.jna.LastErrorException +-dontwarn com.sun.jna.Library +-dontwarn com.sun.jna.Memory +-dontwarn com.sun.jna.Native +-dontwarn com.sun.jna.NativeLibrary +-dontwarn com.sun.jna.Platform +-dontwarn com.sun.jna.Pointer +-dontwarn com.sun.jna.Structure +-dontwarn com.sun.jna.platform.win32.Advapi32 +-dontwarn com.sun.jna.platform.win32.Kernel32 +-dontwarn com.sun.jna.platform.win32.Win32Exception +-dontwarn com.sun.jna.platform.win32.WinBase$OVERLAPPED +-dontwarn com.sun.jna.platform.win32.WinBase$SECURITY_ATTRIBUTES +-dontwarn com.sun.jna.platform.win32.WinDef$DWORD +-dontwarn com.sun.jna.platform.win32.WinDef$LPVOID +-dontwarn com.sun.jna.platform.win32.WinNT$ACL +-dontwarn com.sun.jna.platform.win32.WinNT$HANDLE +-dontwarn com.sun.jna.platform.win32.WinNT$SECURITY_DESCRIPTOR +-dontwarn com.sun.jna.ptr.IntByReference +-dontwarn com.sun.jna.win32.StdCallLibrary +-dontwarn com.sun.jna.win32.W32APIOptions +-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings +-dontwarn java.lang.instrument.ClassDefinition +-dontwarn java.lang.instrument.ClassFileTransformer +-dontwarn java.lang.instrument.IllegalClassFormatException +-dontwarn java.lang.instrument.Instrumentation +-dontwarn java.lang.instrument.UnmodifiableClassException +-dontwarn org.apiguardian.api.API$Status +-dontwarn org.apiguardian.api.API + +-keep class java.lang.instrument.** { *; } +-dontwarn java.lang.instrument.** +-keep class net.bytebuddy.** { *; } +-dontwarn net.bytebuddy.** +-keep class androidx.test.** { *; } +-dontwarn androidx.test.** + +-keep class com.sun.jna.** { *; } +-dontwarn com.sun.jna.** +-keep class edu.umd.cs.findbugs.annotations.** { *; } +-dontwarn edu.umd.cs.findbugs.annotations.** +-keep class org.apiguardian.** { *; } +-dontwarn org.apiguardian.** + +-keep interface com.google.android.gms.location.FusedLocationProviderClient +-keep class com.google.android.gms.location.LocationCallback.** { *; } +-dontwarn com.google.android.gms.** + +-keep class ** implements java.lang.reflect.ParameterizedType { *; } + +-keep class com.auth0.jwt.interfaces.** { *; } + +-keep class com.fasterxml.jackson.core.type.** { *; } + +# Keep Jackson ObjectMapper and related serializers/deserializers +-keep class com.fasterxml.jackson.databind.ObjectMapper { *; } +-keep class com.fasterxml.jackson.databind.ObjectMapper$* { *; } +-keep class com.fasterxml.jackson.databind.** { *; } + +# Keep TypeReference (used for generic types) +-keep class com.fasterxml.jackson.core.type.TypeReference { *; } + +-keep class com.auth0.jwt.** { *; } + +-keep class com.google.gson.** { *; } + +-keep class org.apache.logging.log4j.** { *; } + +-keep class org.apache.commons.** { *; } +-keep class org.apache.commons.logging.** { *; } +-keep interface org.apache.commons.logging.Log +-keep class org.apache.commons.logging.impl.** { *; } +-keep class org.apache.commons.logging.impl.LogFactoryImpl { *; } +-keep class org.apache.commons.logging.impl.Log4JLogger { *; } +-keep class org.apache.commons.logging.impl.Jdk14Logger { *; } +-keep class org.apache.commons.logging.impl.Jdk13LumberjackLogger { *; } +-keep class org.apache.commons.logging.impl.SimpleLog { *; } +-keep class org.apache.commons.logging.LogFactory.** { *; } +-keep class org.apache.commons.logging.LogConfigurationException +-keep class java.lang.ExceptionInInitializerError + +-keepclassmembers class ** { + static java.lang.ClassLoader getClassLoader(); +} + +# Keep all classes with references to reflection (necessary for LogFactory) +-keepclassmembers class * { + public void set*(***); + public *** get*(***); +} + +## Keep Apache Commons BeanUtils classes, in case they’re needed +-keep class org.apache.commons.beanutils.** { *; } + +-keep class org.apache.log4j.** { *; } +-keep class org.slf4j.** { *; } + +-keep enum * { *; } + +-keepclassmembers class * { + *; +} + +# Keep constructors for logging classes +-keepclassmembers class org.apache.commons.logging.** { + public (...); +} + +# Keep all class members that could be accessed via reflection +-keep class * extends java.lang.reflect.** { *; } +-keepclassmembers class * { + *; +} + +#-keep class org.jeasy.rules.jexl.** { *; } +-keep class org.jeasy.rules.jexl.JexlRule { *; } +-keep class org.jeasy.rules.core.** { *; } + +-keep class org.apache.commons.jexl3.** { *; } +-dontwarn org.apache.commons.jexl3.** +-keep class org.apache.commons.jexl3.JexlBuilder { *; } +-keep class org.apache.commons.jexl3.internal.** { *; } +-keep class org.apache.commons.jexl3.internal.Engine { *; } +-keep class org.apache.commons.jexl3.introspection.** { *; } +-keep class org.apache.commons.jexl3.introspection.JexlSandbox { *; } +-keep class org.apache.commons.jexl3.JexlEngine { *; } +-keep class org.apache.commons.jexl3.internal.introspection.Uberspect { *; } +-keep interface org.apache.commons.jexl3.introspection.JexlUberspect +-keep class org.apache.commons.jexl3.introspection.JexlUberspect$** { *; } + +# Keep constructors for JEXL-related classes +-keepclassmembers class org.apache.commons.jexl3.** { + public (...); +} + +-keepclassmembers class org.apache.commons.jexl3.internal.Engine { + (); + void getUberspect(); +} + +-keep class org.apache.commons.lang3.StringUtils { *; } +-keep class org.apache.commons.lang3.RegExUtils { *; } + +-keepclasseswithmembers class ** { + @kotlin.Metadata public final class *; +} + +-keep class kotlinx.coroutines.** { *; } + +-keep class javax.script.** { *; } +-dontwarn javax.script.** +-keep class java.beans.** { *; } +-dontwarn java.beans.** + +-keep class org.apache.commons.jexl3.introspection.JexlSandbox { *; } +-keep interface org.apache.commons.jexl3.introspection.JexlUberspect + +-keep class java.util.Map { *; } +-keep class java.nio.charset.Charset { *; } + +-keep class kotlin.Metadata + +-keep class timber.log.Timber { *; } + +-keep class org.apache.log4j.** { *; } +-keep class org.apache.commons.logging.** { *; } +-dontwarn java.beans.** +-dontwarn org.apache.log4j.** + +-assumenosideeffects class org.apache.log4j.Logger { + public static *; + public *; +} + +# Keep Logback classes +-keep class ch.qos.logback.** { *; } + +# Keep the logback.xml configuration file +-keep class * { + public static final java.lang.String LOGBACK_CONFIG_FILE; +} diff --git a/android/quest/src/main/assets/logback.xml b/android/quest/src/main/assets/logback.xml new file mode 100644 index 0000000000..fd1dcdb0ba --- /dev/null +++ b/android/quest/src/main/assets/logback.xml @@ -0,0 +1,11 @@ + + + + %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n + + + + + + +