Show relay stats in the notifications

This commit is contained in:
greenart7c3
2025-01-15 15:13:04 -03:00
parent 02588a31b7
commit 5e9bd9d5ae
3 changed files with 147 additions and 37 deletions
@@ -0,0 +1,125 @@
package com.greenart7c3.nostrsigner.relays
import android.Manifest
import android.app.Notification
import android.app.NotificationManager
import android.content.pm.PackageManager
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationChannelGroupCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.greenart7c3.nostrsigner.NostrSigner
import com.greenart7c3.nostrsigner.R
import com.vitorpamplona.ammolite.relays.RelayStats
import kotlinx.coroutines.launch
object AmberRelayStats {
var connected = 0
var available = 0
init {
NostrSigner.getInstance().applicationIOScope.launch {
NostrSigner.getInstance().client.relayStatusFlow().collect {
connected = it.connected
available = it.available
}
}
}
private val innerCache = mutableMapOf<String, AmberRelayStat>()
fun createNotification(): Notification {
val channelId = "ServiceChannel"
val group = NotificationChannelGroupCompat.Builder("ServiceGroup")
.setName(NostrSigner.getInstance().getString(R.string.service))
.setDescription(NostrSigner.getInstance().getString(R.string.service_description))
.build()
val channel = NotificationChannelCompat.Builder(channelId, NotificationManager.IMPORTANCE_DEFAULT)
.setName(NostrSigner.getInstance().getString(R.string.service))
.setDescription(NostrSigner.getInstance().getString(R.string.amber_is_running_in_background))
.setSound(null, null)
.setGroup(group.id)
.build()
val notificationManager = NotificationManagerCompat.from(NostrSigner.getInstance())
notificationManager.createNotificationChannelGroup(group)
notificationManager.createNotificationChannel(channel)
val message = NostrSigner.getInstance().client.getAll().joinToString("\n") {
"${it.url} ${if (it.isConnected()) "connected" else "disconnected"}\n" +
"Ping: ${RelayStats.get(it.url).pingInMs}ms" +
if (get(it.url).sent > 0) {
" Sent: ${get(it.url).sent}"
} else {
"" +
if (get(it.url).received > 0) {
" Received: ${get(it.url).received}"
} else {
"" +
if (get(it.url).failed > 0) {
" Rejected by relay: ${get(it.url).failed}"
} else {
"" +
if (RelayStats.get(it.url).errorCounter > 0) " Error: ${RelayStats.get(it.url).errorCounter}" else ""
}
}
}
}
val notificationBuilder =
NotificationCompat.Builder(NostrSigner.getInstance(), channelId)
.setGroup(group.id)
.setContentTitle("$connected of $available connected relays")
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(message),
)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSmallIcon(R.drawable.ic_notification)
return notificationBuilder.build()
}
fun updateNotification() {
Log.d("ConnectivityService", "updateNotification")
val notificationManager = NotificationManagerCompat.from(NostrSigner.getInstance())
if (ActivityCompat.checkSelfPermission(NostrSigner.getInstance(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
notificationManager.notify(1, createNotification())
}
}
fun get(url: String): AmberRelayStat = innerCache.getOrPut(url) { AmberRelayStat() }
fun addSent(url: String) {
get(url).addSent()
updateNotification()
}
fun addReceived(url: String) {
get(url).addReceived()
updateNotification()
}
fun addFailed(url: String) {
get(url).addFailed()
updateNotification()
}
}
class AmberRelayStat(
var sent: Long = 0L,
var received: Long = 0L,
var failed: Long = 0L,
) {
fun addSent() {
sent++
}
fun addReceived() {
received++
}
fun addFailed() {
failed++
}
}
@@ -1,7 +1,5 @@
package com.greenart7c3.nostrsigner.service
import android.app.Notification
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.net.ConnectivityManager
@@ -9,13 +7,9 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationChannelGroupCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.greenart7c3.nostrsigner.BuildConfig
import com.greenart7c3.nostrsigner.NostrSigner
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.relays.AmberRelayStats
import java.util.Timer
import java.util.TimerTask
import kotlinx.coroutines.CoroutineScope
@@ -93,40 +87,13 @@ class ConnectivityService : Service() {
return null!!
}
private fun createNotification(): Notification {
val channelId = "ServiceChannel"
val group = NotificationChannelGroupCompat.Builder("ServiceGroup")
.setName(getString(R.string.service))
.setDescription(getString(R.string.service_description))
.build()
val channel = NotificationChannelCompat.Builder(channelId, NotificationManager.IMPORTANCE_DEFAULT)
.setName(getString(R.string.service))
.setDescription(getString(R.string.amber_is_running_in_background))
.setSound(null, null)
.setGroup(group.id)
.build()
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.createNotificationChannelGroup(group)
notificationManager.createNotificationChannel(channel)
val notificationBuilder =
NotificationCompat.Builder(this, channelId)
.setGroup(group.id)
.setContentTitle(getString(R.string.amber_is_running_in_background))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSmallIcon(R.drawable.ic_notification)
return notificationBuilder.build()
}
override fun onCreate() {
if (isStarted) return
Log.d("ConnectivityService", "onCreate")
isStarted = true
startForeground(1, createNotification())
startForeground(1, AmberRelayStats.createNotification())
@Suppress("KotlinConstantConditions")
if (BuildConfig.FLAVOR != "offline") {
@@ -20,10 +20,12 @@
*/
package com.greenart7c3.nostrsigner.service
import android.util.Log
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.NostrSigner
import com.greenart7c3.nostrsigner.checkNotInMainThread
import com.greenart7c3.nostrsigner.database.LogEntity
import com.greenart7c3.nostrsigner.relays.AmberRelayStats
import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES
import com.vitorpamplona.ammolite.relays.NostrClient
import com.vitorpamplona.ammolite.relays.NostrDataSource
@@ -71,6 +73,8 @@ object NotificationDataSource : NostrDataSource(NostrSigner.getInstance().client
relay: Relay,
subscriptionId: String?,
) {
Log.d("NotificationDataSource", "onRelayStateChange: $type")
AmberRelayStats.updateNotification()
scope.launch {
LocalPreferences.currentAccount(NostrSigner.getInstance())?.let { account ->
NostrSigner.getInstance().getDatabase(account).applicationDao().insertLog(
@@ -125,6 +129,15 @@ object NotificationDataSource : NostrDataSource(NostrSigner.getInstance().client
}
notify(relay, description)
}
override fun onSendResponse(eventId: String, success: Boolean, message: String, relay: Relay) {
if (success) {
AmberRelayStats.addSent(relay.url)
} else {
AmberRelayStats.addFailed(relay.url)
}
super.onSendResponse(eventId, success, message, relay)
}
}
init {
@@ -149,12 +162,16 @@ object NotificationDataSource : NostrDataSource(NostrSigner.getInstance().client
private fun createNotificationsFilter(): TypedFilter {
var since = TimeUtils.now()
val accounts = LocalPreferences.allSavedAccounts(NostrSigner.getInstance())
var localLatest = 0L
accounts.forEach {
val latest = NostrSigner.getInstance().getDatabase(it.npub).applicationDao().getLatestNotification()
if (latest != null) {
since = latest
if (latest != null && latest > localLatest) {
localLatest = latest + 1
}
}
if (localLatest > 0) {
since = localLatest
}
val pubKeys =
accounts.mapNotNull {
@@ -183,6 +200,7 @@ object NotificationDataSource : NostrDataSource(NostrSigner.getInstance().client
) {
checkNotInMainThread()
NotificationUtils.getOrCreateDMChannel(NostrSigner.getInstance().applicationContext)
AmberRelayStats.addReceived(relay.url)
eventNotificationConsumer.consume(event)
}