mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-06 19:33:01 +00:00
IntentUtils only treated a nostrsigner: request as coming from a web page when Chrome's Browser.EXTRA_APPLICATION_ID extra was present. Browsers like DuckDuckGo don't set it, so their URL-encoded requests were parsed as intent extras and failed with "Unknown signer type". The request is now parsed from the URI when Chrome's extra is present, or, if there is no native `type` extra, when the intent has the BROWSABLE category, the referrer is a browser (http(s) referrer or a package that handles https links), or the URI carries a `type=` query parameter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
172 lines
7.2 KiB
Kotlin
172 lines
7.2 KiB
Kotlin
package com.greenart7c3.nostrsigner
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
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?,
|
|
referrer: Uri? = null,
|
|
) {
|
|
viewModelScope.launch(Dispatchers.IO) {
|
|
val account = LocalPreferences.loadFromEncryptedStorage(context)
|
|
account?.let { acc ->
|
|
val intentData = IntentUtils.getIntentData(context, intent, callingPackage, intent.getStringExtra("route"), acc, referrer)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|