mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-05 10:58:23 +00:00
Convert legacy Long timing overloads to Duration, silence flavor warnings
Replace every Long-millisecond call to delay/withTimeoutOrNull/debounce with the kotlin.time.Duration overloads, converting the related constants (EOSE/backup/check timeouts, clipboard clear delay) and the retryWithBackoff/reconnect backoff state to Duration as well. Also suppress the three deliberate cross-flavor compiler warnings in TrustScoreService, WebDavService and ZapstoreUpdater: the offline flavor compiles against OkHttp 4 where response.body is nullable, so the safe call/elvis is required there and only redundant in the free flavor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ho6LW6eEBkVnnpFvr5tvpi
This commit is contained in:
@@ -75,6 +75,8 @@ import java.net.Socket
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -402,11 +404,11 @@ class Amber :
|
|||||||
if (attempt > 0) {
|
if (attempt > 0) {
|
||||||
TorManager.showRetrying()
|
TorManager.showRetrying()
|
||||||
TorManager.stop()
|
TorManager.stop()
|
||||||
delay(3000)
|
delay(3.seconds)
|
||||||
TorManager.start(this@Amber, applicationIOScope)
|
TorManager.start(this@Amber, applicationIOScope)
|
||||||
}
|
}
|
||||||
attempt++
|
attempt++
|
||||||
withTimeoutOrNull(120_000L) {
|
withTimeoutOrNull(2.minutes) {
|
||||||
TorManager.isRunning.first { it }
|
TorManager.isRunning.first { it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,7 +547,7 @@ class Amber :
|
|||||||
}
|
}
|
||||||
AmberLog.d(TAG, "checkForNewRelaysAndUpdateAllFilters wasActive: $wasActive")
|
AmberLog.d(TAG, "checkForNewRelaysAndUpdateAllFilters wasActive: $wasActive")
|
||||||
if (!wasActive) {
|
if (!wasActive) {
|
||||||
delay(3000)
|
delay(3.seconds)
|
||||||
client.connect()
|
client.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
|||||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -142,7 +143,7 @@ class MainViewModel(val context: Context) : ViewModel() {
|
|||||||
var error = true
|
var error = true
|
||||||
var count = 0
|
var count = 0
|
||||||
while (error && count < 10) {
|
while (error && count < 10) {
|
||||||
delay(100)
|
delay(100.milliseconds)
|
||||||
count++
|
count++
|
||||||
try {
|
try {
|
||||||
if (route == Route.UpdateSettings.route) {
|
if (route == Route.UpdateSettings.route) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.greenart7c3.nostrsigner.service.StopServiceReceiver
|
|||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
@@ -43,7 +44,7 @@ class AmberRelayStats(
|
|||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
val relayStatus = combine(client.availableRelaysFlow(), client.connectedRelaysFlow()) { available, connected ->
|
val relayStatus = combine(client.availableRelaysFlow(), client.connectedRelaysFlow()) { available, connected ->
|
||||||
available to connected
|
available to connected
|
||||||
}.debounce(300).onEach {
|
}.debounce(300.milliseconds).onEach {
|
||||||
this.available = it.first
|
this.available = it.first
|
||||||
this.connected = it.second
|
this.connected = it.second
|
||||||
val notificationManager = NotificationManagerCompat.from(appContext)
|
val notificationManager = NotificationManagerCompat.from(appContext)
|
||||||
@@ -96,13 +97,13 @@ class AmberRelayStats(
|
|||||||
notificationManager.createNotificationChannel(statusChannel)
|
notificationManager.createNotificationChannel(statusChannel)
|
||||||
|
|
||||||
Amber.instance.applicationIOScope.launch {
|
Amber.instance.applicationIOScope.launch {
|
||||||
Amber.instance.client.availableRelaysFlow().debounce(300).collect {
|
Amber.instance.client.availableRelaysFlow().debounce(300.milliseconds).collect {
|
||||||
available = it
|
available = it
|
||||||
updateNotification()
|
updateNotification()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Amber.instance.applicationIOScope.launch {
|
Amber.instance.applicationIOScope.launch {
|
||||||
Amber.instance.client.connectedRelaysFlow().debounce(300).collect {
|
Amber.instance.client.connectedRelaysFlow().debounce(300.milliseconds).collect {
|
||||||
connected = it
|
connected = it
|
||||||
updateNotification()
|
updateNotification()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
|
|||||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.EventCmd
|
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.EventCmd
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
|
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -52,7 +53,7 @@ class NostrClientLoggerListener(
|
|||||||
val scope: CoroutineScope,
|
val scope: CoroutineScope,
|
||||||
) : RelayConnectionListener {
|
) : RelayConnectionListener {
|
||||||
private var reconnectJob: Job? = null
|
private var reconnectJob: Job? = null
|
||||||
private var reconnectDelay = 5_000L
|
private var reconnectDelay = 5.seconds
|
||||||
private var lastDisconnectTime = 0L
|
private var lastDisconnectTime = 0L
|
||||||
|
|
||||||
// Counts the failure against the relay and only schedules a reconnect while it
|
// Counts the failure against the relay and only schedules a reconnect while it
|
||||||
@@ -71,15 +72,15 @@ class NostrClientLoggerListener(
|
|||||||
private fun reconnectWithBackoff() {
|
private fun reconnectWithBackoff() {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now - lastDisconnectTime > 60_000) {
|
if (now - lastDisconnectTime > 60_000) {
|
||||||
reconnectDelay = 5_000L
|
reconnectDelay = 5.seconds
|
||||||
}
|
}
|
||||||
lastDisconnectTime = now
|
lastDisconnectTime = now
|
||||||
|
|
||||||
reconnectJob?.cancel()
|
reconnectJob?.cancel()
|
||||||
reconnectJob = scope.launch {
|
reconnectJob = scope.launch {
|
||||||
AmberLog.d(Amber.TAG, "Reconnecting in ${reconnectDelay / 1000}s...")
|
AmberLog.d(Amber.TAG, "Reconnecting in ${reconnectDelay.inWholeSeconds}s...")
|
||||||
delay(reconnectDelay)
|
delay(reconnectDelay)
|
||||||
reconnectDelay = (reconnectDelay * 2).coerceAtMost(60_000L)
|
reconnectDelay = (reconnectDelay * 2).coerceAtMost(60.seconds)
|
||||||
if (!BuildFlavorChecker.isOfflineFlavor() && !Amber.instance.settings.killSwitch.value) {
|
if (!BuildFlavorChecker.isOfflineFlavor() && !Amber.instance.settings.killSwitch.value) {
|
||||||
Amber.instance.reconnect()
|
Amber.instance.reconnect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
|||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
private const val BACKUP_KIND = 30078
|
private const val BACKUP_KIND = 30078
|
||||||
private const val BACKUP_D_TAG = "amber-app-backup"
|
private const val BACKUP_D_TAG = "amber-app-backup"
|
||||||
private const val INBOX_KIND = 10002
|
private const val INBOX_KIND = 10002
|
||||||
private const val INBOX_FETCH_TIMEOUT_MS = 10_000L
|
private val INBOX_FETCH_TIMEOUT = 10.seconds
|
||||||
private const val BACKUP_FETCH_TIMEOUT_MS = 15_000L
|
private val BACKUP_FETCH_TIMEOUT = 15.seconds
|
||||||
private const val PAYLOAD_VERSION = 1
|
private const val PAYLOAD_VERSION = 1
|
||||||
|
|
||||||
private val AGGREGATOR_RELAY = RelayUrlNormalizer.normalizeOrNull("wss://aggr.nostr.land/")
|
private val AGGREGATOR_RELAY = RelayUrlNormalizer.normalizeOrNull("wss://aggr.nostr.land/")
|
||||||
@@ -182,7 +183,7 @@ object ApplicationBackup {
|
|||||||
limit = 1,
|
limit = 1,
|
||||||
)
|
)
|
||||||
client.subscribe(subId, discoveryRelays.associateWith { listOf(filter) })
|
client.subscribe(subId, discoveryRelays.associateWith { listOf(filter) })
|
||||||
delay(INBOX_FETCH_TIMEOUT_MS)
|
delay(INBOX_FETCH_TIMEOUT)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
AmberLog.w(Amber.TAG, "ApplicationBackup: inbox relay fetch failed", e)
|
AmberLog.w(Amber.TAG, "ApplicationBackup: inbox relay fetch failed", e)
|
||||||
@@ -274,7 +275,7 @@ object ApplicationBackup {
|
|||||||
limit = 1,
|
limit = 1,
|
||||||
)
|
)
|
||||||
client.subscribe(subId, relays.associateWith { listOf(filter) })
|
client.subscribe(subId, relays.associateWith { listOf(filter) })
|
||||||
delay(BACKUP_FETCH_TIMEOUT_MS)
|
delay(BACKUP_FETCH_TIMEOUT)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
AmberLog.e(Amber.TAG, "ApplicationBackup: failed to fetch backup event", e)
|
AmberLog.e(Amber.TAG, "ApplicationBackup: failed to fetch backup event", e)
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponse
|
|||||||
import com.vitorpamplona.quartz.nip46RemoteSigner.NostrConnectEvent
|
import com.vitorpamplona.quartz.nip46RemoteSigner.NostrConnectEvent
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import kotlin.collections.toSet
|
import kotlin.collections.toSet
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
@@ -213,11 +215,11 @@ object BunkerRequestUtils {
|
|||||||
|
|
||||||
suspend fun retryWithBackoff(
|
suspend fun retryWithBackoff(
|
||||||
maxRetries: Int = 5,
|
maxRetries: Int = 5,
|
||||||
initialDelayMs: Long = 200L,
|
initialDelay: Duration = 200.milliseconds,
|
||||||
maxDelayMs: Long = 3_200L,
|
maxDelay: Duration = 3_200.milliseconds,
|
||||||
block: suspend () -> Boolean,
|
block: suspend () -> Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
var currentDelay = initialDelayMs
|
var currentDelay = initialDelay
|
||||||
|
|
||||||
repeat(maxRetries) { attempt ->
|
repeat(maxRetries) { attempt ->
|
||||||
delay(currentDelay)
|
delay(currentDelay)
|
||||||
@@ -226,7 +228,7 @@ object BunkerRequestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attempt < maxRetries - 1) {
|
if (attempt < maxRetries - 1) {
|
||||||
currentDelay = (currentDelay * 2).coerceAtMost(maxDelayMs)
|
currentDelay = (currentDelay * 2).coerceAtMost(maxDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +394,7 @@ object BunkerRequestUtils {
|
|||||||
activity?.finishAndRemoveTask()
|
activity?.finishAndRemoveTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(500)
|
delay(500.milliseconds)
|
||||||
|
|
||||||
if (signPolicy != null) {
|
if (signPolicy != null) {
|
||||||
AmberUtils.configureSignPolicy(application, signPolicy, key, permissions)
|
AmberUtils.configureSignPolicy(application, signPolicy, key, permissions)
|
||||||
|
|||||||
@@ -39,13 +39,14 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|||||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val EOSE_TIMEOUT_MS = 30_000L
|
private val EOSE_TIMEOUT = 30.seconds
|
||||||
|
|
||||||
class ProfileSubscription(
|
class ProfileSubscription(
|
||||||
val client: NostrClient,
|
val client: NostrClient,
|
||||||
@@ -165,7 +166,7 @@ class ProfileSubscription(
|
|||||||
relaysPerSubId[subId] = relayListFilter.keys.toMutableSet()
|
relaysPerSubId[subId] = relayListFilter.keys.toMutableSet()
|
||||||
client.subscribe(subId, relayListFilter)
|
client.subscribe(subId, relayListFilter)
|
||||||
timeoutJobs[subId] = scope.launch {
|
timeoutJobs[subId] = scope.launch {
|
||||||
delay(EOSE_TIMEOUT_MS)
|
delay(EOSE_TIMEOUT)
|
||||||
if (relaysPerSubId.containsKey(subId)) {
|
if (relaysPerSubId.containsKey(subId)) {
|
||||||
unsubscribe(subId)
|
unsubscribe(subId)
|
||||||
// still fetch the profile with whatever relay list we have saved
|
// still fetch the profile with whatever relay list we have saved
|
||||||
@@ -181,7 +182,7 @@ class ProfileSubscription(
|
|||||||
relaysPerSubId[subId] = profileFilter.keys.toMutableSet()
|
relaysPerSubId[subId] = profileFilter.keys.toMutableSet()
|
||||||
client.subscribe(subId, profileFilter)
|
client.subscribe(subId, profileFilter)
|
||||||
timeoutJobs[subId] = scope.launch {
|
timeoutJobs[subId] = scope.launch {
|
||||||
delay(EOSE_TIMEOUT_MS)
|
delay(EOSE_TIMEOUT)
|
||||||
if (relaysPerSubId.containsKey(subId)) {
|
if (relaysPerSubId.containsKey(subId)) {
|
||||||
unsubscribe(subId)
|
unsubscribe(subId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.greenart7c3.nostrsigner.service
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@@ -17,7 +18,7 @@ class RelayDisconnectService(ctx: Context, params: WorkerParameters) : Worker(ct
|
|||||||
val job =
|
val job =
|
||||||
ioScope.launch {
|
ioScope.launch {
|
||||||
val url = inputData.getString("relay")
|
val url = inputData.getString("relay")
|
||||||
delay(60000)
|
delay(1.minutes)
|
||||||
url?.let {
|
url?.let {
|
||||||
// TODO: this class is not being used, but this new way of using Nostr client requires removing the relay from all filters to disconnect it
|
// TODO: this class is not being used, but this new way of using Nostr client requires removing the relay from all filters to disconnect it
|
||||||
// val relay = Amber.instance.client.getRelay(it)
|
// val relay = Amber.instance.client.getRelay(it)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
|||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -109,7 +110,7 @@ object ReportSender {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
onDone()
|
onDone()
|
||||||
delay(10000)
|
delay(10.seconds)
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ object TrustScoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Safe call needed: the offline flavor compiles against OkHttp 4, where body is nullable
|
// Safe call needed: the offline flavor compiles against OkHttp 4, where body is nullable
|
||||||
|
@Suppress("UNNECESSARY_SAFE_CALL")
|
||||||
val body = response.body?.string()
|
val body = response.body?.string()
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
cacheResult(normalizedUrl, null, isFailed = true)
|
cacheResult(normalizedUrl, null, isFailed = true)
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import com.greenart7c3.nostrsigner.AmberLog
|
|||||||
import com.greenart7c3.nostrsigner.BuildConfig
|
import com.greenart7c3.nostrsigner.BuildConfig
|
||||||
import com.greenart7c3.nostrsigner.BuildFlavorChecker
|
import com.greenart7c3.nostrsigner.BuildFlavorChecker
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
|
||||||
private const val CHECK_TIMEOUT_MS = 30_000L
|
private val CHECK_TIMEOUT = 30.seconds
|
||||||
|
|
||||||
class UpdateCheckWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) {
|
class UpdateCheckWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) {
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ class UpdateCheckWorker(appContext: Context, workerParams: WorkerParameters) : C
|
|||||||
// If a check was actually started, wait for it to finish so the notification fires
|
// If a check was actually started, wait for it to finish so the notification fires
|
||||||
// before WorkManager considers the job done.
|
// before WorkManager considers the job done.
|
||||||
if (updater.isChecking.value) {
|
if (updater.isChecking.value) {
|
||||||
withTimeoutOrNull(CHECK_TIMEOUT_MS) {
|
withTimeoutOrNull(CHECK_TIMEOUT) {
|
||||||
updater.isChecking.first { !it }
|
updater.isChecking.first { !it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ object WebDavService {
|
|||||||
client.newCall(request).execute().use { response ->
|
client.newCall(request).execute().use { response ->
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
// Safe call needed: the offline flavor compiles against OkHttp 4, where body is nullable
|
// Safe call needed: the offline flavor compiles against OkHttp 4, where body is nullable
|
||||||
|
@Suppress("UNNECESSARY_SAFE_CALL", "USELESS_ELVIS")
|
||||||
val body = response.body?.string() ?: ""
|
val body = response.body?.string() ?: ""
|
||||||
Result.success(body)
|
Result.success(body)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.io.File
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -36,7 +37,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
|
||||||
private const val EOSE_TIMEOUT_MS = 15_000L
|
private val EOSE_TIMEOUT = 15.seconds
|
||||||
private const val RELEASE_KIND = 30063
|
private const val RELEASE_KIND = 30063
|
||||||
private val UPDATE_RELAY_URLS = listOf(
|
private val UPDATE_RELAY_URLS = listOf(
|
||||||
"wss://relay.zapstore.dev",
|
"wss://relay.zapstore.dev",
|
||||||
@@ -128,7 +129,7 @@ class ZapstoreUpdater(
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeoutJob = scope.launch {
|
timeoutJob = scope.launch {
|
||||||
delay(EOSE_TIMEOUT_MS)
|
delay(EOSE_TIMEOUT)
|
||||||
AmberLog.w(Amber.TAG, "ZapstoreUpdater: timeout waiting for EOSE")
|
AmberLog.w(Amber.TAG, "ZapstoreUpdater: timeout waiting for EOSE")
|
||||||
onReleaseEose()
|
onReleaseEose()
|
||||||
}
|
}
|
||||||
@@ -288,7 +289,8 @@ class ZapstoreUpdater(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe call needed: the offline flavor compiles against OkHttp 4, where body is nullable
|
// Elvis needed: the offline flavor compiles against OkHttp 4, where body is nullable
|
||||||
|
@Suppress("USELESS_ELVIS")
|
||||||
val body = response.body ?: return null
|
val body = response.body ?: return null
|
||||||
val contentLength = body.contentLength()
|
val contentLength = body.contentLength()
|
||||||
val apkFile = File(context.cacheDir, "amber-update-${release.version}.apk")
|
val apkFile = File(context.cacheDir, "amber-update-${release.version}.apk")
|
||||||
@@ -338,7 +340,7 @@ class ZapstoreUpdater(
|
|||||||
}
|
}
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
scope.launch(Dispatchers.Main) {
|
scope.launch(Dispatchers.Main) {
|
||||||
delay(2000)
|
delay(2.seconds)
|
||||||
downloadState.value = DownloadState.IDLE
|
downloadState.value = DownloadState.IDLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ import com.vitorpamplona.quartz.nip06KeyDerivation.Bip39Mnemonics
|
|||||||
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -1055,7 +1056,7 @@ fun LoginPage(
|
|||||||
if (isValid.first != null) {
|
if (isValid.first != null) {
|
||||||
keyPair = isValid.first!!
|
keyPair = isValid.first!!
|
||||||
scope.launch {
|
scope.launch {
|
||||||
delay(200)
|
delay(200.milliseconds)
|
||||||
pageState.animateScrollToPage(1)
|
pageState.animateScrollToPage(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1139,7 +1140,7 @@ fun LoginPage(
|
|||||||
if (isValid.first != null) {
|
if (isValid.first != null) {
|
||||||
keyPair = isValid.first!!
|
keyPair = isValid.first!!
|
||||||
scope.launch {
|
scope.launch {
|
||||||
delay(200)
|
delay(200.milliseconds)
|
||||||
pageState.animateScrollToPage(1)
|
pageState.animateScrollToPage(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1182,7 +1183,7 @@ fun LoginPage(
|
|||||||
keyPair = isValid.first!!
|
keyPair = isValid.first!!
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
delay(200)
|
delay(200.milliseconds)
|
||||||
pageState.animateScrollToPage(1)
|
pageState.animateScrollToPage(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1212,7 +1213,7 @@ fun LoginPage(
|
|||||||
keyPair = isValid.first!!
|
keyPair = isValid.first!!
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
delay(200)
|
delay(200.milliseconds)
|
||||||
pageState.animateScrollToPage(1)
|
pageState.animateScrollToPage(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import android.os.PersistableBundle
|
|||||||
import androidx.compose.ui.platform.ClipEntry
|
import androidx.compose.ui.platform.ClipEntry
|
||||||
import androidx.compose.ui.platform.Clipboard
|
import androidx.compose.ui.platform.Clipboard
|
||||||
import com.greenart7c3.nostrsigner.Amber
|
import com.greenart7c3.nostrsigner.Amber
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** How long a copied secret stays on the clipboard before it is cleared. */
|
/** How long a copied secret stays on the clipboard before it is cleared. */
|
||||||
const val SENSITIVE_CLIPBOARD_CLEAR_DELAY_MS = 60_000L
|
val SENSITIVE_CLIPBOARD_CLEAR_DELAY = 60.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a [ClipData] flagged as sensitive so the system (Android 13+) avoids
|
* Creates a [ClipData] flagged as sensitive so the system (Android 13+) avoids
|
||||||
@@ -34,7 +36,7 @@ fun newSensitivePlainText(label: CharSequence, text: CharSequence): ClipData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies a secret to the clipboard flagged as sensitive content and schedules it
|
* Copies a secret to the clipboard flagged as sensitive content and schedules it
|
||||||
* to be cleared after [clearAfterMillis]. The clipboard is only cleared if it
|
* to be cleared after [clearAfter]. The clipboard is only cleared if it
|
||||||
* still contains the copied secret, so anything the user copies afterwards is
|
* still contains the copied secret, so anything the user copies afterwards is
|
||||||
* left untouched.
|
* left untouched.
|
||||||
*/
|
*/
|
||||||
@@ -42,19 +44,19 @@ suspend fun Clipboard.setSensitiveClip(
|
|||||||
label: CharSequence,
|
label: CharSequence,
|
||||||
text: CharSequence,
|
text: CharSequence,
|
||||||
scope: CoroutineScope = Amber.instance.applicationIOScope,
|
scope: CoroutineScope = Amber.instance.applicationIOScope,
|
||||||
clearAfterMillis: Long = SENSITIVE_CLIPBOARD_CLEAR_DELAY_MS,
|
clearAfter: Duration = SENSITIVE_CLIPBOARD_CLEAR_DELAY,
|
||||||
) {
|
) {
|
||||||
setClipEntry(ClipEntry(newSensitivePlainText(label, text)))
|
setClipEntry(ClipEntry(newSensitivePlainText(label, text)))
|
||||||
scheduleSensitiveClear(text, scope, clearAfterMillis)
|
scheduleSensitiveClear(text, scope, clearAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Clipboard.scheduleSensitiveClear(
|
private fun Clipboard.scheduleSensitiveClear(
|
||||||
copiedValue: CharSequence,
|
copiedValue: CharSequence,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
delayMillis: Long,
|
clearAfter: Duration,
|
||||||
) {
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
delay(delayMillis)
|
delay(clearAfter)
|
||||||
val currentText = getClipEntry()?.clipData?.let { clip ->
|
val currentText = getClipEntry()?.clipData?.let { clip ->
|
||||||
if (clip.itemCount > 0) clip.getItemAt(0).text?.toString() else null
|
if (clip.itemCount > 0) clip.getItemAt(0).text?.toString() else null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
|||||||
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
import com.vitorpamplona.quartz.nip40Expiration.expiration
|
||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -177,7 +178,7 @@ fun TranslationReportScreen(
|
|||||||
Amber.instance.pendingTranslationReport.value = null
|
Amber.instance.pendingTranslationReport.value = null
|
||||||
onLoading(false)
|
onLoading(false)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
delay(10000)
|
delay(10.seconds)
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ import com.vitorpamplona.quartz.nip46RemoteSigner.NostrConnectEvent
|
|||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import java.util.Base64
|
import java.util.Base64
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -158,7 +160,7 @@ fun DefaultRelaysScreen(
|
|||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
if (!BuildFlavorChecker.isOfflineFlavor()) {
|
if (!BuildFlavorChecker.isOfflineFlavor()) {
|
||||||
Amber.instance.checkForNewRelaysAndUpdateAllFilters()
|
Amber.instance.checkForNewRelaysAndUpdateAllFilters()
|
||||||
delay(2000)
|
delay(2.seconds)
|
||||||
Amber.instance.client.reconnect()
|
Amber.instance.client.reconnect()
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
} else {
|
} else {
|
||||||
@@ -438,9 +440,9 @@ fun onAddRelay(
|
|||||||
mapOf(addedWSS to filters),
|
mapOf(addedWSS to filters),
|
||||||
)
|
)
|
||||||
|
|
||||||
val canContinue = withTimeoutOrNull(30000) {
|
val canContinue = withTimeoutOrNull(30.seconds) {
|
||||||
while (!canSendRequest) {
|
while (!canSendRequest) {
|
||||||
delay(200)
|
delay(200.milliseconds)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -469,7 +471,7 @@ fun onAddRelay(
|
|||||||
success = client.publishAndConfirm(signedEvent, setOf(addedWSS))
|
success = client.publishAndConfirm(signedEvent, setOf(addedWSS))
|
||||||
if (!success) {
|
if (!success) {
|
||||||
errorCount++
|
errorCount++
|
||||||
delay(1000)
|
delay(1.seconds)
|
||||||
signedEvent = signer.signerSync.sign(
|
signedEvent = signer.signerSync.sign(
|
||||||
TimeUtils.now(),
|
TimeUtils.now(),
|
||||||
NostrConnectEvent.KIND,
|
NostrConnectEvent.KIND,
|
||||||
@@ -483,7 +485,7 @@ fun onAddRelay(
|
|||||||
AmberListenerSingleton.latestErrorMessages.clear()
|
AmberListenerSingleton.latestErrorMessages.clear()
|
||||||
var count = 0
|
var count = 0
|
||||||
while (!filterResult && count < 10) {
|
while (!filterResult && count < 10) {
|
||||||
delay(1000)
|
delay(1.seconds)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.greenart7c3.nostrsigner
|
|||||||
|
|
||||||
import kotlin.system.measureNanoTime
|
import kotlin.system.measureNanoTime
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@@ -77,7 +78,7 @@ class SignerProviderBenchmarkTest {
|
|||||||
runBlocking {
|
runBlocking {
|
||||||
val warmup = MutableStateFlow(true)
|
val warmup = MutableStateFlow(true)
|
||||||
launch {
|
launch {
|
||||||
delay(1)
|
delay(1.milliseconds)
|
||||||
warmup.value = false
|
warmup.value = false
|
||||||
}
|
}
|
||||||
warmup.first { !it }
|
warmup.first { !it }
|
||||||
@@ -87,7 +88,7 @@ class SignerProviderBenchmarkTest {
|
|||||||
val state = MutableStateFlow(true)
|
val state = MutableStateFlow(true)
|
||||||
runBlocking {
|
runBlocking {
|
||||||
launch {
|
launch {
|
||||||
delay(releaseAfterMs)
|
delay(releaseAfterMs.milliseconds)
|
||||||
state.value = false
|
state.value = false
|
||||||
}
|
}
|
||||||
state.first { !it }
|
state.first { !it }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.greenart7c3.nostrsigner.models.SignerType
|
|||||||
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
|
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.joinAll
|
import kotlinx.coroutines.joinAll
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -344,8 +345,8 @@ class BunkerRequestUtilsTest {
|
|||||||
var calls = 0
|
var calls = 0
|
||||||
val result = BunkerRequestUtils.retryWithBackoff(
|
val result = BunkerRequestUtils.retryWithBackoff(
|
||||||
maxRetries = 3,
|
maxRetries = 3,
|
||||||
initialDelayMs = 1L,
|
initialDelay = 1.milliseconds,
|
||||||
maxDelayMs = 4L,
|
maxDelay = 4.milliseconds,
|
||||||
) {
|
) {
|
||||||
calls++
|
calls++
|
||||||
true
|
true
|
||||||
@@ -359,8 +360,8 @@ class BunkerRequestUtilsTest {
|
|||||||
var calls = 0
|
var calls = 0
|
||||||
val result = BunkerRequestUtils.retryWithBackoff(
|
val result = BunkerRequestUtils.retryWithBackoff(
|
||||||
maxRetries = 5,
|
maxRetries = 5,
|
||||||
initialDelayMs = 1L,
|
initialDelay = 1.milliseconds,
|
||||||
maxDelayMs = 4L,
|
maxDelay = 4.milliseconds,
|
||||||
) {
|
) {
|
||||||
calls++
|
calls++
|
||||||
calls >= 3
|
calls >= 3
|
||||||
@@ -374,8 +375,8 @@ class BunkerRequestUtilsTest {
|
|||||||
var calls = 0
|
var calls = 0
|
||||||
val result = BunkerRequestUtils.retryWithBackoff(
|
val result = BunkerRequestUtils.retryWithBackoff(
|
||||||
maxRetries = 3,
|
maxRetries = 3,
|
||||||
initialDelayMs = 1L,
|
initialDelay = 1.milliseconds,
|
||||||
maxDelayMs = 4L,
|
maxDelay = 4.milliseconds,
|
||||||
) {
|
) {
|
||||||
calls++
|
calls++
|
||||||
false
|
false
|
||||||
@@ -389,8 +390,8 @@ class BunkerRequestUtilsTest {
|
|||||||
var calls = 0
|
var calls = 0
|
||||||
val result = BunkerRequestUtils.retryWithBackoff(
|
val result = BunkerRequestUtils.retryWithBackoff(
|
||||||
maxRetries = 1,
|
maxRetries = 1,
|
||||||
initialDelayMs = 1L,
|
initialDelay = 1.milliseconds,
|
||||||
maxDelayMs = 4L,
|
maxDelay = 4.milliseconds,
|
||||||
) {
|
) {
|
||||||
calls++
|
calls++
|
||||||
false
|
false
|
||||||
@@ -403,8 +404,8 @@ class BunkerRequestUtilsTest {
|
|||||||
fun `retryWithBackoff with maxRetries 1 returns true on success`() = runBlocking {
|
fun `retryWithBackoff with maxRetries 1 returns true on success`() = runBlocking {
|
||||||
val result = BunkerRequestUtils.retryWithBackoff(
|
val result = BunkerRequestUtils.retryWithBackoff(
|
||||||
maxRetries = 1,
|
maxRetries = 1,
|
||||||
initialDelayMs = 1L,
|
initialDelay = 1.milliseconds,
|
||||||
maxDelayMs = 4L,
|
maxDelay = 4.milliseconds,
|
||||||
) { true }
|
) { true }
|
||||||
assertTrue(result)
|
assertTrue(result)
|
||||||
}
|
}
|
||||||
@@ -415,8 +416,8 @@ class BunkerRequestUtilsTest {
|
|||||||
var calls = 0
|
var calls = 0
|
||||||
BunkerRequestUtils.retryWithBackoff(
|
BunkerRequestUtils.retryWithBackoff(
|
||||||
maxRetries = maxRetries,
|
maxRetries = maxRetries,
|
||||||
initialDelayMs = 1L,
|
initialDelay = 1.milliseconds,
|
||||||
maxDelayMs = 4L,
|
maxDelay = 4.milliseconds,
|
||||||
) {
|
) {
|
||||||
calls++
|
calls++
|
||||||
false
|
false
|
||||||
|
|||||||
Reference in New Issue
Block a user