@@ -11,14 +11,30 @@ import kotlinx.coroutines.CoroutineScope
1111import kotlinx.coroutines.Dispatchers
1212import kotlinx.coroutines.SupervisorJob
1313import kotlinx.coroutines.cancel
14+ import kotlinx.coroutines.delay
15+ import kotlinx.coroutines.flow.first
1416import kotlinx.coroutines.launch
17+ import org.lightningdevkit.ldknode.Event
1518import to.bitkit.App
1619import to.bitkit.R
20+ import to.bitkit.data.SettingsData
21+ import to.bitkit.data.SettingsStore
22+ import to.bitkit.models.BITCOIN_SYMBOL
23+ import to.bitkit.models.NewTransactionSheetDetails
24+ import to.bitkit.models.NewTransactionSheetDirection
25+ import to.bitkit.models.NewTransactionSheetType
26+ import to.bitkit.models.PrimaryDisplay
27+ import to.bitkit.models.formatToModernDisplay
28+ import to.bitkit.repositories.ActivityRepo
29+ import to.bitkit.repositories.CurrencyRepo
1730import to.bitkit.repositories.LightningRepo
1831import to.bitkit.repositories.WalletRepo
32+ import to.bitkit.services.LdkNodeEventBus
1933import to.bitkit.ui.MainActivity
34+ import to.bitkit.ui.pushNotification
2035import to.bitkit.utils.Logger
2136import javax.inject.Inject
37+ import kotlin.time.Duration.Companion.seconds
2238
2339@AndroidEntryPoint
2440class LightningNodeService : Service () {
@@ -31,6 +47,18 @@ class LightningNodeService : Service() {
3147 @Inject
3248 lateinit var walletRepo: WalletRepo
3349
50+ @Inject
51+ lateinit var ldkNodeEventBus: LdkNodeEventBus
52+
53+ @Inject
54+ lateinit var settingsStore: SettingsStore
55+
56+ @Inject
57+ lateinit var activityRepo: ActivityRepo
58+
59+ @Inject
60+ lateinit var currencyRepo: CurrencyRepo
61+
3462 override fun onCreate () {
3563 super .onCreate()
3664 startForeground(NOTIFICATION_ID , createNotification())
@@ -53,12 +81,76 @@ class LightningNodeService : Service() {
5381 walletRepo.syncBalances()
5482 }
5583 }
84+
85+ launch {
86+ ldkNodeEventBus.events.collect { event ->
87+ handleBackgroundEvent(event)
88+ }
89+ }
5690 }
5791 }
5892
59- // Update the createNotification method in LightningNodeService.kt
93+ private suspend fun handleBackgroundEvent (event : Event ) {
94+ delay(0.5 .seconds) // Small delay to allow lifecycle callbacks to settle after app backgrounding
95+ if (App .currentActivity?.value != null ) return
96+
97+ when (event) {
98+ is Event .PaymentReceived -> {
99+ val sats = event.amountMsat / 1000u
100+ showPaymentNotification(sats.toLong(), event.paymentHash, isOnchain = false )
101+ }
102+
103+ is Event .OnchainTransactionReceived -> {
104+ val sats = event.details.amountSats
105+ val shouldShow = activityRepo.shouldShowReceivedSheet(event.txid, sats.toULong())
106+ if (shouldShow) {
107+ showPaymentNotification(sats, event.txid, isOnchain = true )
108+ }
109+ }
110+
111+ else -> Unit
112+ }
113+ }
114+
115+ private suspend fun showPaymentNotification (sats : Long , paymentHashOrTxId : String? , isOnchain : Boolean ) {
116+ if (App .currentActivity?.value != null ) return
117+
118+ val settings = settingsStore.data.first()
119+ val type = if (isOnchain) NewTransactionSheetType .ONCHAIN else NewTransactionSheetType .LIGHTNING
120+ val direction = NewTransactionSheetDirection .RECEIVED
121+
122+ NewTransactionSheetDetails .save(
123+ this ,
124+ NewTransactionSheetDetails (type, direction, paymentHashOrTxId, sats)
125+ )
126+
127+ val title = getString(R .string.notification_received_title)
128+ val body = if (settings.showNotificationDetails) {
129+ formatNotificationAmount(sats, settings)
130+ } else {
131+ getString(R .string.notification_received_body_hidden)
132+ }
133+
134+ pushNotification(title, body, context = this )
135+ }
136+
137+ private fun formatNotificationAmount (sats : Long , settings : SettingsData ): String {
138+ val converted = currencyRepo.convertSatsToFiat(sats).getOrNull()
139+
140+ val amountText = converted?.let {
141+ val btcDisplay = it.bitcoinDisplay(settings.displayUnit)
142+ if (settings.primaryDisplay == PrimaryDisplay .BITCOIN ) {
143+ " ${btcDisplay.symbol} ${btcDisplay.value} (${it.symbol}${it.formatted} )"
144+ } else {
145+ " ${it.symbol}${it.formatted} (${btcDisplay.symbol} ${btcDisplay.value} )"
146+ }
147+ } ? : " $BITCOIN_SYMBOL ${sats.formatToModernDisplay()} "
148+
149+ return getString(R .string.notification_received_body_amount, amountText)
150+ }
151+
60152 private fun createNotification (
61- contentText : String = " Bitkit is running in background so you can receive Lightning payments"
153+ contentText : String = getString( R .string.notification_running_in_background),
62154 ): Notification {
63155 val notificationIntent = Intent (this , MainActivity ::class .java).apply {
64156 flags = Intent .FLAG_ACTIVITY_CLEAR_TOP or Intent .FLAG_ACTIVITY_SINGLE_TOP
@@ -88,7 +180,7 @@ class LightningNodeService : Service() {
88180 .setContentIntent(pendingIntent)
89181 .addAction(
90182 R .drawable.ic_x,
91- " Stop App " , // TODO: Get from resources
183+ getString( R .string.notification_stop_app),
92184 stopPendingIntent
93185 )
94186 .build()
0 commit comments