mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-05 10:58:23 +00:00
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
170 lines
7.2 KiB
Kotlin
170 lines
7.2 KiB
Kotlin
package com.greenart7c3.nostrsigner
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import androidx.compose.runtime.Stable
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import androidx.navigation.NavHostController
|
|
import com.greenart7c3.nostrsigner.service.BunkerRequestUtils
|
|
import com.greenart7c3.nostrsigner.service.IntentUtils
|
|
import com.greenart7c3.nostrsigner.ui.AccountStateViewModel
|
|
import com.greenart7c3.nostrsigner.ui.navigation.Route
|
|
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
|
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
|
import com.vitorpamplona.quartz.utils.Hex
|
|
import kotlin.time.Duration.Companion.milliseconds
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.launch
|
|
|
|
@Stable
|
|
@SuppressLint("StaticFieldLeak")
|
|
class MainViewModel(val context: Context) : ViewModel() {
|
|
var navController: NavHostController? = null
|
|
|
|
// Intent IDs added by this ViewModel instance — used by onDestroy to remove only
|
|
// the intents owned by this activity, avoiding races with a new SignerActivity
|
|
// instance that may have already added its own intents.
|
|
val addedIntentIds = mutableSetOf<String>()
|
|
|
|
fun getAccount(userFromIntent: String?): String? {
|
|
val currentAccount = LocalPreferences.currentAccount(context)
|
|
try {
|
|
if (!userFromIntent.isNullOrBlank()) {
|
|
if (userFromIntent.startsWith("npub")) {
|
|
if (LocalPreferences.containsAccount(context, userFromIntent)) {
|
|
AmberLog.d(Amber.TAG, "getAccount: $userFromIntent")
|
|
return userFromIntent
|
|
}
|
|
} else {
|
|
val localNpub = Hex.decode(userFromIntent).toNpub()
|
|
if (LocalPreferences.containsAccount(context, localNpub)) {
|
|
AmberLog.d(Amber.TAG, "getAccount: $localNpub")
|
|
return localNpub
|
|
}
|
|
}
|
|
}
|
|
|
|
val pubKeys =
|
|
IntentUtils.intents.value.mapNotNull {
|
|
it.event?.pubKey
|
|
}.filter { it.isNotBlank() } + BunkerRequestUtils.getBunkerRequests().mapNotNull {
|
|
when (val parsed = Nip19Parser.uriToRoute(it.currentAccount)?.entity) {
|
|
is NPub -> parsed.hex
|
|
else -> null
|
|
}
|
|
}
|
|
|
|
if (pubKeys.isEmpty()) {
|
|
if (currentAccount != null && LocalPreferences.containsAccount(context, currentAccount)) {
|
|
AmberLog.d(Amber.TAG, "getAccount: $currentAccount")
|
|
return currentAccount
|
|
}
|
|
|
|
val acc = LocalPreferences.allSavedAccounts(context).firstOrNull()
|
|
if (acc != null) {
|
|
AmberLog.d(Amber.TAG, "getAccount: ${acc.npub}")
|
|
return acc.npub
|
|
} else {
|
|
AmberLog.d(Amber.TAG, "getAccount: null")
|
|
return null
|
|
}
|
|
}
|
|
|
|
val npub = Hex.decode(pubKeys.first()).toNpub()
|
|
AmberLog.d(Amber.TAG, "getAccount: $npub")
|
|
return npub
|
|
} catch (e: Exception) {
|
|
AmberLog.e(Amber.TAG, "Error getting account", e)
|
|
if (currentAccount != null && LocalPreferences.containsAccount(context, currentAccount)) {
|
|
AmberLog.d(Amber.TAG, "getAccount: $currentAccount")
|
|
return currentAccount
|
|
}
|
|
|
|
val acc = LocalPreferences.allSavedAccounts(context).firstOrNull()
|
|
if (acc != null) {
|
|
AmberLog.d(Amber.TAG, "getAccount: ${acc.npub}")
|
|
return acc.npub
|
|
} else {
|
|
AmberLog.d(Amber.TAG, "getAccount: null")
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|
|
fun showBunkerRequests(accountStateViewModel: AccountStateViewModel? = null) {
|
|
val requests =
|
|
BunkerRequestUtils.getBunkerRequests().map {
|
|
it.copy()
|
|
}
|
|
|
|
viewModelScope.launch(Dispatchers.IO) {
|
|
if (requests.isNotEmpty()) {
|
|
val npub = getAccount(null)
|
|
val currentAccount = LocalPreferences.currentAccount(context)
|
|
if (currentAccount != null && npub != null && currentAccount != npub && npub.isNotBlank()) {
|
|
if (npub.startsWith("npub")) {
|
|
AmberLog.d(Amber.TAG, "Switching account to $npub")
|
|
if (LocalPreferences.containsAccount(context, npub)) {
|
|
accountStateViewModel?.switchUser(npub, Route.IncomingRequest.route)
|
|
}
|
|
} else {
|
|
val localNpub = Hex.decode(npub).toNpub()
|
|
AmberLog.d(Amber.TAG, "Switching account to $localNpub")
|
|
if (LocalPreferences.containsAccount(context, localNpub)) {
|
|
accountStateViewModel?.switchUser(localNpub, Route.IncomingRequest.route)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun onNewIntent(
|
|
intent: Intent,
|
|
callingPackage: String?,
|
|
) {
|
|
viewModelScope.launch(Dispatchers.IO) {
|
|
val account = LocalPreferences.loadFromEncryptedStorage(context)
|
|
account?.let { acc ->
|
|
val intentData = IntentUtils.getIntentData(context, intent, callingPackage, intent.getStringExtra("route"), acc)
|
|
if (intentData != null) {
|
|
IntentUtils.addAll(listOf(intentData))
|
|
addedIntentIds.add(intentData.id)
|
|
// The calling app is visible to us now; capture its icon/name.
|
|
IntentUtils.persistNativeAppMetadata(context, acc, callingPackage)
|
|
}
|
|
|
|
intent.getStringExtra("route")?.let { route ->
|
|
viewModelScope.launch(Dispatchers.Main) {
|
|
var error = true
|
|
var count = 0
|
|
while (error && count < 10) {
|
|
delay(100.milliseconds)
|
|
count++
|
|
try {
|
|
if (route == Route.UpdateSettings.route) {
|
|
navController?.navigate(Route.Settings.route) {
|
|
popUpTo(0)
|
|
}
|
|
navController?.navigate(Route.UpdateSettings.route)
|
|
} else {
|
|
navController?.navigate(route) {
|
|
popUpTo(0)
|
|
}
|
|
}
|
|
error = false
|
|
} catch (e: Exception) {
|
|
AmberLog.e(Amber.TAG, "Error navigating to $route", e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|