diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 152e1dc..3e80453 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,6 +33,10 @@ android { targetSdk = 36 versionCode = 1 versionName = "0.1.0" + androidResources { + localeFilters += setOf("en", "es", "eu") + } + // testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner" @@ -52,8 +56,8 @@ android { buildTypes { release { - // isMinifyEnabled = true // Enables R8/ProGuard shrinking - // isShrinkResources = true // Removes unused resources + isMinifyEnabled = true // Enables R8/ProGuard shrinking + isShrinkResources = true // Removes unused resources proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -62,6 +66,8 @@ android { } create("benchmark") { initWith(buildTypes.getByName("release")) + isMinifyEnabled = false // benchmarks must not be minified + isShrinkResources = false // or results are meaningless signingConfig = signingConfigs.getByName("debug") // sign with debug key for easy deployment installation { enableBaselineProfile = false // ← disables profile embedding for this build type only @@ -116,7 +122,7 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.compose.ui.test.junit4) - implementation(libs.androidx.benchmark.junit4) + androidTestImplementation(libs.androidx.benchmark.junit4) androidTestImplementation(libs.androidx.runner) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -128,7 +134,7 @@ dependencies { implementation(libs.retrofit2.retrofit) implementation(libs.converter.gson) implementation(libs.okhttp) - implementation(libs.logging.interceptor) + debugImplementation(libs.logging.interceptor) // Coroutines implementation(libs.kotlinx.coroutines.android) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 4d11340..3a7c583 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,24 +1,124 @@ -# 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 all protobuf generated message classes and their fields +# ────────────────────────────────────────────────────────────────── +# Protobuf lite +# ────────────────────────────────────────────────────────────────── -keep class * extends com.google.protobuf.GeneratedMessageLite { *; } + +# ────────────────────────────────────────────────────────────────── +# Google Tink 1.13.0 +# ────────────────────────────────────────────────────────────────── +-keep class com.google.crypto.tink.proto.** { *; } +-keep class com.google.crypto.tink.shaded.protobuf.** { *; } +-keep class com.google.crypto.tink.** { *; } +-dontwarn com.google.crypto.tink.** + +# ────────────────────────────────────────────────────────────────── +# Gson — generic signature safety +# ────────────────────────────────────────────────────────────────── +-keepattributes Signature +-keepattributes *Annotation* +-keep,allowobfuscation,allowshrinking,allowoptimization class com.google.gson.reflect.TypeToken +-keep,allowobfuscation,allowshrinking,allowoptimization class * extends com.google.gson.reflect.TypeToken + +# ────────────────────────────────────────────────────────────────── +# Gson — ALL classes Gson instantiates via reflection +# ────────────────────────────────────────────────────────────────── + +# Response bodies +-keep class com.bitcointxoko.gudariwallet.api.WalletResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.PaymentStatusResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.PaymentDetails { *; } +-keep class com.bitcointxoko.gudariwallet.api.PaymentDetailResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.WithdrawCallbackResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.FiatRateResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.LnurlpLink { *; } + +# NWC models +-keep class com.bitcointxoko.gudariwallet.api.NwcGetResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.NwcKey { *; } +-keep class com.bitcointxoko.gudariwallet.api.NwcBudget { *; } +-keep class com.bitcointxoko.gudariwallet.api.NwcNewBudget { *; } +-keep class com.bitcointxoko.gudariwallet.api.NwcRegistrationRequest { *; } + +# Request bodies +-keep class com.bitcointxoko.gudariwallet.api.CreateInvoiceRequest { *; } +-keep class com.bitcointxoko.gudariwallet.api.PayInvoiceRequest { *; } +-keep class com.bitcointxoko.gudariwallet.api.DecodeInvoiceRequest { *; } +-keep class com.bitcointxoko.gudariwallet.api.PayLnurlRequest { *; } + +# SuccessAction sealed class + TypeAdapter subclasses +-keep class com.bitcointxoko.gudariwallet.api.SuccessAction { *; } +-keep class com.bitcointxoko.gudariwallet.api.FlexibleStringAdapter +-keep class com.bitcointxoko.gudariwallet.api.SuccessActionAdapter + +# Node alias API responses +-keep class com.bitcointxoko.gudariwallet.api.MempoolNodeResponse { *; } +-keep class com.bitcointxoko.gudariwallet.api.OneMlNodeResponse { *; } + +# ────────────────────────────────────────────────────────────────── +# Retrofit 3.0.0 +# ────────────────────────────────────────────────────────────────── +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +# ────────────────────────────────────────────────────────────────── +# OkHttp 5.x / Okio +# ────────────────────────────────────────────────────────────────── +-dontwarn okhttp3.** +-dontwarn okio.** +-dontwarn javax.annotation.** +-dontwarn org.codehaus.mojo.animal_sniffer.* + +# ────────────────────────────────────────────────────────────────── +# secp256k1-kmp (JNI) +# ────────────────────────────────────────────────────────────────── +-keep class fr.acinq.secp256k1.** { *; } +-keepclasseswithmembernames class * { + native ; +} + +# ────────────────────────────────────────────────────────────────── +# ZXing 4.3.0 +# ────────────────────────────────────────────────────────────────── +-keep class com.google.zxing.** { *; } +-dontwarn com.google.zxing.** + +# ────────────────────────────────────────────────────────────────── +# Room 2.7.1 +# ────────────────────────────────────────────────────────────────── +-keep class * extends androidx.room.RoomDatabase +-keep @androidx.room.Entity class * +-dontwarn androidx.room.** + +# ────────────────────────────────────────────────────────────────── +# WorkManager 2.9.1 +# ────────────────────────────────────────────────────────────────── +-keep class * extends androidx.work.Worker +-keep class * extends androidx.work.CoroutineWorker +-keep class * extends androidx.work.ListenableWorker { + public (android.content.Context, androidx.work.WorkerParameters); +} + +# ────────────────────────────────────────────────────────────────── +# Kotlin Coroutines 1.11.0 +# ────────────────────────────────────────────────────────────────── +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} +-dontwarn kotlinx.coroutines.** + +# ────────────────────────────────────────────────────────────────── +# Lyricist 1.8.0 +# ────────────────────────────────────────────────────────────────── +-keep class com.bitcointxoko.gudariwallet.**Strings { *; } +-keep class com.bitcointxoko.gudariwallet.**Translations { *; } + +# ────────────────────────────────────────────────────────────────── +# Timber 5.0.1 +# ────────────────────────────────────────────────────────────────── +-dontwarn org.jetbrains.annotations.** + +# ────────────────────────────────────────────────────────────────── +# Stack traces — upload mapping.txt to Play Console / Crashlytics +# ────────────────────────────────────────────────────────────────── +-keepattributes SourceFile,LineNumberTable +-renamesourcefileattribute SourceFile diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/api/LNbitsClient.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/api/LNbitsClient.kt index a26dc57..659524d 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/api/LNbitsClient.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/api/LNbitsClient.kt @@ -1,40 +1,22 @@ package com.bitcointxoko.gudariwallet.api -import com.bitcointxoko.gudariwallet.BuildConfig import com.bitcointxoko.gudariwallet.security.SecretStore import com.bitcointxoko.gudariwallet.util.WalletConstants -import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -import timber.log.Timber import java.util.concurrent.TimeUnit object LNbitsClient { - val logging = HttpLoggingInterceptor { message -> - Timber.tag("OkHttp").d(message) - }.apply { - level = HttpLoggingInterceptor.Level.BODY // logs full request + response body - } - // Single shared client — used by both Retrofit and WebSocket val httpClient: OkHttpClient = OkHttpClient.Builder() .connectTimeout(WalletConstants.CONNECT_TIMEOUT_S, TimeUnit.SECONDS) .readTimeout(WalletConstants.READ_TIMEOUT_S, TimeUnit.SECONDS) .writeTimeout(WalletConstants.WRITE_TIMEOUT_S, TimeUnit.SECONDS) - .addInterceptor(logging) - .apply { - if (BuildConfig.DEBUG) { - addInterceptor(HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - }) - } - } .build() fun create(secrets: SecretStore): LNbitsApi { @@ -71,21 +53,11 @@ object LNbitsClient { } val client = httpClient.newBuilder() -// .addInterceptor(authInterceptor) .addInterceptor(dynamicBaseUrlInterceptor) - .apply { - // Logging goes LAST — after URL rewrite — so logs show the real URL - if (BuildConfig.DEBUG) { - addInterceptor(HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - }) - } - } .build() return Retrofit.Builder() .baseUrl("https://placeholder.invalid/") // structurally required by Retrofit; - // always overwritten by the interceptor .client(client) .addConverterFactory(GsonConverterFactory.create(GsonProvider.gson)) .build()