From ba47fc7ddb6500a0a9b2b914c15914f06547e067 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Mar 2026 22:04:47 +0000 Subject: [PATCH 1/3] Make offline flavor not require ConnectivityService - Guard `startService()` in Amber.kt to skip service startup for the offline flavor (the manifest doesn't declare ConnectivityService, so attempts to start it would fail) - Move the periodic DB maintenance tasks (expiring permissions and deleting stale application entries) from ConnectivityService's timer into ClearLogsWorker, which already runs daily via WorkManager for both flavors https://claude.ai/code/session_014Zqb1AEPPF2RrzmEyPnmei --- .../java/com/greenart7c3/nostrsigner/Amber.kt | 1 + .../nostrsigner/service/ClearLogsWorker.kt | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt index a96d9117..2618ac8e 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt @@ -339,6 +339,7 @@ class Amber : } fun startService() { + if (BuildFlavorChecker.isOfflineFlavor()) return try { Log.d(TAG, "Starting ConnectivityService") val operation = PendingIntent.getForegroundService( diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt index 1f2cb867..fd48b196 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/ClearLogsWorker.kt @@ -6,6 +6,7 @@ import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.LocalPreferences +import com.greenart7c3.nostrsigner.database.LogEntity import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils.ONE_WEEK import kotlin.coroutines.cancellation.CancellationException @@ -15,8 +16,10 @@ class ClearLogsWorker(appContext: Context, workerParams: WorkerParameters) : Cor override suspend fun doWork(): Result { LocalPreferences.allSavedAccounts(Amber.instance).forEach { try { - val oneWeek = System.currentTimeMillis() - (ONE_WEEK * 1000L) + val now = System.currentTimeMillis() + val oneWeek = now - (ONE_WEEK * 1000L) val oneWeekAgo = TimeUtils.oneWeekAgo() + val historyDatabase = Amber.instance.getHistoryDatabase(it.npub) val deletedHistory = historyDatabase.dao().deleteOldHistory(oneWeekAgo) if (deletedHistory > 0) { @@ -28,9 +31,24 @@ class ClearLogsWorker(appContext: Context, workerParams: WorkerParameters) : Cor if (deletedLogs > 0) { Log.d(Amber.TAG, "Deleted $deletedLogs old log entries") } + + val database = Amber.instance.getDatabase(it.npub) + database.dao().updateExpiredPermissions(TimeUtils.now()) + val deleted = database.dao().deleteOldApplications(now / 1000) + if (deleted > 0) { + logDatabase.dao().insertLog( + LogEntity( + id = 0, + url = "", + type = "deleteApplications", + message = "Deleted $deleted expired applications", + time = now, + ), + ) + } } catch (e: Exception) { if (e is CancellationException) throw e - Log.e(Amber.TAG, "Error deleting old log entries", e) + Log.e(Amber.TAG, "Error in ClearLogsWorker", e) } } return Result.success() From 32dfb23df69cf2e0631238c510e733875348242f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Mar 2026 08:33:50 +0000 Subject: [PATCH 2/3] Do not start Tor or notifications for offline flavor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Skip `TorManager.init()` and `stats.createNotificationChannel()` in Amber.onCreate() for the offline flavor — no relay connections means no need for notification channels or Tor initialisation - Guard `stats.updateNotification()` in `reconnect()` behind the same offline check to avoid accessing uninitialised lateinit vars - Add an early-return guard in `AmberRelayStats.updateNotification()` so any call path that reaches it from relay listeners is also safe https://claude.ai/code/session_014Zqb1AEPPF2RrzmEyPnmei --- app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt | 10 +++++++--- .../greenart7c3/nostrsigner/relays/AmberRelayStat.kt | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt index 2618ac8e..9249622c 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/Amber.kt @@ -227,8 +227,10 @@ class Amber : instance = this - stats.createNotificationChannel() - TorManager.init(this) + if (!BuildFlavorChecker.isOfflineFlavor()) { + stats.createNotificationChannel() + TorManager.init(this) + } isStartingAppState.value = true isStartingApp.value = true @@ -371,7 +373,9 @@ class Amber : client.connect() } client.reconnect(wasActive) - stats.updateNotification() + if (!BuildFlavorChecker.isOfflineFlavor()) { + stats.updateNotification() + } } fun getDatabase(npub: String): AppDatabase { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/relays/AmberRelayStat.kt b/app/src/main/java/com/greenart7c3/nostrsigner/relays/AmberRelayStat.kt index afdd7db1..9011b40e 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/relays/AmberRelayStat.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/relays/AmberRelayStat.kt @@ -15,6 +15,7 @@ import androidx.core.app.NotificationChannelGroupCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.greenart7c3.nostrsigner.Amber +import com.greenart7c3.nostrsigner.BuildFlavorChecker import com.greenart7c3.nostrsigner.MainActivity import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.service.KillSwitchReceiver @@ -215,6 +216,7 @@ class AmberRelayStats( } fun updateNotification() { + if (BuildFlavorChecker.isOfflineFlavor()) return val notificationManager = NotificationManagerCompat.from(appContext) if (ActivityCompat.checkSelfPermission(appContext, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { createNotification( From e068379d7c26beebe7f67eba4ca927755687cffb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Mar 2026 08:57:05 +0000 Subject: [PATCH 3/3] Remove expired applications check from ConnectivityService Moved to ClearLogsWorker; no need to run it in the timer loop. https://claude.ai/code/session_014Zqb1AEPPF2RrzmEyPnmei --- .../service/ConnectivityService.kt | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt index e727ffe6..a0b0cdd9 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt @@ -12,9 +12,6 @@ import android.util.Log import androidx.core.app.ServiceCompat import com.greenart7c3.nostrsigner.Amber import com.greenart7c3.nostrsigner.BuildFlavorChecker -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.database.LogEntity -import com.vitorpamplona.quartz.utils.TimeUtils import java.util.Timer import java.util.TimerTask import kotlinx.coroutines.CoroutineScope @@ -117,25 +114,6 @@ class ConnectivityService : Service() { timer.schedule( object : TimerTask() { override fun run() { - scope.launch { - LocalPreferences.allSavedAccounts(Amber.instance).forEach { accountInfo -> - val now = System.currentTimeMillis() / 1000 - Amber.instance.getDatabase(accountInfo.npub).dao().updateExpiredPermissions(TimeUtils.now()) - val deleted = Amber.instance.getDatabase(accountInfo.npub).dao().deleteOldApplications(now) - if (deleted > 0) { - Amber.instance.getLogDatabase(accountInfo.npub).dao().insertLog( - LogEntity( - id = 0, - url = "", - type = "deleteApplications", - message = "Deleted $deleted expired applications", - time = System.currentTimeMillis(), - ), - ) - } - } - } - if (BuildFlavorChecker.isOfflineFlavor()) { return }