From 0752b55306b9fb4d8eb2c5e3753b30e60bbcea5e Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 13:48:08 +0100 Subject: [PATCH 01/13] fix: lighting node service test mocks --- .../LightningNodeServiceTest.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt index 200705b97..c06f201cc 100644 --- a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt +++ b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt @@ -7,11 +7,13 @@ import android.app.Notification import android.app.NotificationManager import android.content.Context import androidx.test.core.app.ApplicationProvider +import com.google.firebase.messaging.FirebaseMessaging import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.UninstallModules +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.runBlocking import org.junit.After @@ -23,7 +25,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.lightningdevkit.ldknode.Event -import org.mockito.kotlin.KArgumentCaptor import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor @@ -42,6 +43,7 @@ import to.bitkit.data.AppCacheData import to.bitkit.data.CacheStore import to.bitkit.di.DbModule import to.bitkit.di.DispatchersModule +import to.bitkit.di.ViewModelModule import to.bitkit.domain.commands.NotifyPaymentReceived import to.bitkit.domain.commands.NotifyPaymentReceivedHandler import to.bitkit.models.NewTransactionSheetDetails @@ -52,9 +54,10 @@ import to.bitkit.repositories.LightningRepo import to.bitkit.repositories.WalletRepo import to.bitkit.services.NodeEventHandler import to.bitkit.test.BaseUnitTest +import to.bitkit.ui.shared.toast.ToastQueueManager @HiltAndroidTest -@UninstallModules(DispatchersModule::class, DbModule::class) +@UninstallModules(DispatchersModule::class, DbModule::class, ViewModelModule::class) @Config(application = HiltTestApplication::class, sdk = [34]) // Pin Robolectric to an SDK that supports Java 17 @RunWith(RobolectricTestRunner::class) class LightningNodeServiceTest : BaseUnitTest() { @@ -62,6 +65,12 @@ class LightningNodeServiceTest : BaseUnitTest() { @get:Rule(order = 1) var hiltRule = HiltAndroidRule(this) + @BindValue + val firebaseMessaging = mock() + + @BindValue + val toastManagerProvider = mock<(CoroutineScope) -> ToastQueueManager>() + @BindValue val lightningRepo = mock() @@ -74,8 +83,8 @@ class LightningNodeServiceTest : BaseUnitTest() { @BindValue val cacheStore = mock() - private val captor: KArgumentCaptor = argumentCaptor() - private val cacheDataFlow = MutableSharedFlow(replay = 1) + private val captor = argumentCaptor() + private val cacheData = MutableSharedFlow(replay = 1) private val context = ApplicationProvider.getApplicationContext() @Before @@ -95,7 +104,7 @@ class LightningNodeServiceTest : BaseUnitTest() { whenever(lightningRepo.stop()).thenReturn(Result.success(Unit)) // Set up CacheStore mock - whenever(cacheStore.data).thenReturn(cacheDataFlow) + whenever(cacheStore.data).thenReturn(cacheData) // Mock NotifyPaymentReceivedHandler to return ShowNotification result val sheet = NewTransactionSheetDetails( From 6f601782d92252ad901629ab4e61d54ade26bb87 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:11:29 +0100 Subject: [PATCH 02/13] chore: update ldk-node and bitkit-core --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bd7612931..faddc4294 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ activity-compose = { module = "androidx.activity:activity-compose", version = "1 appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" } barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version = "17.3.0" } biometric = { module = "androidx.biometric:biometric", version = "1.4.0-alpha05" } -bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.1.35" } +bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.1.37" } bouncycastle-provider-jdk = { module = "org.bouncycastle:bcprov-jdk18on", version = "1.83" } camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" } camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" } @@ -58,7 +58,7 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } -ldk-node-android = { module = "com.github.synonymdev:ldk-node", version = "v0.7.0-rc.8" } # fork | local: remove `v` +ldk-node-android = { module = "com.github.synonymdev:ldk-node", version = "v0.7.0-rc.17" } # fork | local: remove `v` lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycle" } lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } From 9b7928c5cae4120e74506d7ed7dc76f0d4d138d1 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:13:06 +0100 Subject: [PATCH 03/13] chore: remove lowercase workaround --- app/src/main/java/to/bitkit/services/CoreService.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/services/CoreService.kt b/app/src/main/java/to/bitkit/services/CoreService.kt index f890cbc7b..4f82a9ece 100644 --- a/app/src/main/java/to/bitkit/services/CoreService.kt +++ b/app/src/main/java/to/bitkit/services/CoreService.kt @@ -186,10 +186,8 @@ class CoreService @Inject constructor( com.synonym.bitkitcore.isAddressUsed(address = address) } - @Suppress("ForbiddenComment") suspend fun decode(input: String): Scanner = ServiceQueue.CORE.background { - // TODO: Remove lowercase workaround when https://github.com/synonymdev/bitkit-core/issues/66 is fixed - com.synonym.bitkitcore.decode(input.lowercase()) + com.synonym.bitkitcore.decode(input) } companion object { From a27556d462167b13fe4018fba9d7fc8fdd48a0e2 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:19:15 +0100 Subject: [PATCH 04/13] fix: use ldk-node-android rc.17 --- app/src/main/java/to/bitkit/ext/ChannelDetails.kt | 1 + .../to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt | 2 ++ gradle/libs.versions.toml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt index 00441336b..5a5a39cf5 100644 --- a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt +++ b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt @@ -78,6 +78,7 @@ fun createChannelDetails(): ChannelDetails { forceCloseSpendDelay = null, inboundHtlcMinimumMsat = 0u, inboundHtlcMaximumMsat = null, + claimableOnCloseSats = 0u, config = ChannelConfig( forwardingFeeProportionalMillionths = 0u, forwardingFeeBaseMsat = 0u, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt index d4101feb0..59bbaa6c0 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt @@ -694,6 +694,7 @@ private fun PreviewAutoMode() { forceCloseSpendDelay = null, inboundHtlcMinimumMsat = 0uL, inboundHtlcMaximumMsat = null, + claimableOnCloseSats = 0uL, config = org.lightningdevkit.ldknode.ChannelConfig( forwardingFeeProportionalMillionths = 0u, forwardingFeeBaseMsat = 0u, @@ -764,6 +765,7 @@ private fun PreviewSpendingMode() { forceCloseSpendDelay = null, inboundHtlcMinimumMsat = 0uL, inboundHtlcMaximumMsat = null, + claimableOnCloseSats = 0uL, config = org.lightningdevkit.ldknode.ChannelConfig( forwardingFeeProportionalMillionths = 0u, forwardingFeeBaseMsat = 0u, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index faddc4294..831bdcdd9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -58,7 +58,7 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } -ldk-node-android = { module = "com.github.synonymdev:ldk-node", version = "v0.7.0-rc.17" } # fork | local: remove `v` +ldk-node-android = { module = "com.github.synonymdev.ldk-node:ldk-node-android", version = "v0.7.0-rc.17" } lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycle" } lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } From 0b749c8b7422345158a1cb999afb9c51ccccf464 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:20:58 +0100 Subject: [PATCH 05/13] chore: add todo to use claimableOnCloseSats --- app/src/main/java/to/bitkit/ext/ChannelDetails.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt index 5a5a39cf5..a8995e2e4 100644 --- a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt +++ b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt @@ -14,7 +14,9 @@ import org.lightningdevkit.ldknode.MaxDustHtlcExposure * - outbound_capacity: What we can spend now over Lightning * - our_reserve: Our reserve that we get back on close */ +@Suppress("ForbiddenComment") val ChannelDetails.amountOnClose: ULong + // TODO: use channelDetails.claimableOnCloseSats get() { val outboundCapacitySat = this.outboundCapacityMsat / 1000u val ourReserve = this.unspendablePunishmentReserve ?: 0u From 9f62d140f15519f85f6e3413ad9651a03b95f9e0 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:21:59 +0100 Subject: [PATCH 06/13] chore: add todo to use claimableOnCloseSats --- .../main/java/to/bitkit/ext/ChannelDetails.kt | 108 ++++++++---------- 1 file changed, 49 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt index a8995e2e4..fc24e195d 100644 --- a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt +++ b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt @@ -25,69 +25,59 @@ val ChannelDetails.amountOnClose: ULong } /** Returns only `open` channels, filtering out pending ones. */ -fun List.filterOpen(): List { - return this.filter { it.isChannelReady } -} +fun List.filterOpen(): List = this.filter { it.isChannelReady } /** Returns only `pending` channels. */ -fun List.filterPending(): List { - return this.filterNot { it.isChannelReady } -} +fun List.filterPending(): List = this.filterNot { it.isChannelReady } /** Returns a limit in sats as close as possible to the HTLC limit we can currently send. */ -fun List?.totalNextOutboundHtlcLimitSats(): ULong { - return this?.filter { it.isUsable } - ?.sumOf { it.nextOutboundHtlcLimitMsat / 1000u } - ?: 0u -} +fun List?.totalNextOutboundHtlcLimitSats(): ULong = this?.filter { it.isUsable } + ?.sumOf { it.nextOutboundHtlcLimitMsat / 1000u } + ?: 0u /** Calculates the total remote balance (inbound capacity) from open channels. */ -fun List.calculateRemoteBalance(): ULong { - return this - .filterOpen() - .sumOf { it.inboundCapacityMsat / 1000u } -} +fun List.calculateRemoteBalance(): ULong = this + .filterOpen() + .sumOf { it.inboundCapacityMsat / 1000u } -fun createChannelDetails(): ChannelDetails { - return ChannelDetails( - channelId = "channelId", - counterpartyNodeId = "counterpartyNodeId", - fundingTxo = null, - shortChannelId = null, - outboundScidAlias = null, - inboundScidAlias = null, - channelValueSats = 0u, - unspendablePunishmentReserve = null, - userChannelId = "0", - feerateSatPer1000Weight = 0u, - outboundCapacityMsat = 0u, - inboundCapacityMsat = 0u, - confirmationsRequired = null, - confirmations = null, - isOutbound = false, - isChannelReady = false, - isUsable = false, - isAnnounced = false, - cltvExpiryDelta = null, - counterpartyUnspendablePunishmentReserve = 0u, - counterpartyOutboundHtlcMinimumMsat = null, - counterpartyOutboundHtlcMaximumMsat = null, - counterpartyForwardingInfoFeeBaseMsat = null, - counterpartyForwardingInfoFeeProportionalMillionths = null, - counterpartyForwardingInfoCltvExpiryDelta = null, - nextOutboundHtlcLimitMsat = 0u, - nextOutboundHtlcMinimumMsat = 0u, - forceCloseSpendDelay = null, - inboundHtlcMinimumMsat = 0u, - inboundHtlcMaximumMsat = null, - claimableOnCloseSats = 0u, - config = ChannelConfig( - forwardingFeeProportionalMillionths = 0u, - forwardingFeeBaseMsat = 0u, - cltvExpiryDelta = 0u, - maxDustHtlcExposure = MaxDustHtlcExposure.FixedLimit(limitMsat = 0u), - forceCloseAvoidanceMaxFeeSatoshis = 0u, - acceptUnderpayingHtlcs = false, - ), - ) -} +fun createChannelDetails(): ChannelDetails = ChannelDetails( + channelId = "channelId", + counterpartyNodeId = "counterpartyNodeId", + fundingTxo = null, + shortChannelId = null, + outboundScidAlias = null, + inboundScidAlias = null, + channelValueSats = 0u, + unspendablePunishmentReserve = null, + userChannelId = "0", + feerateSatPer1000Weight = 0u, + outboundCapacityMsat = 0u, + inboundCapacityMsat = 0u, + confirmationsRequired = null, + confirmations = null, + isOutbound = false, + isChannelReady = false, + isUsable = false, + isAnnounced = false, + cltvExpiryDelta = null, + counterpartyUnspendablePunishmentReserve = 0u, + counterpartyOutboundHtlcMinimumMsat = null, + counterpartyOutboundHtlcMaximumMsat = null, + counterpartyForwardingInfoFeeBaseMsat = null, + counterpartyForwardingInfoFeeProportionalMillionths = null, + counterpartyForwardingInfoCltvExpiryDelta = null, + nextOutboundHtlcLimitMsat = 0u, + nextOutboundHtlcMinimumMsat = 0u, + forceCloseSpendDelay = null, + inboundHtlcMinimumMsat = 0u, + inboundHtlcMaximumMsat = null, + claimableOnCloseSats = 0u, + config = ChannelConfig( + forwardingFeeProportionalMillionths = 0u, + forwardingFeeBaseMsat = 0u, + cltvExpiryDelta = 0u, + maxDustHtlcExposure = MaxDustHtlcExposure.FixedLimit(limitMsat = 0u), + forceCloseAvoidanceMaxFeeSatoshis = 0u, + acceptUnderpayingHtlcs = false, + ), +) From aeadbe9fd4ba4a24d22ca44dc5a8610967d3bec2 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:23:19 +0100 Subject: [PATCH 07/13] chore: update deps --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 831bdcdd9..b9e440b8d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,7 +41,7 @@ core-splashscreen = { group = "androidx.core", name = "core-splashscreen", versi datastore-preferences = { module = "androidx.datastore:datastore-preferences", version = "1.2.0" } detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } detekt-compose-rules = { module = "io.nlopez.compose.rules:detekt", version = "0.5.3" } -firebase-bom = { module = "com.google.firebase:firebase-bom", version = "34.7.0" } +firebase-bom = { module = "com.google.firebase:firebase-bom", version = "34.8.0" } firebase-messaging = { module = "com.google.firebase:firebase-messaging" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } @@ -79,8 +79,8 @@ test-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines- test-espresso-core = { module = "androidx.test.espresso:espresso-core", version = "3.7.0" } test-junit = { group = "junit", name = "junit", version = "4.13.2" } test-junit-ext = { module = "androidx.test.ext:junit", version = "1.3.0" } -test-mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "6.1.0" } -test-robolectric = { module = "org.robolectric:robolectric", version = "4.16" } +test-mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "6.2.2" } +test-robolectric = { module = "org.robolectric:robolectric", version = "4.16.1" } test-turbine = { group = "app.cash.turbine", name = "turbine", version = "1.2.1" } vss-client = { module = "com.synonym:vss-client-android", version = "0.4.0" } work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version = "2.11.0" } From 79fe1d944cb35f9030891cf211b3308108e5b38e Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:28:11 +0100 Subject: [PATCH 08/13] chore: update bitkit-core --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b9e440b8d..f0021b9ef 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ activity-compose = { module = "androidx.activity:activity-compose", version = "1 appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" } barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version = "17.3.0" } biometric = { module = "androidx.biometric:biometric", version = "1.4.0-alpha05" } -bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.1.37" } +bitkit-core = { module = "com.synonym:bitkit-core-android", version = "0.1.38" } bouncycastle-provider-jdk = { module = "org.bouncycastle:bcprov-jdk18on", version = "1.83" } camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" } camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" } From 3a98c0ec8bcc6052b1a15c69bfa56c9653b829ce Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:36:41 +0100 Subject: [PATCH 09/13] chore: lint --- app/src/main/java/to/bitkit/ext/ChannelDetails.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt index fc24e195d..4e1898d85 100644 --- a/app/src/main/java/to/bitkit/ext/ChannelDetails.kt +++ b/app/src/main/java/to/bitkit/ext/ChannelDetails.kt @@ -14,10 +14,10 @@ import org.lightningdevkit.ldknode.MaxDustHtlcExposure * - outbound_capacity: What we can spend now over Lightning * - our_reserve: Our reserve that we get back on close */ -@Suppress("ForbiddenComment") val ChannelDetails.amountOnClose: ULong - // TODO: use channelDetails.claimableOnCloseSats + @Suppress("ForbiddenComment") get() { + // TODO: use channelDetails.claimableOnCloseSats val outboundCapacitySat = this.outboundCapacityMsat / 1000u val ourReserve = this.unspendablePunishmentReserve ?: 0u From dee14a98160c145f9387571a4ac6ea02e9b79c7c Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:47:21 +0100 Subject: [PATCH 10/13] fix: adapt lnurl channel to new bitkit-core impl --- .../to/bitkit/repositories/LightningRepo.kt | 2 -- .../java/to/bitkit/services/LnurlService.kt | 23 ------------------- .../external/LnurlChannelViewModel.kt | 18 ++++----------- 3 files changed, 5 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/LightningRepo.kt b/app/src/main/java/to/bitkit/repositories/LightningRepo.kt index aacbd5c29..f44ee9eec 100644 --- a/app/src/main/java/to/bitkit/repositories/LightningRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/LightningRepo.kt @@ -659,8 +659,6 @@ class LightningRepo @Inject constructor( lnurlService.requestLnurlWithdraw(callbackUrl) } - suspend fun fetchLnurlChannelInfo(url: String) = lnurlService.fetchLnurlChannelInfo(url) - suspend fun requestLnurlChannel( k1: String, callback: String, diff --git a/app/src/main/java/to/bitkit/services/LnurlService.kt b/app/src/main/java/to/bitkit/services/LnurlService.kt index ae7e2e325..b5bc799ce 100644 --- a/app/src/main/java/to/bitkit/services/LnurlService.kt +++ b/app/src/main/java/to/bitkit/services/LnurlService.kt @@ -63,21 +63,6 @@ class LnurlService @Inject constructor( return@runCatching response.body() } - suspend fun fetchLnurlChannelInfo(url: String): Result = runCatching { - Logger.debug("Fetching LNURL channel info from: $url", context = TAG) - - val response: HttpResponse = client.get(url) - Logger.debug("Http call: $response", context = TAG) - - if (!response.status.isSuccess()) { - throw HttpError("fetchLnurlChannelInfo error: '${response.status.description}'", response.status.value) - } - - return@runCatching response.body() - }.onFailure { - Logger.warn("Failed to fetch channel info", it, context = TAG) - } - suspend fun requestLnurlChannel(url: String): Result = runCatching { Logger.debug("Requesting LNURL channel request via: '$url'", context = TAG) @@ -127,11 +112,3 @@ data class LnurlChannelResponse( val status: String? = null, val reason: String? = null, ) - -@Serializable -data class LnurlChannelInfoResponse( - val uri: String, - val tag: String, - val callback: String, - val k1: String, -) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/LnurlChannelViewModel.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/LnurlChannelViewModel.kt index 52401cc90..53dec2fe4 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/LnurlChannelViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/LnurlChannelViewModel.kt @@ -36,19 +36,11 @@ class LnurlChannelViewModel @Inject constructor( private fun fetchChannelInfo() { viewModelScope.launch { - lightningRepo.fetchLnurlChannelInfo(params.uri) - .onSuccess { channelInfo -> - val peer = runCatching { PeerDetails.of(channelInfo.uri) }.getOrElse { - errorToast(it) - return@onSuccess - } - _uiState.update { it.copy(peer = peer) } - } - .onFailure { error -> - val message = context.getString(R.string.other__lnurl_channel_error_raw) - .replace("{raw}", error.message.orEmpty()) - errorToast(Exception(message)) - } + val peer = runCatching { PeerDetails.of(params.uri) }.getOrElse { + errorToast(it) + return@launch + } + _uiState.update { it.copy(peer = peer) } } } From 0bb8796e31f0100230016c6b0204e5aed1f711df Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 15:49:50 +0100 Subject: [PATCH 11/13] refactor: use android log in cleanup --- app/src/main/java/to/bitkit/utils/Logger.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/to/bitkit/utils/Logger.kt b/app/src/main/java/to/bitkit/utils/Logger.kt index 7064710e7..543937557 100644 --- a/app/src/main/java/to/bitkit/utils/Logger.kt +++ b/app/src/main/java/to/bitkit/utils/Logger.kt @@ -239,14 +239,18 @@ class LogSaverImpl( } } - private fun log(message: String, level: LogLevel = LogLevel.INFO) { + private fun log( + message: String, + level: LogLevel = LogLevel.INFO, + androidLog: (String, String) -> Unit = { tag, msg -> Log.i(tag, msg) }, + ) { val formatted = formatLog(level, message, TAG, getCallerPath(), getCallerLine()) - Log.i(APP, formatted) + androidLog(APP, formatted) save(formatted) } private fun cleanupOldLogFiles(maxTotalSizeMB: Int = 20) { - log("Deleting old log files…", LogLevel.VERBOSE) + log("Deleting old log files…", LogLevel.VERBOSE, Log::v) val logDir = runCatching { Env.logDir }.getOrNull() ?: return val logFiles = logDir @@ -263,15 +267,15 @@ class LogSaverImpl( if (totalSize <= maxSizeBytes) return runCatching { - Log.d(APP, "Deleting old log file: '${file.name}'") + log("Deleting old log file: '${file.name}'", LogLevel.DEBUG, Log::d) if (file.delete()) { totalSize -= file.length() } }.onFailure { - Log.w(APP, "Failed to delete old log file: '${file.name}'", it) + log("Failed to delete old log file: '${file.name}'", LogLevel.WARN, Log::w) } } - Log.v(APP, "Deleted all old log files.") + log("Deleted all old log files.", LogLevel.VERBOSE, Log::v) } companion object { From 2f18adcd893ef6f1b7471c5c6bca8eb143982793 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 17:12:47 +0100 Subject: [PATCH 12/13] chore: update tests --- .../LightningNodeServiceTest.kt | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt index c06f201cc..2d7d7b7b0 100644 --- a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt +++ b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt @@ -27,9 +27,10 @@ import org.junit.runner.RunWith import org.lightningdevkit.ldknode.Event import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull -import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock import org.mockito.kotlin.never +import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.robolectric.Robolectric @@ -83,24 +84,30 @@ class LightningNodeServiceTest : BaseUnitTest() { @BindValue val cacheStore = mock() - private val captor = argumentCaptor() + private var capturedHandler: NodeEventHandler? = null private val cacheData = MutableSharedFlow(replay = 1) private val context = ApplicationProvider.getApplicationContext() @Before fun setUp() = runBlocking { hiltRule.inject() - whenever( - lightningRepo.start( - any(), - anyOrNull(), - any(), - anyOrNull(), - anyOrNull(), - captor.capture(), - anyOrNull(), - ) - ).thenReturn(Result.success(Unit)) + lightningRepo.stub { + onBlocking { + start( + any(), + anyOrNull(), + any(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + ) + } doAnswer { + @Suppress("UNCHECKED_CAST") + capturedHandler = it.getArgument(5) as? NodeEventHandler + Result.success(Unit) + } + } whenever(lightningRepo.stop()).thenReturn(Result.success(Unit)) // Set up CacheStore mock @@ -139,7 +146,6 @@ class LightningNodeServiceTest : BaseUnitTest() { controller.create().startCommand(0, 0) testScheduler.advanceUntilIdle() - val capturedHandler = captor.lastValue assertNotNull("Event handler should be captured", capturedHandler) val event = Event.PaymentReceived( @@ -186,7 +192,7 @@ class LightningNodeServiceTest : BaseUnitTest() { customRecords = emptyList() ) - captor.lastValue?.invoke(event) + capturedHandler?.invoke(event) testScheduler.advanceUntilIdle() val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -214,7 +220,7 @@ class LightningNodeServiceTest : BaseUnitTest() { customRecords = emptyList() ) - captor.lastValue?.invoke(event) + capturedHandler?.invoke(event) testScheduler.advanceUntilIdle() val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager From 93103cc00a370b2b676614a8475edd06d90159de Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 26 Jan 2026 17:24:05 +0100 Subject: [PATCH 13/13] chore: cleanup --- .../java/to/bitkit/androidServices/LightningNodeServiceTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt index 2d7d7b7b0..f8c6bd9aa 100644 --- a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt +++ b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt @@ -92,7 +92,7 @@ class LightningNodeServiceTest : BaseUnitTest() { fun setUp() = runBlocking { hiltRule.inject() lightningRepo.stub { - onBlocking { + on { start( any(), anyOrNull(), @@ -103,7 +103,6 @@ class LightningNodeServiceTest : BaseUnitTest() { anyOrNull(), ) } doAnswer { - @Suppress("UNCHECKED_CAST") capturedHandler = it.getArgument(5) as? NodeEventHandler Result.success(Unit) }