Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/src/main/java/to/bitkit/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import to.bitkit.appwidget.AppWidgetRefreshReason
import to.bitkit.appwidget.AppWidgetRefreshScheduler
import to.bitkit.env.Env
import to.bitkit.services.BluetoothInit
import to.bitkit.utils.Logger
import javax.inject.Inject

@HiltAndroidApp
Expand All @@ -34,6 +35,7 @@ internal open class App : Application(), Configuration.Provider {

override fun onCreate() {
super.onCreate()
installUncaughtExceptionLogger()
Env.initAppStoragePath(filesDir.absolutePath)
SingletonImageLoader.setSafe { imageLoader }
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand All @@ -42,7 +44,17 @@ internal open class App : Application(), Configuration.Provider {
BluetoothInit.ensureInitialized()
}

private fun installUncaughtExceptionLogger() {
val previous = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
Logger.error("Uncaught exception on thread '${thread.name}'", throwable, context = TAG)
previous?.uncaughtException(thread, throwable)
}
}

companion object {
private const val TAG = "App"

@SuppressLint("StaticFieldLeak") // Should be safe given its manual memory management
internal var currentActivity: CurrentActivity? = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import org.lightningdevkit.ldknode.Event
import to.bitkit.App
import to.bitkit.R
import to.bitkit.appwidget.AppWidgetRefreshReason
import to.bitkit.appwidget.AppWidgetRefreshScheduler
import to.bitkit.async.appScope
import to.bitkit.data.CacheStore
import to.bitkit.di.UiDispatcher
import to.bitkit.domain.commands.NotifyChannelReady
Expand Down Expand Up @@ -53,7 +52,7 @@ class LightningNodeService : Service() {
@UiDispatcher
lateinit var uiDispatcher: CoroutineDispatcher

private val serviceScope by lazy { CoroutineScope(SupervisorJob() + uiDispatcher) }
private val serviceScope by lazy { appScope(uiDispatcher, TAG) }

@Inject
lateinit var lightningRepo: LightningRepo
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/async/BaseCoroutineScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import kotlin.coroutines.CoroutineContext

open class BaseCoroutineScope(
private val dispatcher: CoroutineDispatcher,
) : CoroutineScope by CoroutineScope(SupervisorJob() + dispatcher) {
tag: String,
) : CoroutineScope by CoroutineScope(SupervisorJob() + dispatcher + loggingExceptionHandler(tag)) {

@Throws(InterruptedException::class)
protected fun <T> runBlocking(
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/to/bitkit/async/CoroutineScopes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package to.bitkit.async

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import to.bitkit.utils.Logger

fun loggingExceptionHandler(tag: String): CoroutineExceptionHandler =
CoroutineExceptionHandler { _, throwable ->
Logger.error("Uncaught coroutine exception", throwable, context = tag)
}

fun appScope(dispatcher: CoroutineDispatcher, tag: String): CoroutineScope =
CoroutineScope(dispatcher + SupervisorJob() + loggingExceptionHandler(tag))
5 changes: 3 additions & 2 deletions app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import to.bitkit.ext.runSuspendCatching
import to.bitkit.utils.AppError
import java.util.concurrent.Executors
import java.util.concurrent.ThreadFactory
Expand All @@ -20,14 +21,14 @@ enum class ServiceQueue {
coroutineContext: CoroutineContext = scope.coroutineContext,
block: suspend CoroutineScope.() -> T,
): T = runBlocking(coroutineContext) {
runCatching { block() }.getOrElse { throw AppError(it) }
runSuspendCatching { block() }.getOrElse { throw AppError(it) }
}

suspend fun <T> background(
coroutineContext: CoroutineContext = scope.coroutineContext,
block: suspend CoroutineScope.() -> T,
): T = withContext(coroutineContext) {
runCatching { block() }.getOrElse { throw AppError(it) }
runSuspendCatching { block() }.getOrElse { throw AppError(it) }
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/keychain/Keychain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Keychain @Inject constructor(
private val db: AppDb,
@ApplicationContext private val context: Context,
@IoDispatcher private val dispatcher: CoroutineDispatcher,
) : BaseCoroutineScope(dispatcher) {
) : BaseCoroutineScope(dispatcher, TAG) {
companion object {
private const val TAG = "Keychain"
private const val CAUSE_CHAIN_DEPTH = 4
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/to/bitkit/repositories/BackupRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import com.synonym.bitkitcore.migrateBackupActivityTagsJson
import com.synonym.bitkitcore.migrateBackupPreActivityMetadataJson
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.currentCoroutineContext
Expand All @@ -33,6 +31,7 @@ import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import to.bitkit.R
import to.bitkit.async.appScope
import to.bitkit.data.AppDb
import to.bitkit.data.CacheStore
import to.bitkit.data.SettingsStore
Expand Down Expand Up @@ -102,7 +101,7 @@ class BackupRepo @Inject constructor(
private val clock: Clock,
private val db: AppDb,
) {
private val scope = CoroutineScope(ioDispatcher + SupervisorJob())
private val scope = appScope(ioDispatcher, TAG)

private val backupJobs = mutableMapOf<BackupCategory, Job>()
private val statusObserverJobs = mutableListOf<Job>()
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/to/bitkit/repositories/BlocktankRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
Expand All @@ -50,6 +48,7 @@ import org.lightningdevkit.ldknode.Bolt11Invoice
import org.lightningdevkit.ldknode.ChannelDetails
import org.lightningdevkit.ldknode.Event
import to.bitkit.async.ServiceQueue
import to.bitkit.async.appScope
import to.bitkit.data.CacheStore
import to.bitkit.di.BgDispatcher
import to.bitkit.env.Env
Expand Down Expand Up @@ -82,7 +81,7 @@ class BlocktankRepo @Inject constructor(
@Named("enablePolling") private val enablePolling: Boolean,
private val lightningRepo: LightningRepo,
) {
private val repoScope = CoroutineScope(bgDispatcher + SupervisorJob())
private val repoScope = appScope(bgDispatcher, TAG)

private val _blocktankState = MutableStateFlow(BlocktankState())
val blocktankState: StateFlow<BlocktankState> = _blocktankState.asStateFlow()
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/to/bitkit/repositories/ConnectivityRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import android.os.Handler
import android.os.Looper
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -19,6 +17,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import to.bitkit.async.appScope
import to.bitkit.di.BgDispatcher
import to.bitkit.utils.Logger
import javax.inject.Inject
Expand All @@ -29,11 +28,12 @@ class ConnectivityRepo @Inject constructor(
@ApplicationContext private val context: Context,
@BgDispatcher private val bgDispatcher: CoroutineDispatcher,
) {
private val repoScope = CoroutineScope(bgDispatcher + SupervisorJob())
private val repoScope = appScope(bgDispatcher, TAG)
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val mainHandler = Handler(Looper.getMainLooper())

companion object {
private const val TAG = "ConnectivityRepo"
private const val DELAY_MS = 250L
}

Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/to/bitkit/repositories/CurrencyRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
Expand All @@ -22,6 +20,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import to.bitkit.async.appScope
import to.bitkit.data.CacheStore
import to.bitkit.data.SettingsStore
import to.bitkit.di.BgDispatcher
Expand Down Expand Up @@ -58,7 +57,7 @@ class CurrencyRepo @Inject constructor(
private val clock: Clock,
@Named("enablePolling") private val enablePolling: Boolean,
) : AmountInputHandler {
private val repoScope = CoroutineScope(bgDispatcher + SupervisorJob())
private val repoScope = appScope(bgDispatcher, TAG)
private val _currencyState = MutableStateFlow(CurrencyState())
val currencyState: StateFlow<CurrencyState> = _currencyState.asStateFlow()

Expand Down
9 changes: 6 additions & 3 deletions app/src/main/java/to/bitkit/repositories/HealthRepo.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package to.bitkit.repositories

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand All @@ -12,6 +10,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.lightningdevkit.ldknode.ChannelDetails
import to.bitkit.async.appScope
import to.bitkit.data.CacheStore
import to.bitkit.di.BgDispatcher
import to.bitkit.models.BackupCategory
Expand All @@ -33,7 +32,11 @@ class HealthRepo @Inject constructor(
private val cacheStore: CacheStore,
private val clock: Clock,
) {
private val repoScope = CoroutineScope(bgDispatcher + SupervisorJob())
companion object {
private const val TAG = "HealthRepo"
}

private val repoScope = appScope(bgDispatcher, TAG)

private val _healthState = MutableStateFlow(AppHealthState())
val healthState: StateFlow<AppHealthState> = _healthState.asStateFlow()
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -30,6 +28,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import to.bitkit.async.appScope
import to.bitkit.data.HwWalletStore
import to.bitkit.data.SettingsStore
import to.bitkit.di.IoDispatcher
Expand Down Expand Up @@ -90,7 +89,7 @@ class HwWalletRepo @Inject constructor(
private val SUPPORTED_WATCHER_ADDRESS_TYPES = setOf(HwFundingAddressType.NATIVE_SEGWIT.settingsKey)
}

private val scope = CoroutineScope(SupervisorJob() + ioDispatcher)
private val scope = appScope(ioDispatcher, TAG)

private val activeWatchers = mutableSetOf<String>()
private val activeWatcherElectrumUrls = mutableMapOf<String, String>()
Expand Down
7 changes: 3 additions & 4 deletions app/src/main/java/to/bitkit/repositories/LightningRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -54,6 +52,7 @@ import org.lightningdevkit.ldknode.PaymentId
import org.lightningdevkit.ldknode.PeerDetails
import org.lightningdevkit.ldknode.SpendableUtxo
import org.lightningdevkit.ldknode.Txid
import to.bitkit.async.appScope
import to.bitkit.data.CacheStore
import to.bitkit.data.SettingsData
import to.bitkit.data.SettingsStore
Expand All @@ -73,6 +72,7 @@ import to.bitkit.models.NATIVE_WITNESS_TYPES
import to.bitkit.models.NodeLifecycleState
import to.bitkit.models.OpenChannelResult
import to.bitkit.models.TransactionSpeed
import to.bitkit.models.WalletScope
import to.bitkit.models.safe
import to.bitkit.models.satsToMsat
import to.bitkit.models.toAddressType
Expand All @@ -88,7 +88,6 @@ import to.bitkit.services.LnurlWithdrawResponse
import to.bitkit.services.LspNotificationsService
import to.bitkit.services.NodeEventHandler
import to.bitkit.utils.AppError
import to.bitkit.models.WalletScope
import to.bitkit.utils.Logger
import to.bitkit.utils.ServiceError
import to.bitkit.utils.UrlValidator
Expand Down Expand Up @@ -128,7 +127,7 @@ class LightningRepo @Inject constructor(
private val _nodeEvents = MutableSharedFlow<Event>(extraBufferCapacity = 64)
val nodeEvents = _nodeEvents.asSharedFlow()

private val scope = CoroutineScope(bgDispatcher + SupervisorJob())
private val scope = appScope(bgDispatcher, TAG)

private val _eventHandlers = ConcurrentHashMap.newKeySet<NodeEventHandler>()
private val _isRecoveryMode = MutableStateFlow(false)
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/to/bitkit/repositories/PrivatePaykitRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import com.synonym.paykit.PrivatePaymentListDeliveryReport
import com.synonym.paykit.PrivatePaymentListReservationUpdateInput
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -27,6 +25,7 @@ import org.lightningdevkit.ldknode.PaymentDirection
import org.lightningdevkit.ldknode.PaymentKind
import org.lightningdevkit.ldknode.PaymentStatus
import to.bitkit.App
import to.bitkit.async.appScope
import to.bitkit.data.PrivatePaykitCacheStore
import to.bitkit.data.SettingsStore
import to.bitkit.di.IoDispatcher
Expand Down Expand Up @@ -86,7 +85,7 @@ class PrivatePaykitRepo @Inject constructor(

private val publicationMutex = Mutex()
private val serializedDispatcher = ioDispatcher.limitedParallelism(1)
private val retryScope = CoroutineScope(serializedDispatcher + SupervisorJob())
private val retryScope = appScope(serializedDispatcher, TAG)
private val knownSavedContactKeys = mutableSetOf<String>()
private var state: PrivatePaykitState? = null
private val pendingMessageDrainRetryLock = Any()
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/to/bitkit/repositories/PubkyRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import io.ktor.client.call.body
import io.ktor.client.request.post
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
Expand All @@ -33,6 +31,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import to.bitkit.async.appScope
import to.bitkit.data.PubkyStore
import to.bitkit.data.SettingsStore
import to.bitkit.data.hasPublicPaykitPublicationState
Expand Down Expand Up @@ -94,7 +93,7 @@ class PubkyRepo @Inject constructor(
private const val AVATAR_QUALITY = 80
}

private val scope = CoroutineScope(ioDispatcher + SupervisorJob())
private val scope = appScope(ioDispatcher, TAG)
private val serviceInitializeMutex = Mutex()
private val initializeMutex = Mutex()
private val loadProfileMutex = Mutex()
Expand Down
Loading
Loading