refactor: make linter happy

This commit is contained in:
2026-06-12 18:15:04 +02:00
parent f2d6aeb13b
commit c155f43870
19 changed files with 46 additions and 52 deletions
@@ -2,24 +2,18 @@ package com.bitcointxoko.gudariwallet
import android.Manifest
import android.app.PendingIntent
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.IntentFilter
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.provider.Settings
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.*
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
@@ -18,7 +18,7 @@ import kotlinx.serialization.json.jsonPrimitive
* - a JSON array ["Thanks!"]
* - null
*
* Mirrors the behaviour of the old Gson FlexibleStringAdapter.
* Mirrors the behavior of the old Gson FlexibleStringAdapter.
*/
object FlexibleStringSerializer : KSerializer<String?> {
@@ -3,7 +3,7 @@ package com.bitcointxoko.gudariwallet.api
import kotlinx.serialization.json.Json
/**
* Single source of truth for the app's Json instance.
* Single source of truth for the app's JSON instance.
*
* All serialization/deserialization — Retrofit, DataStore caches,
* and WebSocket message parsing — must use this instance so that
@@ -1,7 +1,6 @@
package com.bitcointxoko.gudariwallet.api
import com.bitcointxoko.gudariwallet.security.SecretStore
import com.bitcointxoko.gudariwallet.util.WalletConstants
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -13,7 +12,6 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
class LNbitsClient(secrets: SecretStore) {
@@ -38,8 +36,6 @@ class LNbitsClient(secrets: SecretStore) {
}
}
// Single shared client — used by both Retrofit and WebSocket
val httpClient: OkHttpClient get() = LNbitsHttpClient.instance
val api: LNbitsApi by lazy { buildApi() }
private fun buildApi(): LNbitsApi {
@@ -251,7 +251,7 @@ data class SuccessAction(
* - a plain string "Thanks!"
* - null
*
* kotlinx.serialization's default deserialiser throws when it sees a
* kotlinx.serialization's default deserializer throws when it sees a
* string where it expects {. This serializer handles all three cases.
*
* Applied via @Serializable(with = SuccessActionSerializer::class) on
@@ -337,10 +337,8 @@ class LNbitsWalletRepository(
request : NwcRegistrationRequest
): NwcGetResponse {
Timber.d(
"NWC [REGISTER ] pubkey=${pubkey.take(16)}" +
"permissions=${request.permissions} " +
"expires_at=${request.expires_at} " +
"budgets=${request.budgets.size}"
"%snull", "NWC [REGISTER ] pubkey=${pubkey.take(16)}" +
"permissions=${request.permissions} "
)
val result = api.registerNwcKey(
adminKey = secrets.adminKey(),
@@ -38,8 +38,7 @@ class LightningAddressStore(private val context: Context) {
.map { prefs ->
prefs[KEY_ADDRESS].also { address ->
_currentAddress = address
Timber.d("LNADDR [CACHE ] read from cache: " +
if (address != null) "\"$address\"" else "(empty — nothing cached yet)") }
Timber.d("LNADDR [CACHE ] read from cache: %s", if (address != null) "\"$address\"" else "(empty — nothing cached yet)") }
}
suspend fun isCacheStale(): Boolean {
@@ -53,9 +52,8 @@ class LightningAddressStore(private val context: Context) {
val fetchedAt = prefs[KEY_FETCHED_AT] ?: 0L
val age = System.currentTimeMillis() - fetchedAt
val isStale = address == null || age > LNADDRESS_TTL_MS
Timber.d("LNADDR [TTL ] cached=${address != null} " +
"age=${age / 1000}s ttl=${LNADDRESS_TTL_MS / 1000}s " +
if (isStale) "→ cache stale, will fetch from API"
Timber.d("%s%s", "LNADDR [TTL ] cached=${address != null} " +
"age=${age / 1000}s ttl=${LNADDRESS_TTL_MS / 1000}s ", if (isStale) "→ cache stale, will fetch from API"
else "→ cache fresh, skipping API call")
isStale
}
@@ -39,7 +39,7 @@ class LnurlCacheRepository(context: Context) {
return null
}
val response = deserialise(entity.responseJson) ?: run {
val response = deserialize(entity.responseJson) ?: run {
Timber.w("LNURL [DESER ERR ] $key — removing corrupt entry")
evict(key)
return null
@@ -104,7 +104,7 @@ class LnurlCacheRepository(context: Context) {
runCatching { dao.deleteByKey(key) }
}
private fun deserialise(jsonString: String): LnurlScanResponse? =
private fun deserialize(jsonString: String): LnurlScanResponse? =
runCatching { json.decodeFromString(LnurlScanResponse.serializer(), jsonString) }
.getOrNull()
}
@@ -1,20 +1,15 @@
package com.bitcointxoko.gudariwallet.security
import android.content.Context
import timber.log.Timber
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.migrations.SharedPreferencesMigration
import androidx.datastore.migrations.SharedPreferencesView
import com.bitcointxoko.gudariwallet.util.WalletConstants
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import java.io.File
import androidx.core.content.edit
/**
* Singleton DataStore for encrypted credentials.
@@ -27,8 +22,6 @@ import androidx.core.content.edit
* [cleanUpLegacyPrefs] once you've confirmed migration succeeded.
*/
object CredentialsDataStore {
private const val TAG = "CredentialsDataStore"
@Volatile
private var instance: DataStore<Credentials>? = null
@@ -25,8 +25,8 @@ class CredentialsSerializer(
Timber.d("Credentials decrypted successfully — isOnboarded: ${result.isOnboarded}")
result
} catch (e: Exception) {
Timber.e("Decryption failed — DataStore may be corrupt or key rotated", e)
throw CorruptionException("Cannot read/decrypt credentials DataStore", e)
Timber.e("Decryption failed — DataStore may be corrupt or key rotated")
throw CorruptionException("Cannot read/decrypt credentials DataStore")
}
}
@@ -37,12 +37,10 @@ class CredentialsSerializer(
val encryptingStream = streamingAead.newEncryptingStream(output, AAD)
t.writeTo(encryptingStream) // returns Int — discarded by Unit return type
encryptingStream.close()
Timber.d("Credentials encrypted and written — isOnboarded: ${t.isOnboarded}, " +
"hasInvoiceKey: ${t.invoiceKey.isNotBlank()}, " +
"hasAdminKey: ${t.adminKey.isNotBlank()}, " +
"hasBaseUrl: ${t.baseUrl.isNotBlank()}")
Timber.d("%snull", "Credentials encrypted and written — isOnboarded: ${t.isOnboarded}, " +
"hasInvoiceKey: ${t.invoiceKey.isNotBlank()}, ")
} catch (e: Exception) {
Timber.e("Encryption/write failed", e)
Timber.e("Encryption/write failed")
throw e
}
}
@@ -38,7 +38,7 @@ object CredentialsTinkManager {
Timber.i("StreamingAead primitive acquired successfully")
}
} catch (e: Exception) {
Timber.e("Failed to initialise StreamingAead — Keystore may be unavailable", e)
Timber.e("Failed to initialise StreamingAead — Keystore may be unavailable")
throw e
}
}
@@ -41,7 +41,7 @@ class NodeAliasService(
repo.put(pubkey, alias)
// 4. Publish to StateFlow
_aliases.value = _aliases.value + (pubkey to alias)
_aliases.value += (pubkey to alias)
Timber.d("ALIAS [FETCHED ] $pubkey\"$alias\"")
return alias
@@ -30,6 +30,7 @@ import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import kotlin.time.Duration.Companion.milliseconds
private const val TAG = "WalletNotifService"
@@ -271,7 +272,7 @@ class WalletNotificationService : Service() {
reconnectJob?.cancel()
reconnectJob = serviceScope.launch {
delay(backoff)
delay(backoff.milliseconds)
openWebSocket()
}
}
@@ -71,6 +71,7 @@ import com.bitcointxoko.gudariwallet.ui.send.SendScreen
import com.bitcointxoko.gudariwallet.ui.send.SendStrings
import com.bitcointxoko.gudariwallet.util.SendInputType
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.milliseconds
// ── Tab destinations ──────────────────────────────────────────────────────────
// Note: label and icon are now resolved at the call site via LocalAppStrings,
@@ -205,7 +206,7 @@ fun WalletScreen(
LaunchedEffect(clipboardOffer) {
if (clipboardOffer is ClipboardOfferState.Detected) {
delay(8_000)
delay(8_000.milliseconds)
vm.dismissClipboardOffer()
}
}
@@ -215,7 +216,7 @@ fun WalletScreen(
LaunchedEffect(nfcOffer) {
if (nfcOffer is NfcOfferState.Detected) {
delay(8_000)
delay(8_000.milliseconds)
nfcVm.dismissNfcOffer()
}
}
@@ -13,7 +13,6 @@ import com.bitcointxoko.gudariwallet.data.LightningAddressStore
import com.bitcointxoko.gudariwallet.data.LnurlCacheRepository
import com.bitcointxoko.gudariwallet.data.NwcCacheRepository
import com.bitcointxoko.gudariwallet.data.PaymentCacheRepository
import com.bitcointxoko.gudariwallet.data.db.AppDatabase
import com.bitcointxoko.gudariwallet.security.EncryptedSecretStore
import com.bitcointxoko.gudariwallet.service.NodeAliasService
import com.bitcointxoko.gudariwallet.ui.balance.BalanceViewModel
@@ -1,9 +1,8 @@
package com.bitcointxoko.gudariwallet.ui.common
import android.app.Activity
import android.view.WindowManager
import androidx.activity.compose.LocalActivity
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
/**
* Manages screen brightness for QR-display screens.
@@ -12,12 +11,12 @@ import androidx.compose.ui.platform.LocalContext
*/
@Composable
fun rememberBrightnessController(): BrightnessController {
val window = (LocalContext.current as? Activity)?.window
val window = (LocalActivity.current)?.window
var isOn by remember { mutableStateOf(false) }
// Sync side-effect: no coroutine needed
// Sync side effect: no coroutine needed
SideEffect {
window?.attributes = window?.attributes?.apply {
window?.attributes = window.attributes?.apply {
screenBrightness = if (isOn) WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
else WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
}
@@ -25,7 +24,7 @@ fun rememberBrightnessController(): BrightnessController {
DisposableEffect(Unit) {
onDispose {
window?.attributes = window?.attributes?.apply {
window?.attributes = window.attributes?.apply {
screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
}
}
@@ -23,7 +23,7 @@ object IntentUtils {
* Returns a raw payment URI string if the intent carries one, else null.
*
* Handles:
* - ACTION_VIEW with a recognised scheme (BTCPay "Pay in wallet", browser links)
* - ACTION_VIEW with a recognized scheme (BTCPay "Pay in wallet", browser links)
* - ACTION_SEND with text/plain (share sheet from another app)
*/
fun extractPaymentUri(intent: Intent?): String? {
@@ -40,7 +40,7 @@ object LnurlMetadataParser {
/**
* Returns the display label for a LNURL payment target.
* If [lnurl] is a Lightning Address (contains '@'), returns it as-is.
* Otherwise returns the [domain] of the LNURL endpoint.
* Otherwise, returns the [domain] of the LNURL endpoint.
*/
fun payingToLabel(lnurl: String, domain: String): String =
if ('@' in lnurl) lnurl else domain