Separated BunkerRequests from Intents

This commit is contained in:
greenart7c3
2025-06-23 16:42:51 -03:00
parent 89fb618781
commit a32025bcce
19 changed files with 1091 additions and 958 deletions
@@ -50,8 +50,6 @@ import com.vitorpamplona.quartz.utils.Hex
import java.time.Duration
import java.time.Instant
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
fun Intent.isLaunchFromHistory(): Boolean =
@@ -221,41 +219,44 @@ class MainActivity : AppCompatActivity() {
CircularProgressIndicator()
}
} else {
var npub = remember { mainViewModel.getAccount(intent.getStringExtra("current_user")) }
val bunkerRequests = BunkerRequestUtils.state.collectAsStateWithLifecycle(emptyList())
var npub = remember { mainViewModel.getAccount(intent?.getStringExtra("current_user")) }
val accountStateViewModel: AccountStateViewModel =
viewModel {
AccountStateViewModel(npub)
}
LaunchedEffect(Unit) {
LaunchedEffect(Unit, bunkerRequests.value) {
launch(Dispatchers.IO) {
val localNpub = mainViewModel.getAccount(intent?.getStringExtra("current_user"))
val currentAccount = LocalPreferences.currentAccount(context)
if (currentAccount != null && npub != null && currentAccount != npub && npub.isNotBlank()) {
if (npub.startsWith("npub")) {
Log.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()
if (currentAccount != null && localNpub != null && currentAccount != localNpub && localNpub.isNotBlank()) {
if (localNpub.startsWith("npub")) {
Log.d(Amber.TAG, "Switching account to $localNpub")
if (LocalPreferences.containsAccount(context, localNpub)) {
accountStateViewModel.switchUser(localNpub, Route.IncomingRequest.route)
}
} else {
val localNpub2 = Hex.decode(localNpub).toNpub()
Log.d(Amber.TAG, "Switching account to $localNpub2")
if (LocalPreferences.containsAccount(context, localNpub2)) {
accountStateViewModel.switchUser(localNpub2, Route.IncomingRequest.route)
}
}
}
}
launch {
BunkerRequestUtils.state
.receiveAsFlow()
.collectLatest {
mainViewModel.showBunkerRequests(null, accountStateViewModel)
}
}
}
AccountScreen(accountStateViewModel, intent, packageName, appName, mainViewModel.intents, navController)
AccountScreen(
accountStateViewModel = accountStateViewModel,
intent = intent,
packageName = packageName,
appName = appName,
flow = mainViewModel.intents,
bunkerRequests = bunkerRequests.value,
navController = navController,
)
}
}
}
@@ -47,8 +47,8 @@ class MainViewModel(val context: Context) : ViewModel() {
val pubKeys =
intents.value.mapNotNull {
it.event?.pubKey ?: it.bunkerRequest?.currentAccount
}.filter { it.isNotBlank() }
it.event?.pubKey
}.filter { it.isNotBlank() } + BunkerRequestUtils.getBunkerRequests().map { it.currentAccount }
if (pubKeys.isEmpty()) {
if (currentAccount != null && LocalPreferences.containsAccount(context, currentAccount)) {
@@ -90,7 +90,7 @@ class MainViewModel(val context: Context) : ViewModel() {
fun showBunkerRequests(callingPackage: String?, accountStateViewModel: AccountStateViewModel? = null) {
val requests =
BunkerRequestUtils.getBunkerRequests().map {
it.value.copy()
it.copy()
}
viewModelScope.launch(Dispatchers.IO) {
@@ -1,5 +1,7 @@
package com.greenart7c3.nostrsigner.models
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
@@ -11,67 +13,45 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.ui.RememberType
import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
import kotlin.collections.asIterable
data class BunkerRequest(
val id: String,
val method: String,
val params: Array<String>,
var localKey: String,
var relays: List<RelaySetupInfo>,
var secret: String,
var currentAccount: String,
var encryptionType: EncryptionType,
var nostrConnectSecret: String,
var closeApplication: Boolean,
data class AmberBunkerRequest(
val request: BunkerRequest,
val localKey: String,
val relays: List<RelaySetupInfo>,
val currentAccount: String,
val encryptionType: EncryptionType,
val nostrConnectSecret: String,
val closeApplication: Boolean,
val name: String,
val signedEvent: Event?,
val encryptDecryptResponse: String?,
val checked: MutableState<Boolean> = mutableStateOf(true),
val rememberType: MutableState<RememberType> = mutableStateOf(RememberType.NEVER),
) {
fun toJson(): String {
return mapper.writeValueAsString(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as BunkerRequest
if (id != other.id) return false
if (method != other.method) return false
if (!params.contentEquals(other.params)) return false
if (localKey != other.localKey) return false
if (relays != other.relays) return false
if (secret != other.secret) return false
if (currentAccount != other.currentAccount) return false
if (encryptionType != other.encryptionType) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + method.hashCode()
result = 31 * result + params.contentHashCode()
result = 31 * result + localKey.hashCode()
result = 31 * result + relays.hashCode()
result = 31 * result + secret.hashCode()
result = 31 * result + currentAccount.hashCode()
result = 31 * result + encryptionType.hashCode()
return result
}
companion object {
val mapper: ObjectMapper =
jacksonObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerModule(
SimpleModule()
.addDeserializer(BunkerRequest::class.java, BunkerRequestDeserializer())
.addSerializer(BunkerRequest::class.java, BunkerRequestSerializer()),
.addDeserializer(AmberBunkerRequest::class.java, AmberBunkerRequestDeserializer())
.addSerializer(AmberBunkerRequest::class.java, AmberBunkerRequestSerializer()),
)
fun fromJson(jsonObject: JsonNode): BunkerRequest {
fun fromJson(jsonObject: JsonNode): AmberBunkerRequest {
val encryptionTypeString = jsonObject.get("encryptionType")?.asText()?.intern() ?: ""
val encryptionType = when (encryptionTypeString) {
"NIP44" -> EncryptionType.NIP44
@@ -79,60 +59,56 @@ data class BunkerRequest(
else -> EncryptionType.NIP04
}
return BunkerRequest(
id = jsonObject.get("id").asText().intern(),
method = jsonObject.get("method").asText().intern(),
params = jsonObject.get("params").asIterable().toList().map {
it.asText().intern()
}.toTypedArray(),
return AmberBunkerRequest(
request = EventMapper.mapper.readValue(jsonObject.get("request").asText().intern()),
localKey = jsonObject.get("localKey")?.asText()?.intern() ?: "",
relays = jsonObject.get("relays")?.asIterable()?.toList()?.map {
var relayUrl = it.asText().intern()
if (relayUrl.endsWith("/")) relayUrl = relayUrl.dropLast(1)
RelaySetupInfo(relayUrl, read = true, write = true, feedTypes = COMMON_FEED_TYPES)
} ?: Amber.instance.getSavedRelays().toList(),
secret = jsonObject.get("secret")?.asText()?.intern() ?: "",
currentAccount = jsonObject.get("currentAccount")?.asText()?.intern() ?: "",
encryptionType = encryptionType,
nostrConnectSecret = jsonObject.get("nostrConnectSecret")?.asText()?.intern() ?: "",
closeApplication = jsonObject.get("closeApplication")?.asBoolean() != false,
name = jsonObject.get("name")?.asText()?.intern() ?: "",
signedEvent = jsonObject.get("signedEvent")?.asText()?.intern()?.let {
EventMapper.fromJson(it)
},
encryptDecryptResponse = jsonObject.get("encryptDecryptResponse")?.asText()?.intern(),
)
}
private class BunkerRequestDeserializer : StdDeserializer<BunkerRequest>(BunkerRequest::class.java) {
private class AmberBunkerRequestDeserializer : StdDeserializer<AmberBunkerRequest>(AmberBunkerRequest::class.java) {
override fun deserialize(
jp: JsonParser,
ctxt: DeserializationContext,
): BunkerRequest {
): AmberBunkerRequest {
return fromJson(jp.codec.readTree(jp))
}
}
private class BunkerRequestSerializer : StdSerializer<BunkerRequest>(BunkerRequest::class.java) {
private class AmberBunkerRequestSerializer : StdSerializer<AmberBunkerRequest>(AmberBunkerRequest::class.java) {
override fun serialize(
value: BunkerRequest,
value: AmberBunkerRequest,
gen: JsonGenerator,
provider: SerializerProvider,
) {
gen.writeStartObject()
gen.writeStringField("id", value.id)
gen.writeStringField("method", value.method)
gen.writeArrayFieldStart("params")
value.params.forEach {
gen.writeString(it)
}
gen.writeEndArray()
gen.writeStringField("request", mapper.writeValueAsString(value.request))
gen.writeStringField("localKey", value.localKey)
gen.writeArrayFieldStart("relays")
value.relays.forEach {
gen.writeString(it.url)
}
gen.writeEndArray()
gen.writeStringField("secret", value.secret)
gen.writeStringField("currentAccount", value.currentAccount)
gen.writeStringField("encryptionType", value.encryptionType.toString())
gen.writeStringField("nostrConnectSecret", value.nostrConnectSecret)
gen.writeBooleanField("closeApplication", value.closeApplication)
gen.writeStringField("name", value.name)
gen.writeStringField("signedEvent", value.signedEvent?.toJson())
gen.writeStringField("encryptDecryptResponse", value.encryptDecryptResponse)
gen.writeEndObject()
}
}
@@ -18,7 +18,6 @@ data class IntentData(
val currentAccount: String,
val checked: MutableState<Boolean>,
val rememberType: MutableState<RememberType>,
val bunkerRequest: BunkerRequest?,
val route: String?,
val event: Event?,
val encryptedData: String?,
@@ -8,9 +8,7 @@ import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.database.ApplicationPermissionsEntity
import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.Permission
import com.greenart7c3.nostrsigner.models.SignerType
import com.greenart7c3.nostrsigner.models.basicPermissions
@@ -130,20 +128,18 @@ object AmberUtils {
}
fun sendBunkerError(
intentData: IntentData,
account: Account,
bunkerRequest: BunkerRequest,
bunkerRequest: AmberBunkerRequest,
relays: List<RelaySetupInfo>,
context: Context,
closeApplication: Boolean,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
onLoading: (Boolean) -> Unit,
) {
BunkerRequestUtils.sendBunkerResponse(
context,
account,
bunkerRequest,
BunkerResponse(bunkerRequest.id, "", "user rejected"),
BunkerResponse(bunkerRequest.request.id, "", "user rejected"),
relays,
onLoading = onLoading,
onDone = { result ->
@@ -152,7 +148,6 @@ object AmberUtils {
} else {
BunkerRequestUtils.clearRequests()
EventNotificationConsumer(context).notificationManager().cancelAll()
onRemoveIntentData(listOf(intentData), IntentResultType.REMOVE)
val activity = Amber.instance.getMainActivity()
activity?.intent = null
if (closeApplication) {
@@ -1,11 +1,8 @@
package com.greenart7c3.nostrsigner.service
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.database.ApplicationEntity
import com.greenart7c3.nostrsigner.database.ApplicationPermissionsEntity
@@ -13,57 +10,49 @@ import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions
import com.greenart7c3.nostrsigner.database.HistoryEntity2
import com.greenart7c3.nostrsigner.database.LogEntity
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.CompressionType
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.EncryptionType
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.models.Permission
import com.greenart7c3.nostrsigner.models.ReturnType
import com.greenart7c3.nostrsigner.models.SignerType
import com.greenart7c3.nostrsigner.models.containsNip
import com.greenart7c3.nostrsigner.relays.AmberListenerSingleton
import com.greenart7c3.nostrsigner.service.IntentUtils.getUnsignedEvent
import com.greenart7c3.nostrsigner.service.model.AmberEvent
import com.greenart7c3.nostrsigner.ui.RememberType
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponse
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
object BunkerRequestUtils {
val state = Channel<String>(Channel.CONFLATED)
private val bunkerRequests = ConcurrentHashMap<String, BunkerRequest>()
val state = MutableStateFlow(listOf<AmberBunkerRequest>())
fun addRequest(request: BunkerRequest) {
bunkerRequests[request.id] = request
state.trySend("")
fun addRequest(request: AmberBunkerRequest) {
if (state.value.any { it.request.id == request.request.id }) return
state.tryEmit(state.value + request)
}
fun clearRequests() {
bunkerRequests.clear()
state.trySend("")
state.tryEmit(emptyList())
}
fun remove(id: String) {
bunkerRequests.remove(id)
state.trySend("")
state.tryEmit(state.value.filter { it.request.id != id })
}
fun getBunkerRequests(): Map<String, BunkerRequest> {
return bunkerRequests
fun getBunkerRequests(): List<AmberBunkerRequest> {
return state.value
}
fun sendBunkerResponse(
context: Context,
account: Account,
bunkerRequest: BunkerRequest,
bunkerRequest: AmberBunkerRequest,
bunkerResponse: BunkerResponse,
relays: List<RelaySetupInfo>,
onLoading: (Boolean) -> Unit,
@@ -158,7 +147,7 @@ object BunkerRequestUtils {
}
private fun sendBunkerResponse(
bunkerRequest: BunkerRequest? = null,
bunkerRequest: AmberBunkerRequest,
account: Account,
localKey: String,
encryptedContent: String,
@@ -173,7 +162,7 @@ object BunkerRequestUtils {
encryptedContent,
) {
Amber.instance.applicationIOScope.launch {
Log.d(Amber.TAG, "Sending response to relays ${relays.map { it.url }} type ${bunkerRequest?.method}")
Log.d(Amber.TAG, "Sending response to relays ${relays.map { it.url }} type ${bunkerRequest.request.method}")
if (Amber.instance.client.getAll().any { !it.isConnected() }) {
Amber.instance.checkForNewRelays(
@@ -196,12 +185,12 @@ object BunkerRequestUtils {
}
}
if (success) {
Log.d(Amber.TAG, "Success response to relays ${relays.map { it.url }} type ${bunkerRequest?.method}")
Log.d(Amber.TAG, "Success response to relays ${relays.map { it.url }} type ${bunkerRequest.request.method}")
onDone(true)
} else {
onDone(false)
AmberListenerSingleton.showErrorMessage()
Log.d(Amber.TAG, "Failed response to relays ${relays.map { it.url }} type ${bunkerRequest?.method}")
Log.d(Amber.TAG, "Failed response to relays ${relays.map { it.url }} type ${bunkerRequest.request.method}")
}
AmberListenerSingleton.getListener()?.let {
Amber.instance.client.unsubscribe(it)
@@ -221,6 +210,7 @@ object BunkerRequestUtils {
"nip04_decrypt" -> SignerType.NIP04_DECRYPT
"nip44_encrypt" -> SignerType.NIP44_ENCRYPT
"nip44_decrypt" -> SignerType.NIP44_DECRYPT
"decrypt_zap_event" -> SignerType.DECRYPT_ZAP_EVENT
else -> SignerType.INVALID
}
}
@@ -233,222 +223,17 @@ object BunkerRequestUtils {
val amberEvent = AmberEvent.fromJson(bunkerRequest.params.first())
AmberEvent.toEvent(amberEvent).toJson()
}
"nip04_encrypt", "nip04_decrypt", "nip44_encrypt", "nip44_decrypt" -> bunkerRequest.params.getOrElse(1) { "" }
"nip04_encrypt", "nip04_decrypt", "nip44_encrypt", "nip44_decrypt", "decrypt_zap_event" -> bunkerRequest.params.getOrElse(1) { "" }
else -> ""
}
}
fun getIntentDataFromBunkerRequest(
context: Context,
intent: Intent,
bunkerRequest: BunkerRequest,
route: String?,
onReady: (IntentData?) -> Unit,
) {
val type = getTypeFromBunker(bunkerRequest)
if (type == SignerType.INVALID) {
Log.d(Amber.TAG, "Invalid type ${bunkerRequest.method}")
onReady(null)
return
}
val data = getDataFromBunker(bunkerRequest)
val account = LocalPreferences.loadFromEncryptedStorageSync(context, bunkerRequest.currentAccount)!!
val pubKey =
if (bunkerRequest.method.endsWith("encrypt") || bunkerRequest.method.endsWith("decrypt")) {
bunkerRequest.params.first()
} else {
""
}
val id = bunkerRequest.id
val permissions = mutableListOf<Permission>()
if (type == SignerType.CONNECT && bunkerRequest.params.size > 2) {
val split = bunkerRequest.params[2].split(",")
split.forEach {
if (it.isNotBlank()) {
val split2 = it.split(":")
val permissionType = split2.first()
val kind =
try {
split2[1].toInt()
} catch (_: Exception) {
null
}
permissions.add(
Permission(
permissionType,
kind,
),
)
}
}
}
permissions.removeIf { it.kind == null && (it.type == "sign_event" || it.type == "nip") }
permissions.removeIf { it.type == "nip" && (it.kind == null || !it.kind.containsNip()) }
@Suppress("KotlinConstantConditions")
when (type) {
SignerType.CONNECT -> {
onReady(
IntentData(
data,
"",
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
CompressionType.NONE,
ReturnType.EVENT,
permissions,
bunkerRequest.currentAccount,
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
bunkerRequest,
route,
null,
null,
),
)
}
SignerType.SIGN_EVENT -> {
val unsignedEvent = getUnsignedEvent(data, account)
account.signer.sign<Event>(
unsignedEvent.createdAt,
unsignedEvent.kind,
unsignedEvent.tags,
unsignedEvent.content,
) {
onReady(
IntentData(
data,
"",
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
CompressionType.NONE,
ReturnType.EVENT,
permissions,
bunkerRequest.currentAccount,
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
bunkerRequest,
route,
it,
null,
),
)
}
}
SignerType.NIP04_ENCRYPT, SignerType.NIP04_DECRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> {
val result =
try {
AmberUtils.encryptOrDecryptData(
data,
type,
account,
pubKey,
) ?: "Could not decrypt the message"
} catch (e: Exception) {
Amber.instance.applicationIOScope.launch {
val database = Amber.instance.getDatabase(account.npub)
database.applicationDao().insertLog(
LogEntity(
0,
bunkerRequest.localKey,
type.toString(),
e.message ?: "Could not decrypt the message",
System.currentTimeMillis(),
),
)
}
"Could not decrypt the message"
}
onReady(
IntentData(
data,
"",
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
CompressionType.NONE,
ReturnType.EVENT,
permissions,
bunkerRequest.currentAccount,
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
bunkerRequest,
route,
null,
result,
),
)
}
SignerType.GET_PUBLIC_KEY -> {
onReady(
IntentData(
account.npub,
"",
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
CompressionType.NONE,
ReturnType.EVENT,
permissions,
bunkerRequest.currentAccount,
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
bunkerRequest,
route,
null,
null,
),
)
}
SignerType.SIGN_MESSAGE -> {
onReady(
IntentData(
data,
"",
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
CompressionType.NONE,
ReturnType.EVENT,
permissions,
bunkerRequest.currentAccount,
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
bunkerRequest,
route,
null,
null,
),
)
}
SignerType.INVALID -> {
onReady(null)
}
}
}
fun sendResult(
context: Context,
account: Account,
key: String,
event: String,
bunkerRequest: BunkerRequest,
response: String,
bunkerRequest: AmberBunkerRequest,
kind: Int?,
onLoading: (Boolean) -> Unit,
permissions: List<Permission>? = null,
@@ -456,8 +241,6 @@ object BunkerRequestUtils {
signPolicy: Int? = null,
shouldCloseApplication: Boolean? = null,
rememberType: RememberType,
intentData: IntentData,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
oldKey: String = "",
) {
onLoading(true)
@@ -471,8 +254,8 @@ object BunkerRequestUtils {
}
var savedApplication = database.applicationDao().getByKey(key)
if (savedApplication == null && bunkerRequest.secret.isNotBlank()) {
savedApplication = database.applicationDao().getByKey(bunkerRequest.secret)
if (savedApplication == null && bunkerRequest.request is BunkerRequestConnect && bunkerRequest.request.secret?.isNotBlank() == true) {
savedApplication = database.applicationDao().getByKey(bunkerRequest.request.secret!!)
if (savedApplication != null) {
val tempApplication = savedApplication.application.copy(
key = key,
@@ -484,7 +267,7 @@ object BunkerRequestUtils {
}.toMutableList(),
)
database.applicationDao().delete(bunkerRequest.secret)
database.applicationDao().delete(bunkerRequest.request.secret!!)
database.applicationDao().insertApplicationWithPermissions(tempApp2)
}
}
@@ -494,6 +277,8 @@ object BunkerRequestUtils {
newRelays = relays.toSet(),
)
val secret = if (bunkerRequest.request is BunkerRequestConnect) bunkerRequest.request.secret ?: "" else ""
val application =
savedApplication ?: ApplicationWithPermissions(
application = ApplicationEntity(
@@ -505,8 +290,8 @@ object BunkerRequestUtils {
"",
account.hexKey,
true,
bunkerRequest.secret,
bunkerRequest.secret.isNotBlank(),
secret,
secret.isNotBlank(),
account.signPolicy,
shouldCloseApplication ?: bunkerRequest.closeApplication,
),
@@ -518,7 +303,7 @@ object BunkerRequestUtils {
AmberUtils.configureSignPolicy(application, signPolicy, key, permissions)
}
val type = getTypeFromBunker(bunkerRequest)
val type = getTypeFromBunker(bunkerRequest.request)
if (rememberType != RememberType.NEVER) {
AmberUtils.acceptPermission(
application = application,
@@ -551,9 +336,8 @@ object BunkerRequestUtils {
}
}
val localIntentData = intentData.copy()
val localBunkerRequest = bunkerRequest.copy()
clearRequests()
onRemoveIntentData(listOf(intentData), IntentResultType.REMOVE)
// assume that everything worked and try to revert it if it fails
EventNotificationConsumer(context).notificationManager().cancelAll()
@@ -574,7 +358,7 @@ object BunkerRequestUtils {
context,
account,
bunkerRequest,
BunkerResponse(bunkerRequest.id, event, null),
BunkerResponse(bunkerRequest.request.id, response, null),
application.application.relays.ifEmpty { relays },
onLoading,
onDone = {
@@ -591,8 +375,7 @@ object BunkerRequestUtils {
}
onLoading(false)
addRequest(localIntentData.bunkerRequest!!)
onRemoveIntentData(listOf(localIntentData), IntentResultType.ADD)
addRequest(localBunkerRequest)
} else {
val activity = Amber.instance.getMainActivity()
activity?.intent = null
@@ -609,19 +392,20 @@ object BunkerRequestUtils {
fun sendRejection(
key: String,
account: Account,
bunkerRequest: BunkerRequest,
bunkerRequest: AmberBunkerRequest,
appName: String,
rememberType: RememberType,
signerType: SignerType,
kind: Int?,
intentData: IntentData,
onLoading: (Boolean) -> Unit,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
) {
Amber.instance.applicationIOScope.launch(Dispatchers.IO) {
val savedApplication = Amber.instance.getDatabase(account.npub).applicationDao().getByKey(key)
val defaultRelays = Amber.instance.settings.defaultRelays
val relays = savedApplication?.application?.relays?.ifEmpty { defaultRelays } ?: bunkerRequest.relays.ifEmpty { defaultRelays }
val secret = if (bunkerRequest.request is BunkerRequestConnect) bunkerRequest.request.secret ?: "" else ""
val application =
savedApplication ?: ApplicationWithPermissions(
application = ApplicationEntity(
@@ -633,8 +417,8 @@ object BunkerRequestUtils {
"",
account.hexKey,
true,
bunkerRequest.secret,
bunkerRequest.secret.isNotBlank(),
secret,
secret.isNotBlank(),
account.signPolicy,
bunkerRequest.closeApplication,
),
@@ -657,27 +441,27 @@ object BunkerRequestUtils {
newRelays = relays.toSet(),
)
Amber.instance.getDatabase(account.npub).applicationDao().insertApplicationWithPermissions(application)
Amber.instance.getDatabase(account.npub).applicationDao().addHistory(
HistoryEntity2(
0,
key,
signerType.toString(),
null,
TimeUtils.now(),
false,
),
)
if (bunkerRequest.request !is BunkerRequestConnect) {
Amber.instance.getDatabase(account.npub).applicationDao().insertApplicationWithPermissions(application)
Amber.instance.getDatabase(account.npub).applicationDao().addHistory(
HistoryEntity2(
0,
key,
signerType.toString(),
null,
TimeUtils.now(),
false,
),
)
}
AmberUtils.sendBunkerError(
intentData,
account,
bunkerRequest,
relays,
Amber.instance,
closeApplication = application.application.closeApplication,
onLoading = onLoading,
onRemoveIntentData = onRemoveIntentData,
)
}
}
@@ -36,7 +36,7 @@ import com.greenart7c3.nostrsigner.database.HistoryEntity2
import com.greenart7c3.nostrsigner.database.LogEntity
import com.greenart7c3.nostrsigner.database.NotificationEntity
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.EncryptionType
import com.greenart7c3.nostrsigner.models.Permission
import com.greenart7c3.nostrsigner.models.SignerType
@@ -46,11 +46,20 @@ import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES
import com.vitorpamplona.ammolite.relays.Relay
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip01Core.verify
import com.vitorpamplona.quartz.nip04Dm.crypto.EncryptedInfo
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip04Decrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip04Encrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip44Decrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip44Encrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestSign
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponse
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.UUID
import kotlinx.coroutines.launch
class EventNotificationConsumer(private val applicationContext: Context) {
@@ -158,10 +167,32 @@ class EventNotificationConsumer(private val applicationContext: Context) {
),
)
val bunkerRequest = BunkerRequest.mapper.readValue(request, BunkerRequest::class.java)
bunkerRequest.localKey = event.pubKey
bunkerRequest.currentAccount = acc.npub
bunkerRequest.encryptionType = encryptionType
val bunkerRequest = EventMapper.mapper.readValue(request, BunkerRequest::class.java)
val signedEvent = if (bunkerRequest is BunkerRequestSign) {
acc.signer.signerSync.sign(bunkerRequest.event)
} else {
null
}
val encryptDecryptResponse = if (bunkerRequest is BunkerRequestNip44Decrypt) {
acc.signer.signerSync.nip44Decrypt(bunkerRequest.ciphertext, bunkerRequest.pubKey)
} else if (bunkerRequest is BunkerRequestNip44Encrypt) {
acc.signer.signerSync.nip44Encrypt(bunkerRequest.message, bunkerRequest.pubKey)
} else if (bunkerRequest is BunkerRequestNip04Decrypt) {
acc.signer.signerSync.nip04Decrypt(bunkerRequest.ciphertext, bunkerRequest.pubKey)
} else if (bunkerRequest is BunkerRequestNip04Encrypt) {
acc.signer.signerSync.nip04Encrypt(bunkerRequest.message, bunkerRequest.pubKey)
} else if (bunkerRequest.method == "decrypt_zap_event") {
AmberUtils.encryptOrDecryptData(
bunkerRequest.params[1],
SignerType.DECRYPT_ZAP_EVENT,
acc,
bunkerRequest.params.first(),
)
} else {
null
}
val type = BunkerRequestUtils.getTypeFromBunker(bunkerRequest)
if (type == SignerType.INVALID) {
@@ -178,13 +209,26 @@ class EventNotificationConsumer(private val applicationContext: Context) {
return
}
val data = BunkerRequestUtils.getDataFromBunker(bunkerRequest)
val request = AmberBunkerRequest(
request = bunkerRequest,
localKey = event.pubKey,
relays = responseRelay,
currentAccount = acc.hexKey,
encryptionType = encryptionType,
nostrConnectSecret = "",
closeApplication = true,
name = "",
signedEvent = signedEvent,
encryptDecryptResponse = encryptDecryptResponse,
)
var amberEvent: AmberEvent? = null
val pubKey =
if (bunkerRequest.method.endsWith("encrypt") || bunkerRequest.method.endsWith("decrypt")) {
bunkerRequest.params.first()
} else if (bunkerRequest.method == "decrypt_zap_event") {
bunkerRequest.params[1]
} else if (bunkerRequest.method == "sign_event") {
amberEvent = AmberEvent.fromJson(bunkerRequest.params.first())
amberEvent.pubKey
@@ -192,7 +236,7 @@ class EventNotificationConsumer(private val applicationContext: Context) {
""
}
val permission = dao.getByKey(bunkerRequest.localKey)
val permission = dao.getByKey(event.pubKey)
if (permission != null && ((permission.application.secret != permission.application.key && permission.application.useSecret) || permission.application.isConnected) && type == SignerType.CONNECT) {
Amber.instance.applicationIOScope.launch {
dao
@@ -208,11 +252,11 @@ class EventNotificationConsumer(private val applicationContext: Context) {
)
}
BunkerRequestUtils.sendBunkerResponse(
applicationContext,
acc,
bunkerRequest,
BunkerResponse(bunkerRequest.id, bunkerRequest.nostrConnectSecret.ifBlank { "ack" }, null),
permission.application.relays,
context = applicationContext,
account = acc,
bunkerRequest = request,
bunkerResponse = BunkerResponse(bunkerRequest.id, request.nostrConnectSecret.ifBlank { "ack" }, null),
relays = permission.application.relays,
onLoading = {},
onDone = {},
)
@@ -220,14 +264,9 @@ class EventNotificationConsumer(private val applicationContext: Context) {
}
var applicationWithSecret: ApplicationWithPermissions? = null
if (type == SignerType.CONNECT) {
val secret =
try {
bunkerRequest.params[1]
} catch (_: Exception) {
""
}
bunkerRequest.secret = secret
if (bunkerRequest is BunkerRequestConnect) {
val secret = bunkerRequest.secret ?: UUID.randomUUID().toString()
applicationWithSecret = dao.getBySecret(secret)
if (applicationWithSecret == null || secret.isBlank() || applicationWithSecret.application.isConnected || !applicationWithSecret.application.useSecret) {
val message = if (applicationWithSecret == null) {
@@ -240,11 +279,11 @@ class EventNotificationConsumer(private val applicationContext: Context) {
"secret not in use"
}
BunkerRequestUtils.sendBunkerResponse(
applicationContext,
acc,
bunkerRequest,
BunkerResponse(bunkerRequest.id, "", message),
applicationWithSecret?.application?.relays ?: responseRelay,
context = applicationContext,
account = acc,
bunkerRequest = request,
bunkerResponse = BunkerResponse(bunkerRequest.id, "", message),
relays = applicationWithSecret?.application?.relays ?: responseRelay,
onLoading = { },
onDone = { },
)
@@ -255,23 +294,24 @@ class EventNotificationConsumer(private val applicationContext: Context) {
val relays = permission?.application?.relays ?: applicationWithSecret?.application?.relays ?: responseRelay
if (permission == null && applicationWithSecret == null) {
BunkerRequestUtils.sendBunkerResponse(
applicationContext,
acc,
bunkerRequest,
BunkerResponse(bunkerRequest.id, "", "no permission"),
relays,
context = applicationContext,
account = acc,
bunkerRequest = request,
bunkerResponse = BunkerResponse(bunkerRequest.id, "", "no permission"),
relays = relays,
onLoading = { },
onDone = { },
)
return
}
val data = BunkerRequestUtils.getDataFromBunker(bunkerRequest)
val cursor =
applicationContext.contentResolver.query(
"content://${BuildConfig.APPLICATION_ID}.$type".toUri(),
arrayOf(data, pubKey, acc.npub),
"1",
null,
bunkerRequest.localKey,
event.pubKey,
)
var name = event.pubKey.toShortenHex()
if (permission != null && permission.application.name.isNotBlank()) {
@@ -297,17 +337,17 @@ class EventNotificationConsumer(private val applicationContext: Context) {
"Bunker",
"BunkerID",
applicationContext,
bunkerRequest,
request,
)
} else {
if (localCursor.moveToFirst()) {
if (localCursor.getColumnIndex("rejected") > -1) {
BunkerRequestUtils.sendBunkerResponse(
applicationContext,
acc,
bunkerRequest,
BunkerResponse(bunkerRequest.id, "", "user rejected"),
relays,
context = applicationContext,
account = acc,
bunkerRequest = request,
bunkerResponse = BunkerResponse(bunkerRequest.id, "", "user rejected"),
relays = relays,
onLoading = { },
onDone = { },
)
@@ -319,11 +359,11 @@ class EventNotificationConsumer(private val applicationContext: Context) {
val result = localCursor.getString(index)
BunkerRequestUtils.sendBunkerResponse(
applicationContext,
acc,
bunkerRequest,
BunkerResponse(bunkerRequest.id, result, null),
relays,
context = applicationContext,
account = acc,
bunkerRequest = request,
bunkerResponse = BunkerResponse(bunkerRequest.id, result, null),
relays = relays,
onLoading = { },
onDone = { },
)
@@ -336,7 +376,7 @@ class EventNotificationConsumer(private val applicationContext: Context) {
"Bunker",
"BunkerID",
applicationContext,
bunkerRequest,
request,
)
}
}
@@ -25,7 +25,7 @@ import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions
import com.greenart7c3.nostrsigner.database.HistoryEntity2
import com.greenart7c3.nostrsigner.database.LogEntity
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.CompressionType
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
@@ -149,22 +149,21 @@ object IntentUtils {
) {
onReady(
IntentData(
localData,
appName,
type,
pubKey,
"",
callbackUrl,
compressionType,
returnType,
listOf(),
Hex.decode(it.pubKey).toNpub(),
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
it,
null,
data = localData,
name = appName,
type = type,
pubKey = pubKey,
id = "",
callBackUrl = callbackUrl,
compression = compressionType,
returnType = returnType,
permissions = listOf(),
currentAccount = Hex.decode(it.pubKey).toNpub(),
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = it,
encryptedData = null,
),
)
}
@@ -196,66 +195,63 @@ object IntentUtils {
onReady(
IntentData(
localData,
appName,
type,
pubKey,
"",
callbackUrl,
compressionType,
returnType,
listOf(),
Hex.decode(pubKey).toNpub(),
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
null,
result,
data = localData,
name = appName,
type = type,
pubKey = pubKey,
id = "",
callBackUrl = callbackUrl,
compression = compressionType,
returnType = returnType,
permissions = listOf(),
currentAccount = Hex.decode(pubKey).toNpub(),
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = null,
encryptedData = result,
),
)
}
SignerType.GET_PUBLIC_KEY -> {
onReady(
IntentData(
localData,
appName,
type,
pubKey,
"",
callbackUrl,
compressionType,
returnType,
listOf(),
"",
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
null,
null,
data = localData,
name = appName,
type = type,
pubKey = pubKey,
id = "",
callBackUrl = callbackUrl,
compression = compressionType,
returnType = returnType,
permissions = listOf(),
currentAccount = "",
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = null,
encryptedData = null,
),
)
}
SignerType.SIGN_MESSAGE -> {
onReady(
IntentData(
localData,
appName,
type,
pubKey,
"",
callbackUrl,
compressionType,
returnType,
listOf(),
"",
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
null,
null,
data = localData,
name = appName,
type = type,
pubKey = pubKey,
id = "",
callBackUrl = callbackUrl,
compression = compressionType,
returnType = returnType,
permissions = listOf(),
currentAccount = "",
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = null,
encryptedData = null,
),
)
}
@@ -338,22 +334,21 @@ object IntentUtils {
onReady(
IntentData(
data,
name,
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
compressionType,
returnType,
permissions,
npub ?: Hex.decode(it.pubKey).toNpub(),
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
it,
null,
data = data,
name = name,
type = type,
pubKey = pubKey,
id = id,
callBackUrl = intent.extras?.getString("callbackUrl"),
compression = compressionType,
returnType = returnType,
permissions = permissions,
currentAccount = npub ?: Hex.decode(it.pubKey).toNpub(),
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = it,
encryptedData = null,
),
)
}
@@ -390,22 +385,21 @@ object IntentUtils {
onReady(
IntentData(
data,
name,
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
compressionType,
returnType,
permissions,
npub ?: Hex.decode(pubKey).toNpub(),
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
null,
result,
data = data,
name = name,
type = type,
pubKey = pubKey,
id = id,
callBackUrl = intent.extras?.getString("callbackUrl"),
compression = compressionType,
returnType = returnType,
permissions = permissions,
currentAccount = npub ?: Hex.decode(pubKey).toNpub(),
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = null,
encryptedData = result,
),
)
}
@@ -417,22 +411,21 @@ object IntentUtils {
onReady(
IntentData(
data,
name,
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
compressionType,
returnType,
permissions,
npub ?: Hex.decode(pubKey).toNpub(),
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
null,
null,
data = data,
name = name,
type = type,
pubKey = pubKey,
id = id,
callBackUrl = intent.extras?.getString("callbackUrl"),
compression = compressionType,
returnType = returnType,
permissions = permissions,
currentAccount = npub ?: Hex.decode(pubKey).toNpub(),
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = null,
encryptedData = null,
),
)
}
@@ -444,22 +437,21 @@ object IntentUtils {
onReady(
IntentData(
data,
name,
type,
pubKey,
id,
intent.extras?.getString("callbackUrl"),
compressionType,
returnType,
permissions,
npub ?: Hex.decode(pubKey).toNpub(),
mutableStateOf(true),
mutableStateOf(RememberType.NEVER),
null,
route,
null,
null,
data = data,
name = name,
type = type,
pubKey = pubKey,
id = id,
callBackUrl = intent.extras?.getString("callbackUrl"),
compression = compressionType,
returnType = returnType,
permissions = permissions,
currentAccount = npub ?: Hex.decode(pubKey).toNpub(),
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
route = route,
event = null,
encryptedData = null,
),
)
}
@@ -496,9 +488,9 @@ object IntentUtils {
val bunkerRequest =
if (intent.getStringExtra("bunker") != null) {
BunkerRequest.mapper.readValue(
AmberBunkerRequest.mapper.readValue(
intent.getStringExtra("bunker"),
BunkerRequest::class.java,
AmberBunkerRequest::class.java,
)
} else {
null
@@ -520,9 +512,9 @@ object IntentUtils {
}
if (intent.dataString?.startsWith("nostrconnect:") == true) {
NostrConnectUtils.getIntentFromNostrConnect(intent, route, localAccount, onReady)
NostrConnectUtils.getIntentFromNostrConnect(intent, localAccount)
} else if (bunkerRequest != null) {
BunkerRequestUtils.getIntentDataFromBunkerRequest(context, intent, bunkerRequest, route, onReady)
BunkerRequestUtils.addRequest(bunkerRequest)
} else if (intent.extras?.getString(Browser.EXTRA_APPLICATION_ID) == null) {
getIntentDataFromIntent(context, intent, packageName, route, localAccount, onReady)
} else {
@@ -775,10 +767,10 @@ object IntentUtils {
"",
account.hexKey,
true,
intentData.bunkerRequest?.secret ?: "",
intentData.bunkerRequest?.secret != null,
"",
false,
account.signPolicy,
intentData.bunkerRequest?.closeApplication != false,
true,
),
permissions = mutableListOf(),
)
@@ -1,8 +1,10 @@
package com.greenart7c3.nostrsigner.service
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
object MultiEventScreenIntents {
var intents = listOf<IntentData>()
var bunkerRequests = listOf<AmberBunkerRequest>()
var appName = ""
}
@@ -2,23 +2,17 @@ package com.greenart7c3.nostrsigner.service
import android.content.Intent
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.database.LogEntity
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.BunkerMetadata
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.CompressionType
import com.greenart7c3.nostrsigner.models.EncryptionType
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.Permission
import com.greenart7c3.nostrsigner.models.ReturnType
import com.greenart7c3.nostrsigner.models.SignerType
import com.greenart7c3.nostrsigner.models.containsNip
import com.greenart7c3.nostrsigner.ui.RememberType
import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import java.util.UUID
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect
import kotlinx.coroutines.launch
object NostrConnectUtils {
@@ -28,9 +22,7 @@ object NostrConnectUtils {
fun getIntentFromNostrConnect(
intent: Intent,
route: String?,
account: Account,
onReady: (IntentData?) -> Unit,
) {
try {
val data = intent.dataString.toString().replace("nostrconnect://", "")
@@ -115,36 +107,26 @@ object NostrConnectUtils {
permissions.removeIf { it.kind == null && (it.type == "sign_event" || it.type == "nip") }
permissions.removeIf { it.type == "nip" && (it.kind == null || !it.kind.containsNip()) }
onReady(
IntentData(
data = "ack",
name = name,
type = SignerType.CONNECT,
pubKey = pubKey,
id = "",
callBackUrl = null,
compression = CompressionType.NONE,
returnType = ReturnType.EVENT,
permissions = permissions,
currentAccount = "",
checked = mutableStateOf(true),
rememberType = mutableStateOf(RememberType.NEVER),
bunkerRequest = BunkerRequest(
id = UUID.randomUUID().toString().substring(0, 4),
method = "connect",
params = arrayOf(pubKey),
localKey = pubKey,
relays = relays.ifEmpty { Amber.instance.getSavedRelays().toList() },
BunkerRequestUtils.addRequest(
AmberBunkerRequest(
BunkerRequestConnect(
remoteKey = pubKey,
secret = "",
currentAccount = account.npub,
encryptionType = EncryptionType.NIP04,
nostrConnectSecret = nostrConnectSecret,
closeApplication = intent.getBooleanExtra("closeApplication", true),
permissions = if (permissions.isNotEmpty()) {
permissions.joinToString { if (it.kind != null) "${it.type}:${it.kind}" else it.type }
} else {
null
},
),
route = route,
event = null,
encryptedData = null,
isNostrConnectURI = true,
localKey = pubKey,
relays = relays.ifEmpty { Amber.instance.getSavedRelays().toList() },
currentAccount = account.hexKey,
encryptionType = EncryptionType.NIP04,
nostrConnectSecret = nostrConnectSecret,
closeApplication = intent.getBooleanExtra("closeApplication", true),
name = name,
signedEvent = null,
encryptDecryptResponse = null,
),
)
} catch (e: Exception) {
@@ -160,7 +142,6 @@ object NostrConnectUtils {
),
)
}
onReady(null)
}
}
}
@@ -31,7 +31,7 @@ import android.service.notification.StatusBarNotification
import androidx.core.app.NotificationCompat
import com.greenart7c3.nostrsigner.MainActivity
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.ui.navigation.Route
object NotificationUtils {
@@ -65,7 +65,7 @@ object NotificationUtils {
messageTitle: String,
channelId: String,
applicationContext: Context,
bunkerRequest: BunkerRequest,
bunkerRequest: AmberBunkerRequest,
) {
sendNotification(
id = id,
@@ -85,7 +85,7 @@ object NotificationUtils {
picture: BitmapDrawable?,
channelId: String,
applicationContext: Context,
bunkerRequest: BunkerRequest,
bunkerRequest: AmberBunkerRequest,
) {
val notId = id.hashCode()
val notifications: Array<StatusBarNotification> = activeNotifications
@@ -34,20 +34,23 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.relays.AmberListenerSingleton
import com.greenart7c3.nostrsigner.service.IntentUtils
import com.greenart7c3.nostrsigner.ui.navigation.Route
import kotlinx.coroutines.flow.MutableStateFlow
@SuppressLint("StateFlowValueCalledInComposition", "UnrememberedMutableState")
@Composable
fun AccountScreen(
accountStateViewModel: AccountStateViewModel,
intent: Intent,
intent: Intent?,
packageName: String?,
appName: String?,
flow: MutableStateFlow<List<IntentData>>,
bunkerRequests: List<AmberBunkerRequest>,
navController: NavHostController,
) {
val accountState by accountStateViewModel.accountContent.collectAsState()
@@ -67,36 +70,45 @@ fun AccountScreen(
}
is AccountState.LoggedIn -> {
LaunchedEffect(intent, intents) {
IntentUtils.getIntentData(context, intent, packageName, intent.getStringExtra("route"), state.account) {
it?.let { intentData ->
if (intents.none { item -> item.id == intentData.id }) {
val oldIntents = intents.toMutableList()
oldIntents.add(intentData)
flow.value = oldIntents
intent?.let {
IntentUtils.getIntentData(context, intent, packageName, intent.getStringExtra("route"), state.account) {
it?.let { intentData ->
if (intents.none { item -> item.id == intentData.id }) {
val oldIntents = intents.toMutableList()
oldIntents.add(intentData)
flow.value = oldIntents
}
}
}
}
}
val localRoute = mutableStateOf(intents.firstNotNullOfOrNull { it.route } ?: state.route)
val localRoute = mutableStateOf(
intents.firstNotNullOfOrNull { it.route } ?: if (bunkerRequests.isNotEmpty()) {
Route.IncomingRequest.route
} else {
null ?: state.route
},
)
AmberListenerSingleton.accountStateViewModel = accountStateViewModel
DisplayErrorMessages(accountStateViewModel)
MainScreen(
state.account,
accountStateViewModel,
intents,
packageName,
appName,
localRoute,
navController,
account = state.account,
accountStateViewModel = accountStateViewModel,
intents = intents,
bunkerRequests = bunkerRequests,
packageName = packageName,
appName = appName,
route = localRoute,
navController = navController,
onRemoveIntentData = { results, type ->
val oldIntents = intents.toMutableList()
when (type) {
IntentResultType.ADD -> {
oldIntents.addAll(results)
}
IntentResultType.REMOVE -> {
oldIntents.removeAll(results)
}
@@ -25,12 +25,14 @@ import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.service.ApplicationNameCache
import com.greenart7c3.nostrsigner.service.toShortenHex
import com.greenart7c3.nostrsigner.ui.components.BunkerSingleEventHomeScreen
import com.greenart7c3.nostrsigner.ui.components.IntentSingleEventHomeScreen
import com.greenart7c3.nostrsigner.ui.components.MultiEventHomeScreen
import com.greenart7c3.nostrsigner.ui.components.SingleEventHomeScreen
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -38,6 +40,7 @@ import kotlinx.coroutines.launch
fun IncomingRequestScreen(
modifier: Modifier,
intents: List<IntentData>,
bunkerRequests: List<AmberBunkerRequest>,
packageName: String?,
applicationName: String?,
account: Account,
@@ -68,7 +71,7 @@ fun IncomingRequestScreen(
if (loading) {
CenterCircularProgressIndicator(modifier)
} else {
if (intents.isEmpty()) {
if (intents.isEmpty() && bunkerRequests.isEmpty()) {
Column(
modifier.fillMaxSize(),
Arrangement.Center,
@@ -86,7 +89,7 @@ fun IncomingRequestScreen(
)
}
} else if (intents.size == 1) {
SingleEventHomeScreen(
IntentSingleEventHomeScreen(
modifier = modifier,
packageName = packageName,
applicationName = applicationName,
@@ -95,38 +98,24 @@ fun IncomingRequestScreen(
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
} else if (bunkerRequests.size == 1) {
BunkerSingleEventHomeScreen(
modifier = modifier,
bunkerRequest = bunkerRequests.first(),
account = account,
onLoading = onLoading,
)
} else {
if (intents.filter { it.bunkerRequest == null }.size == 1) {
SingleEventHomeScreen(
modifier = modifier,
packageName = packageName,
applicationName = applicationName,
intentData = intents.first { it.bunkerRequest == null },
account = account,
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
} else if (intents.filter { it.bunkerRequest != null }.size == 1) {
SingleEventHomeScreen(
modifier = modifier,
packageName = packageName,
applicationName = applicationName,
intentData = intents.first { it.bunkerRequest != null },
account = account,
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
} else {
MultiEventHomeScreen(
modifier = modifier,
intents = intents,
packageName = packageName,
accountParam = account,
navController = navController,
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
}
MultiEventHomeScreen(
modifier = modifier,
intents = intents,
bunkerRequests = bunkerRequests,
packageName = packageName,
accountParam = account,
navController = navController,
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
}
}
}
@@ -58,6 +58,7 @@ import com.greenart7c3.nostrsigner.BuildConfig
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.relays.AmberRelayStats
@@ -127,6 +128,7 @@ fun MainScreen(
account: Account,
accountStateViewModel: AccountStateViewModel,
intents: List<IntentData>,
bunkerRequests: List<AmberBunkerRequest>,
packageName: String?,
appName: String?,
route: MutableState<String?>,
@@ -215,14 +217,14 @@ fun MainScreen(
)
}
var localRoute by remember { mutableStateOf(route.value ?: if (intents.isEmpty()) Route.Applications.route else Route.IncomingRequest.route) }
LaunchedEffect(Unit, route.value, intents) {
var localRoute by remember { mutableStateOf(route.value ?: if (intents.isEmpty() && bunkerRequests.isEmpty()) Route.Applications.route else Route.IncomingRequest.route) }
LaunchedEffect(Unit, route.value, intents, bunkerRequests) {
launch(Dispatchers.Main) {
if (route.value != null) {
localRoute = route.value!!
route.value = null
} else {
localRoute = if (intents.isEmpty()) Route.Applications.route else Route.IncomingRequest.route
localRoute = if (intents.isEmpty() && bunkerRequests.isEmpty()) Route.Applications.route else Route.IncomingRequest.route
}
}
}
@@ -261,6 +263,7 @@ fun MainScreen(
navBackStackEntry = navBackStackEntry,
account = account,
intents = intents,
bunkerRequests = bunkerRequests,
packageName = packageName,
)
},
@@ -349,6 +352,7 @@ fun MainScreen(
.padding(horizontal = verticalPadding)
.padding(top = verticalPadding * 1.5f),
intents = intents,
bunkerRequests = bunkerRequests,
packageName = packageName,
applicationName = appName,
account = account,
@@ -28,9 +28,11 @@ import androidx.compose.ui.unit.dp
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Permission
import com.greenart7c3.nostrsigner.models.SignerType
import com.greenart7c3.nostrsigner.service.BunkerRequestUtils
import com.greenart7c3.nostrsigner.service.MultiEventScreenIntents
import com.greenart7c3.nostrsigner.service.model.AmberEvent
import com.greenart7c3.nostrsigner.ui.components.RememberMyChoice
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestSign
@Composable
fun SeeDetailsScreen(
@@ -41,15 +43,25 @@ fun SeeDetailsScreen(
modifier = modifier
.fillMaxWidth(),
) {
var rememberType by remember { mutableStateOf(MultiEventScreenIntents.intents.first().rememberType.value) }
val first = MultiEventScreenIntents.intents.first()
val permission = if (first.type == SignerType.SIGN_EVENT) {
Permission("sign_event", first.event!!.kind)
var rememberType by remember { mutableStateOf(MultiEventScreenIntents.intents.firstOrNull()?.rememberType?.value ?: MultiEventScreenIntents.bunkerRequests.first().rememberType.value) }
val type = if (MultiEventScreenIntents.intents.isNotEmpty()) {
MultiEventScreenIntents.intents.first().type
} else {
Permission(first.type.toString().toLowerCase(Locale.current), null)
BunkerRequestUtils.getTypeFromBunker(MultiEventScreenIntents.bunkerRequests.first().request)
}
val permission = if (type == SignerType.SIGN_EVENT) {
val event = if (MultiEventScreenIntents.intents.isNotEmpty()) {
MultiEventScreenIntents.intents.first().event!!
} else {
MultiEventScreenIntents.bunkerRequests.first().signedEvent!!
}
Permission("sign_event", event.kind)
} else {
Permission(type.toString().toLowerCase(Locale.current), null)
}
val message = if (first.type == SignerType.CONNECT) {
val message = if (type == SignerType.CONNECT) {
stringResource(R.string.connect)
} else {
permission.toLocalizedString(context)
@@ -123,5 +135,50 @@ fun SeeDetailsScreen(
}
}
}
MultiEventScreenIntents.bunkerRequests.forEach { bunkerRequest ->
Card(
Modifier
.padding(4.dp),
colors = CardDefaults.cardColors().copy(
containerColor = MaterialTheme.colorScheme.background,
),
border = BorderStroke(1.dp, Color.Gray),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable {
bunkerRequest.checked.value = !bunkerRequest.checked.value
},
) {
Checkbox(
checked = bunkerRequest.checked.value,
onCheckedChange = { _ ->
bunkerRequest.checked.value = !bunkerRequest.checked.value
},
colors = CheckboxDefaults.colors().copy(
uncheckedBorderColor = Color.Gray,
),
)
val data = if (bunkerRequest.request is BunkerRequestSign) {
val event = bunkerRequest.signedEvent!!
if (event.kind == 22242) AmberEvent.relay(event) else event.content
} else {
bunkerRequest.encryptDecryptResponse ?: BunkerRequestUtils.getDataFromBunker(bunkerRequest.request)
}
Text(
modifier = Modifier
.weight(1f)
.padding(vertical = 8.dp),
text = data.ifBlank { message },
color = if (bunkerRequest.checked.value) Color.Unspecified else Color.Gray,
)
}
}
}
}
}
@@ -25,6 +25,7 @@ import androidx.navigation.NavBackStackEntry
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.service.toShortenHex
import com.greenart7c3.nostrsigner.ui.navigation.Route
@@ -44,6 +45,7 @@ fun AmberTopAppBar(
navBackStackEntry: NavBackStackEntry?,
account: Account,
intents: List<IntentData>,
bunkerRequests: List<AmberBunkerRequest>,
packageName: String?,
) {
if (destinationRoute != "login" && destinationRoute != "create" && destinationRoute != "loginPage") {
@@ -132,8 +134,14 @@ fun AmberTopAppBar(
}
} else {
launch(Dispatchers.IO) {
if (destinationRoute == Route.IncomingRequest.route && intents.isNotEmpty()) {
val application = Amber.instance.getDatabase(account.npub).applicationDao().getByKey(intents.first().bunkerRequest?.localKey ?: packageName ?: "")?.application
if (destinationRoute == Route.IncomingRequest.route && (intents.isNotEmpty() || bunkerRequests.isNotEmpty())) {
val key = if (bunkerRequests.isNotEmpty()) {
bunkerRequests.first().localKey
} else {
packageName
}
val application = Amber.instance.getDatabase(account.npub).applicationDao().getByKey(key ?: "")?.application
val titleTemp = application?.name?.ifBlank { application.key.toShortenHex() } ?: packageName ?: ""
title = if (titleTemp.isBlank()) {
routes.find { it.route == destinationRoute }?.title ?: ""
@@ -48,6 +48,7 @@ import com.greenart7c3.nostrsigner.database.ApplicationEntity
import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions
import com.greenart7c3.nostrsigner.database.HistoryEntity2
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.models.Permission
@@ -63,6 +64,8 @@ import com.greenart7c3.nostrsigner.ui.Result
import com.greenart7c3.nostrsigner.ui.navigation.Route
import com.greenart7c3.nostrsigner.ui.theme.orange
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestSign
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponse
import com.vitorpamplona.quartz.nip55AndroidSigner.signString
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
@@ -74,26 +77,26 @@ import kotlinx.coroutines.launch
fun MultiEventHomeScreen(
modifier: Modifier,
intents: List<IntentData>,
bunkerRequests: List<AmberBunkerRequest>,
packageName: String?,
accountParam: Account,
navController: NavController,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
onLoading: (Boolean) -> Unit,
) {
if (intents.first().bunkerRequest != null) {
if (bunkerRequests.isNotEmpty()) {
BunkerMultiEventHomeScreen(
modifier = modifier,
intents = intents.filter { it.bunkerRequest != null },
packageName = packageName,
accountParam = accountParam,
navController = navController,
onRemoveIntentData = onRemoveIntentData,
bunkerRequests = bunkerRequests,
onLoading = onLoading,
)
} else {
IntentMultiEventHomeScreen(
modifier = modifier,
intents = intents.filter { it.bunkerRequest == null },
intents = intents,
packageName = packageName,
accountParam = accountParam,
navController = navController,
@@ -219,7 +222,7 @@ fun IntentMultiEventHomeScreen(
accountParam
} ?: continue
val localKey = intentData.bunkerRequest?.localKey ?: packageName ?: continue
val localKey = packageName ?: continue
val database = Amber.instance.getDatabase(thisAccount.npub)
val savedApplication = database.applicationDao().getByKey(localKey)
@@ -380,12 +383,8 @@ fun IntentMultiEventHomeScreen(
if (results.isNotEmpty()) {
sendResultIntent(results)
}
if (intents.any { it.bunkerRequest != null }) {
EventNotificationConsumer(context).notificationManager().cancelAll()
finishActivity(closeApp)
} else {
finishActivity(closeApp)
}
finishActivity(closeApp)
} finally {
onLoading(false)
}
@@ -428,10 +427,10 @@ fun IntentMultiEventHomeScreen(
"",
thisAccount.hexKey,
true,
intentData.bunkerRequest?.secret ?: "",
intentData.bunkerRequest?.secret != null,
"",
false,
thisAccount.signPolicy,
intentData.bunkerRequest?.closeApplication != false,
true,
),
permissions = mutableListOf(),
)
@@ -464,16 +463,15 @@ fun IntentMultiEventHomeScreen(
@Composable
fun BunkerMultiEventHomeScreen(
modifier: Modifier,
intents: List<IntentData>,
bunkerRequests: List<AmberBunkerRequest>,
packageName: String?,
accountParam: Account,
navController: NavController,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
onLoading: (Boolean) -> Unit,
) {
val context = LocalContext.current
val grouped = intents.groupBy { it.type }.filter { it.key != SignerType.SIGN_EVENT }
val grouped2 = intents.filter { it.type == SignerType.SIGN_EVENT }.groupBy { it.event?.kind }
val grouped = bunkerRequests.groupBy { BunkerRequestUtils.getTypeFromBunker(it.request) }.filter { it.key != SignerType.SIGN_EVENT }
val grouped2 = bunkerRequests.filter { it.request is BunkerRequestSign }.groupBy { (it.request as BunkerRequestSign).event.kind }
val acceptEventsGroup1 = grouped.map {
remember {
mutableStateOf(true)
@@ -485,14 +483,14 @@ fun BunkerMultiEventHomeScreen(
}
}
var localAccount by remember { mutableStateOf("") }
val key = intents.firstOrNull()?.bunkerRequest?.localKey ?: "$packageName"
val key = bunkerRequests.first().localKey
var rememberType by remember { mutableStateOf(RememberType.NEVER) }
LaunchedEffect(Unit) {
launch(Dispatchers.IO) {
localAccount = LocalPreferences.loadFromEncryptedStorage(
context,
intents.firstOrNull()?.currentAccount ?: "",
bunkerRequests.first().currentAccount,
)?.npub?.toShortenHex() ?: ""
}
}
@@ -510,26 +508,26 @@ fun BunkerMultiEventHomeScreen(
)
grouped.toList().forEachIndexed { index, it ->
PermissionCard(
BunkerPermissionCard(
context = context,
acceptEventsGroup = acceptEventsGroup1,
index = index,
item = it,
onDetailsClick = {
MultiEventScreenIntents.intents = it
MultiEventScreenIntents.bunkerRequests = it
MultiEventScreenIntents.appName = appName
navController.navigate(Route.SeeDetails.route)
},
)
}
grouped2.toList().forEachIndexed { index, it ->
PermissionCard(
BunkerPermissionCard(
context = context,
acceptEventsGroup = acceptEventsGroup2,
index = index,
item = it,
onDetailsClick = {
MultiEventScreenIntents.intents = it
MultiEventScreenIntents.bunkerRequests = it
MultiEventScreenIntents.appName = appName
navController.navigate(Route.SeeDetails.route)
},
@@ -546,7 +544,7 @@ fun BunkerMultiEventHomeScreen(
onReject = {},
onChanged = {
rememberType = it
intents.forEach {
bunkerRequests.forEach {
it.rememberType.value = rememberType
}
},
@@ -561,28 +559,31 @@ fun BunkerMultiEventHomeScreen(
onLoading(true)
Amber.instance.applicationIOScope.launch(Dispatchers.IO) {
try {
reconnectToRelays(intents)
reconnectToRelays()
var closeApp = true
BunkerRequestUtils.clearRequests()
onRemoveIntentData(intents, IntentResultType.REMOVE)
for (intentData in intents) {
intentData.bunkerRequest ?: continue
for (request in bunkerRequests) {
val thisAccount =
if (intentData.currentAccount.isNotBlank()) {
if (request.currentAccount.isNotBlank()) {
LocalPreferences.loadFromEncryptedStorage(
context,
intentData.currentAccount,
request.currentAccount,
)
} else {
accountParam
} ?: continue
val localKey = intentData.bunkerRequest.localKey
val localKey = request.localKey
val database = Amber.instance.getDatabase(thisAccount.npub)
val savedApplication = database.applicationDao().getByKey(localKey)
val secret = if (request.request is BunkerRequestConnect) {
request.request.secret ?: ""
} else {
""
}
val application =
savedApplication ?: ApplicationWithPermissions(
application = ApplicationEntity(
@@ -594,10 +595,10 @@ fun BunkerMultiEventHomeScreen(
"",
thisAccount.hexKey,
true,
intentData.bunkerRequest.secret,
intentData.bunkerRequest.secret.isNotBlank(),
secret,
secret.isNotBlank(),
thisAccount.signPolicy,
intentData.bunkerRequest.closeApplication,
request.closeApplication,
),
permissions = mutableListOf(),
)
@@ -606,72 +607,70 @@ fun BunkerMultiEventHomeScreen(
closeApp = false
}
if (intentData.type == SignerType.SIGN_EVENT) {
val localEvent = intentData.event!!
if (request.request is BunkerRequestSign) {
val localEvent = request.signedEvent!!
if (intentData.rememberType.value != RememberType.NEVER && intentData.checked.value) {
if (request.rememberType.value != RememberType.NEVER && request.checked.value) {
AmberUtils.acceptOrRejectPermission(
application,
localKey,
intentData.type,
localEvent.kind,
true,
intentData.rememberType.value,
thisAccount,
application = application,
key = localKey,
signerType = SignerType.SIGN_EVENT,
kind = localEvent.kind,
value = true,
rememberType = request.rememberType.value,
account = thisAccount,
)
}
database.applicationDao().insertApplicationWithPermissions(application)
database.applicationDao().addHistory(
HistoryEntity2(
0,
localKey,
intentData.type.toString(),
localEvent.kind,
TimeUtils.now(),
intentData.checked.value,
entity = HistoryEntity2(
id = 0,
pkKey = localKey,
type = SignerType.SIGN_EVENT.toString(),
kind = localEvent.kind,
time = TimeUtils.now(),
accepted = request.checked.value,
),
)
val localIntentData = intentData.copy()
BunkerRequestUtils.remove(intentData.bunkerRequest.id)
val localBunkerRequest = request.copy()
BunkerRequestUtils.remove(request.request.id)
if (intentData.checked.value) {
if (request.checked.value) {
BunkerRequestUtils.sendBunkerResponse(
context,
thisAccount,
intentData.bunkerRequest,
BunkerResponse(intentData.bunkerRequest.id, localEvent.toJson(), null),
request,
BunkerResponse(request.request.id, localEvent.toJson(), null),
application.application.relays,
onLoading = {},
onDone = {
if (!it) {
BunkerRequestUtils.addRequest(localIntentData.bunkerRequest!!)
BunkerRequestUtils.addRequest(localBunkerRequest)
}
},
)
} else {
AmberUtils.sendBunkerError(
intentData,
thisAccount,
intentData.bunkerRequest,
account = thisAccount,
bunkerRequest = request,
relays = application.application.relays,
context = context,
closeApplication = application.application.closeApplication,
onRemoveIntentData = onRemoveIntentData,
onLoading = {},
)
}
} else if (intentData.type == SignerType.SIGN_MESSAGE) {
if (intentData.rememberType.value != RememberType.NEVER && intentData.checked.value) {
} else if (request.request.method == "sign_message") {
if (request.rememberType.value != RememberType.NEVER && request.checked.value) {
AmberUtils.acceptOrRejectPermission(
application,
localKey,
intentData.type,
SignerType.SIGN_MESSAGE,
null,
true,
intentData.rememberType.value,
request.rememberType.value,
thisAccount,
)
}
@@ -681,44 +680,42 @@ fun BunkerMultiEventHomeScreen(
HistoryEntity2(
0,
localKey,
intentData.type.toString(),
SignerType.SIGN_MESSAGE.toString(),
null,
TimeUtils.now(),
intentData.checked.value,
request.checked.value,
),
)
val signedMessage = signString(intentData.data, thisAccount.signer.keyPair.privKey!!).toHexKey()
val localIntentData = intentData.copy()
BunkerRequestUtils.remove(intentData.bunkerRequest.id)
val signedMessage = signString(request.request.params.first(), thisAccount.signer.keyPair.privKey!!).toHexKey()
val localBunkerRequest = request.copy()
BunkerRequestUtils.remove(localBunkerRequest.request.id)
if (intentData.checked.value) {
if (request.checked.value) {
BunkerRequestUtils.sendBunkerResponse(
context,
thisAccount,
intentData.bunkerRequest,
BunkerResponse(intentData.bunkerRequest.id, signedMessage, null),
request,
BunkerResponse(request.request.id, signedMessage, null),
application.application.relays,
onLoading = {},
onDone = {
if (!it) {
BunkerRequestUtils.addRequest(localIntentData.bunkerRequest!!)
BunkerRequestUtils.addRequest(localBunkerRequest)
}
},
)
} else {
AmberUtils.sendBunkerError(
intentData,
thisAccount,
intentData.bunkerRequest,
account = thisAccount,
bunkerRequest = request,
relays = application.application.relays,
context = context,
closeApplication = application.application.closeApplication,
onRemoveIntentData = onRemoveIntentData,
onLoading = {},
)
}
} else if (intentData.type == SignerType.CONNECT) {
} else if (request.request is BunkerRequestConnect) {
if (savedApplication == null) {
database.applicationDao().insertApplicationWithPermissions(application)
@@ -726,51 +723,50 @@ fun BunkerMultiEventHomeScreen(
HistoryEntity2(
0,
localKey,
intentData.type.toString(),
SignerType.CONNECT.toString(),
null,
TimeUtils.now(),
intentData.checked.value,
request.checked.value,
),
)
val localIntentData = intentData.copy()
BunkerRequestUtils.remove(intentData.bunkerRequest.id)
if (intentData.checked.value) {
val localBunkerRequest = request.copy()
BunkerRequestUtils.remove(request.request.id)
if (request.checked.value) {
BunkerRequestUtils.sendBunkerResponse(
context,
thisAccount,
intentData.bunkerRequest,
BunkerResponse(intentData.bunkerRequest.id, "", null),
request,
BunkerResponse(request.request.id, "", null),
application.application.relays,
onLoading = {},
onDone = {
if (!it) {
BunkerRequestUtils.addRequest(localIntentData.bunkerRequest!!)
BunkerRequestUtils.addRequest(localBunkerRequest)
}
},
)
} else {
AmberUtils.sendBunkerError(
intentData,
thisAccount,
intentData.bunkerRequest,
account = thisAccount,
bunkerRequest = request,
relays = application.application.relays,
context = context,
closeApplication = application.application.closeApplication,
onRemoveIntentData = onRemoveIntentData,
onLoading = {},
)
}
}
} else {
if (intentData.rememberType.value != RememberType.NEVER && intentData.checked.value) {
val type = BunkerRequestUtils.getTypeFromBunker(request.request)
if (request.rememberType.value != RememberType.NEVER && request.checked.value) {
AmberUtils.acceptOrRejectPermission(
application,
localKey,
intentData.type,
type,
null,
true,
intentData.rememberType.value,
request.rememberType.value,
thisAccount,
)
}
@@ -781,39 +777,37 @@ fun BunkerMultiEventHomeScreen(
HistoryEntity2(
0,
localKey,
intentData.type.toString(),
type.toString(),
null,
TimeUtils.now(),
intentData.checked.value,
request.checked.value,
),
)
val signature = intentData.encryptedData ?: continue
val localIntentData = intentData.copy()
BunkerRequestUtils.remove(intentData.bunkerRequest.id)
if (intentData.checked.value) {
val signature = request.encryptDecryptResponse ?: continue
val localBunkerRequest = request.copy()
BunkerRequestUtils.remove(request.request.id)
if (request.checked.value) {
BunkerRequestUtils.sendBunkerResponse(
context,
thisAccount,
intentData.bunkerRequest,
BunkerResponse(intentData.bunkerRequest.id, signature, null),
request,
BunkerResponse(request.request.id, signature, null),
application.application.relays,
onLoading = {},
onDone = {
if (!it) {
BunkerRequestUtils.addRequest(localIntentData.bunkerRequest!!)
BunkerRequestUtils.addRequest(localBunkerRequest)
}
},
)
} else {
AmberUtils.sendBunkerError(
intentData,
thisAccount,
intentData.bunkerRequest,
account = thisAccount,
bunkerRequest = request,
relays = application.application.relays,
context = context,
closeApplication = application.application.closeApplication,
onRemoveIntentData = onRemoveIntentData,
onLoading = {},
)
}
@@ -837,22 +831,24 @@ fun BunkerMultiEventHomeScreen(
Amber.instance.applicationIOScope.launch {
var closeApp = true
BunkerRequestUtils.clearRequests()
onRemoveIntentData(intents, IntentResultType.REMOVE)
for (intentData in intents) {
intentData.bunkerRequest ?: continue
for (request in bunkerRequests) {
val thisAccount =
if (intentData.currentAccount.isNotBlank()) {
if (request.currentAccount.isNotBlank()) {
LocalPreferences.loadFromEncryptedStorage(
context,
intentData.currentAccount,
request.currentAccount,
)
} else {
accountParam
} ?: continue
val localKey = intentData.bunkerRequest.localKey
val localKey = request.localKey
val database = Amber.instance.getDatabase(thisAccount.npub)
val secret = if (request.request is BunkerRequestConnect) {
request.request.secret ?: ""
} else {
""
}
val application =
database
.applicationDao()
@@ -866,22 +862,22 @@ fun BunkerMultiEventHomeScreen(
"",
thisAccount.hexKey,
true,
intentData.bunkerRequest.secret,
intentData.bunkerRequest.secret.isNotBlank(),
secret,
secret.isNotBlank(),
thisAccount.signPolicy,
intentData.bunkerRequest.closeApplication,
request.closeApplication,
),
permissions = mutableListOf(),
)
if (intentData.rememberType.value != RememberType.NEVER && intentData.checked.value) {
if (request.rememberType.value != RememberType.NEVER && request.checked.value) {
AmberUtils.acceptOrRejectPermission(
application,
localKey,
intentData.type,
BunkerRequestUtils.getTypeFromBunker(request.request),
null,
false,
intentData.rememberType.value,
request.rememberType.value,
thisAccount,
)
}
@@ -918,9 +914,7 @@ private fun sendResultIntent(
Amber.instance.getMainActivity()?.setResult(Activity.RESULT_OK, intent)
}
private suspend fun reconnectToRelays(intents: List<IntentData>) {
if (!intents.any { it.bunkerRequest != null }) return
private suspend fun reconnectToRelays() {
Amber.instance.checkForNewRelays()
}
@@ -1028,3 +1022,108 @@ fun PermissionCard(
}
}
}
@Composable
fun BunkerPermissionCard(
context: Context,
acceptEventsGroup: List<MutableState<Boolean>>,
index: Int,
item: Pair<Any?, List<AmberBunkerRequest>>,
onDetailsClick: (List<AmberBunkerRequest>) -> Unit,
) {
Card(
Modifier
.padding(4.dp),
colors = CardDefaults.cardColors().copy(
containerColor = MaterialTheme.colorScheme.background,
),
border = BorderStroke(1.dp, Color.Gray),
) {
Column(
Modifier
.padding(4.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable {
acceptEventsGroup[index].value = !acceptEventsGroup[index].value
item.second.forEach { it.checked.value = acceptEventsGroup[index].value }
},
) {
Checkbox(
checked = acceptEventsGroup[index].value,
onCheckedChange = { _ ->
acceptEventsGroup[index].value = !acceptEventsGroup[index].value
item.second.forEach { it.checked.value = acceptEventsGroup[index].value }
},
colors = CheckboxDefaults.colors().copy(
uncheckedBorderColor = Color.Gray,
),
)
val first = item.first
val permission = if (first is Int) {
Permission("sign_event", first)
} else {
Permission(first.toString().toLowerCase(Locale.current), null)
}
val message = if (first == SignerType.CONNECT) {
stringResource(R.string.connect)
} else {
permission.toLocalizedString(context)
}
Text(
modifier = Modifier.weight(1f),
text = message,
color = if (acceptEventsGroup[index].value) Color.Unspecified else Color.Gray,
)
}
if (acceptEventsGroup[index].value) {
val selected = item.second.filter { it.checked.value }.size
val total = item.second.size
Row(
Modifier
.fillMaxWidth()
.clickable {
onDetailsClick(item.second)
}
.padding(end = 8.dp, top = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
stringResource(R.string.of_events, selected, total),
modifier = Modifier.padding(start = 48.dp, bottom = 4.dp),
color = Color.Gray,
fontSize = 14.sp,
)
Text(
buildAnnotatedString {
withLink(
LinkAnnotation.Clickable(
tag = "See_details",
styles = TextLinkStyles(
style = SpanStyle(
textDecoration = TextDecoration.Underline,
),
),
linkInteractionListener = {
onDetailsClick(item.second)
},
),
) {
append(stringResource(R.string.see_details))
}
},
modifier = Modifier.padding(start = 46.dp, bottom = 8.dp),
color = Color.Gray,
fontSize = 14.sp,
)
}
}
}
}
}
@@ -30,7 +30,7 @@ import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.database.AppDatabase
import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.BunkerRequest
import com.greenart7c3.nostrsigner.models.AmberBunkerRequest
import com.greenart7c3.nostrsigner.models.IntentData
import com.greenart7c3.nostrsigner.models.IntentResultType
import com.greenart7c3.nostrsigner.models.Permission
@@ -43,6 +43,13 @@ import com.greenart7c3.nostrsigner.ui.RememberType
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestGetPublicKey
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip04Decrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip04Encrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip44Decrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip44Encrypt
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestSign
import com.vitorpamplona.quartz.nip55AndroidSigner.signString
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.utils.Hex
@@ -51,39 +58,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun SingleEventHomeScreen(
modifier: Modifier,
packageName: String?,
applicationName: String?,
intentData: IntentData,
account: Account,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
onLoading: (Boolean) -> Unit,
) {
if (intentData.bunkerRequest != null) {
BunkerSingleEventHomeScreen(
modifier = modifier,
bunkerRequest = intentData.bunkerRequest,
intentData = intentData,
account = account,
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
} else {
IntentSingleEventHomeScreen(
modifier = modifier,
packageName = packageName,
applicationName = applicationName,
intentData = intentData,
account = account,
onRemoveIntentData = onRemoveIntentData,
onLoading = onLoading,
)
}
}
@Composable
private fun IntentSingleEventHomeScreen(
fun IntentSingleEventHomeScreen(
modifier: Modifier,
packageName: String?,
applicationName: String?,
@@ -404,12 +379,10 @@ private fun IntentSingleEventHomeScreen(
}
@Composable
private fun BunkerSingleEventHomeScreen(
fun BunkerSingleEventHomeScreen(
modifier: Modifier,
bunkerRequest: BunkerRequest,
intentData: IntentData,
bunkerRequest: AmberBunkerRequest,
account: Account,
onRemoveIntentData: (List<IntentData>, IntentResultType) -> Unit,
onLoading: (Boolean) -> Unit,
) {
var applicationEntity by remember {
@@ -420,21 +393,22 @@ private fun BunkerSingleEventHomeScreen(
LaunchedEffect(Unit) {
launch(Dispatchers.IO) {
applicationEntity =
if (bunkerRequest.secret.isNotBlank()) {
Amber.instance.getDatabase(account.npub).applicationDao().getBySecret(bunkerRequest.secret)
if (bunkerRequest.request is BunkerRequestConnect && bunkerRequest.request.secret?.isNotBlank() == true) {
Amber.instance.getDatabase(account.npub).applicationDao().getBySecret(bunkerRequest.request.secret!!)
} else {
Amber.instance.getDatabase(account.npub).applicationDao().getByKey(key)
}
}
}
var appName = applicationEntity?.application?.name ?: intentData.name
var appName = applicationEntity?.application?.name ?: bunkerRequest.name
appName = appName.ifBlank { key.toShortenHex() }
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val type = BunkerRequestUtils.getTypeFromBunker(bunkerRequest.request)
when (intentData.type) {
SignerType.CONNECT -> {
when (bunkerRequest.request) {
is BunkerRequestConnect -> {
var showExistingAppDialog by remember { mutableStateOf(false) }
var localPermissions by remember { mutableStateOf<List<Permission>?>(null) }
var localCloseApplication by remember { mutableStateOf<Boolean?>(null) }
@@ -448,7 +422,7 @@ private fun BunkerSingleEventHomeScreen(
Text(text = stringResource(R.string.replace_connection))
},
text = {
Text(text = stringResource(R.string.replace_app_message, intentData.name))
Text(text = stringResource(R.string.replace_app_message, bunkerRequest.name))
},
onDismissRequest = {
showExistingAppDialog = false
@@ -457,18 +431,14 @@ private fun BunkerSingleEventHomeScreen(
Column {
AmberButton(
onClick = {
val result = if (intentData.type == SignerType.CONNECT) {
bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
} else {
account.hexKey
}
val result = bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
BunkerRequestUtils.sendResult(
oldKey = existingAppKey,
context = context,
account = account,
key = key,
event = result,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
@@ -477,8 +447,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = localSignPolicy,
shouldCloseApplication = localCloseApplication,
rememberType = localRememberType,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
},
text = stringResource(R.string.replace),
@@ -486,17 +454,13 @@ private fun BunkerSingleEventHomeScreen(
AmberButton(
text = stringResource(R.string.create_a_new_connection),
onClick = {
val result = if (intentData.type == SignerType.CONNECT) {
bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
} else {
account.hexKey
}
val result = bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
event = result,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
@@ -505,8 +469,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = localSignPolicy,
shouldCloseApplication = localCloseApplication,
rememberType = localRememberType,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
},
)
@@ -526,27 +488,31 @@ private fun BunkerSingleEventHomeScreen(
shouldCloseApp = applicationEntity?.application?.closeApplication ?: bunkerRequest.closeApplication,
account = account,
appName = appName,
permissions = intentData.permissions,
onAccept = { permissions, signPolicy, closeApplication, rememberType ->
val result = if (intentData.type == SignerType.CONNECT) {
bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
permissions = bunkerRequest.request.permissions?.split(",")?.map {
val split = it.split(":")
if (split.size > 1) {
Permission(split[0], split[1].toInt())
} else {
account.hexKey
Permission(split[0], null)
}
},
onAccept = { permissions, signPolicy, closeApplication, rememberType ->
val result = bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
localPermissions = permissions
localSignPolicy = signPolicy
localCloseApplication = closeApplication
localRememberType = rememberType
if (intentData.name.isNotBlank()) {
if (bunkerRequest.name.isNotBlank()) {
Amber.instance.applicationIOScope.launch {
val existingApp = AppDatabase.getDatabase(Amber.instance, account.npub).applicationDao().getByName(intentData.name)
val existingApp = AppDatabase.getDatabase(Amber.instance, account.npub).applicationDao().getByName(bunkerRequest.name)
if (existingApp == null) {
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
event = result,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
@@ -555,8 +521,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = signPolicy,
shouldCloseApplication = closeApplication,
rememberType = rememberType,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
} else {
existingAppKey = existingApp.application.key
@@ -568,7 +532,7 @@ private fun BunkerSingleEventHomeScreen(
context = context,
account = account,
key = key,
event = result,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
@@ -577,8 +541,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = signPolicy,
shouldCloseApplication = closeApplication,
rememberType = rememberType,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
}
},
@@ -589,32 +551,26 @@ private fun BunkerSingleEventHomeScreen(
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = intentData.type,
signerType = type,
kind = null,
intentData = intentData,
onLoading = onLoading,
onRemoveIntentData = onRemoveIntentData,
)
},
)
}
SignerType.GET_PUBLIC_KEY -> {
is BunkerRequestGetPublicKey -> {
BunkerGetPubKeyScreen(
modifier = modifier,
account = account,
applicationName = appName,
onAccept = { permissions, signPolicy, closeApplication, rememberType ->
val result = if (intentData.type == SignerType.CONNECT) {
bunkerRequest.nostrConnectSecret.ifBlank { "ack" }
} else {
account.hexKey
}
val result = account.hexKey
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
event = result,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
@@ -623,8 +579,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = signPolicy,
shouldCloseApplication = closeApplication,
rememberType = rememberType,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
},
onReject = {
@@ -634,93 +588,21 @@ private fun BunkerSingleEventHomeScreen(
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = intentData.type,
signerType = type,
kind = null,
intentData = intentData,
onLoading = onLoading,
onRemoveIntentData = onRemoveIntentData,
)
},
)
}
SignerType.SIGN_MESSAGE -> {
val permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == intentData.type.toString()
}
val acceptUntil = permission?.acceptUntil ?: 0
val rejectUntil = permission?.rejectUntil ?: 0
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
null
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) {
false
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) {
true
} else {
null
}
BunkerSignMessage(
account = account,
modifier = modifier,
content = intentData.data,
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = intentData.type,
onAccept = {
Amber.instance.applicationIOScope.launch(Dispatchers.IO) {
val result = signString(intentData.data, account.signer.keyPair.privKey!!).toHexKey()
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
event = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
permissions = null,
appName = appName,
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
}
},
onReject = {
BunkerRequestUtils.sendRejection(
key = key,
account = account,
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = intentData.type,
kind = null,
intentData = intentData,
onLoading = onLoading,
onRemoveIntentData = onRemoveIntentData,
)
},
)
}
SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> {
val nip = when (intentData.type) {
SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT -> 4
SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT -> 44
SignerType.DECRYPT_ZAP_EVENT -> null
else -> null
}
is BunkerRequestNip04Encrypt -> {
val nip = 4
var permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == intentData.type.toString()
it.pkKey == key && it.type == type.toString()
}
if (permission == null && nip != null) {
if (permission == null) {
permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == "NIP" && it.kind == nip
@@ -743,24 +625,17 @@ private fun BunkerSingleEventHomeScreen(
BunkerEncryptDecryptData(
account = account,
modifier = modifier,
content = intentData.data,
encryptedData = intentData.encryptedData ?: "",
content = bunkerRequest.request.message,
encryptedData = bunkerRequest.encryptDecryptResponse ?: "",
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = intentData.type,
type = type,
onAccept = {
val result =
if (intentData.encryptedData == "Could not decrypt the message" && (intentData.type == SignerType.DECRYPT_ZAP_EVENT)) {
""
} else {
intentData.encryptedData ?: ""
}
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
event = result,
response = bunkerRequest.encryptDecryptResponse ?: "",
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
@@ -769,8 +644,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
},
onReject = {
@@ -780,18 +653,217 @@ private fun BunkerSingleEventHomeScreen(
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = intentData.type,
signerType = type,
kind = null,
intentData = intentData,
onLoading = onLoading,
onRemoveIntentData = onRemoveIntentData,
)
},
)
}
else -> {
val event = intentData.event!!
is BunkerRequestNip04Decrypt -> {
val nip = 4
var permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == type.toString()
}
if (permission == null) {
permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == "NIP" && it.kind == nip
}
}
val acceptUntil = permission?.acceptUntil ?: 0
val rejectUntil = permission?.rejectUntil ?: 0
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
null
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) {
false
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) {
true
} else {
null
}
BunkerEncryptDecryptData(
account = account,
modifier = modifier,
content = bunkerRequest.request.ciphertext,
encryptedData = bunkerRequest.encryptDecryptResponse ?: "",
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = type,
onAccept = {
val result = bunkerRequest.encryptDecryptResponse ?: ""
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
permissions = null,
appName = appName,
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
)
},
onReject = {
BunkerRequestUtils.sendRejection(
key = key,
account = account,
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = type,
kind = null,
onLoading = onLoading,
)
},
)
}
is BunkerRequestNip44Encrypt -> {
val nip = 44
var permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == type.toString()
}
if (permission == null) {
permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == "NIP" && it.kind == nip
}
}
val acceptUntil = permission?.acceptUntil ?: 0
val rejectUntil = permission?.rejectUntil ?: 0
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
null
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) {
false
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) {
true
} else {
null
}
BunkerEncryptDecryptData(
account = account,
modifier = modifier,
content = bunkerRequest.request.message,
encryptedData = bunkerRequest.encryptDecryptResponse ?: "",
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = type,
onAccept = {
val result = bunkerRequest.encryptDecryptResponse ?: ""
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
permissions = null,
appName = appName,
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
)
},
onReject = {
BunkerRequestUtils.sendRejection(
key = key,
account = account,
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = type,
kind = null,
onLoading = onLoading,
)
},
)
}
is BunkerRequestNip44Decrypt -> {
val nip = 44
var permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == type.toString()
}
if (permission == null) {
permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == "NIP" && it.kind == nip
}
}
val acceptUntil = permission?.acceptUntil ?: 0
val rejectUntil = permission?.rejectUntil ?: 0
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
null
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) {
false
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) {
true
} else {
null
}
BunkerEncryptDecryptData(
account = account,
modifier = modifier,
content = bunkerRequest.request.ciphertext,
encryptedData = bunkerRequest.encryptDecryptResponse ?: "",
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = type,
onAccept = {
val result = bunkerRequest.encryptDecryptResponse ?: ""
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
permissions = null,
appName = appName,
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
)
},
onReject = {
BunkerRequestUtils.sendRejection(
key = key,
account = account,
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = type,
kind = null,
onLoading = onLoading,
)
},
)
}
is BunkerRequestSign -> {
val event = bunkerRequest.signedEvent!!
val accounts =
LocalPreferences.allSavedAccounts(context).filter {
it.npub.bechToBytes().toHexKey() == event.pubKey
@@ -815,7 +887,7 @@ private fun BunkerSingleEventHomeScreen(
val permission =
applicationEntity?.permissions?.firstOrNull {
val nip = event.kind.kindToNip()?.toIntOrNull()
it.pkKey == key && ((it.type == intentData.type.toString() && it.kind == event.kind) || (nip != null && it.type == "NIP" && it.kind == nip))
it.pkKey == key && ((it.type == type.toString() && it.kind == event.kind) || (nip != null && it.type == "NIP" && it.kind == nip))
}
val acceptUntil = permission?.acceptUntil ?: 0
@@ -838,7 +910,7 @@ private fun BunkerSingleEventHomeScreen(
appName = appName,
event = event,
rawJson = event.toJson(),
type = intentData.type,
type = type,
onAccept = {
if (event.pubKey != account.hexKey && !isPrivateEvent(event.kind, event.tags)) {
coroutineScope.launch {
@@ -855,7 +927,7 @@ private fun BunkerSingleEventHomeScreen(
context = context,
account = account,
key = key,
event = event.toJson(),
response = event.toJson(),
bunkerRequest = bunkerRequest,
kind = event.kind,
onLoading = onLoading,
@@ -864,8 +936,6 @@ private fun BunkerSingleEventHomeScreen(
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
onRemoveIntentData = onRemoveIntentData,
intentData = intentData,
)
},
onReject = {
@@ -875,11 +945,135 @@ private fun BunkerSingleEventHomeScreen(
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = intentData.type,
signerType = type,
kind = event.kind,
intentData = intentData,
onLoading = onLoading,
onRemoveIntentData = onRemoveIntentData,
)
},
)
}
}
else -> {
if (type == SignerType.DECRYPT_ZAP_EVENT) {
var permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == type.toString()
}
val acceptUntil = permission?.acceptUntil ?: 0
val rejectUntil = permission?.rejectUntil ?: 0
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
null
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) {
false
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) {
true
} else {
null
}
BunkerEncryptDecryptData(
account = account,
modifier = modifier,
content = bunkerRequest.request.params[1],
encryptedData = bunkerRequest.encryptDecryptResponse ?: "",
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = type,
onAccept = {
val result =
if (bunkerRequest.encryptDecryptResponse == "Could not decrypt the message") {
""
} else {
bunkerRequest.encryptDecryptResponse ?: ""
}
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
permissions = null,
appName = appName,
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
)
},
onReject = {
BunkerRequestUtils.sendRejection(
key = key,
account = account,
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = type,
kind = null,
onLoading = onLoading,
)
},
)
} else if (type == SignerType.SIGN_MESSAGE) {
val permission =
applicationEntity?.permissions?.firstOrNull {
it.pkKey == key && it.type == type.toString()
}
val acceptUntil = permission?.acceptUntil ?: 0
val rejectUntil = permission?.rejectUntil ?: 0
val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) {
null
} else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) {
false
} else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) {
true
} else {
null
}
BunkerSignMessage(
account = account,
modifier = modifier,
content = bunkerRequest.request.params.first(),
shouldRunOnAccept = acceptOrReject,
appName = appName,
type = type,
onAccept = {
Amber.instance.applicationIOScope.launch(Dispatchers.IO) {
val result = signString(bunkerRequest.request.params.first(), account.signer.keyPair.privKey!!).toHexKey()
BunkerRequestUtils.sendResult(
context = context,
account = account,
key = key,
response = result,
bunkerRequest = bunkerRequest,
kind = null,
onLoading = onLoading,
permissions = null,
appName = appName,
signPolicy = null,
shouldCloseApplication = bunkerRequest.closeApplication,
rememberType = it,
)
}
},
onReject = {
BunkerRequestUtils.sendRejection(
key = key,
account = account,
bunkerRequest = bunkerRequest,
appName = appName,
rememberType = it,
signerType = type,
kind = null,
onLoading = onLoading,
)
},
)
+1 -1
View File
@@ -14,7 +14,7 @@ lifecycle_version = "2.9.1"
material3 = "1.3.2"
mockk = "1.14.2"
nav_version = "2.9.0"
quartz = "07434eb4b6"
quartz = "e7dec1702d"
compose_ui = "1.8.2"
richtextUi = "e1151c8"
roomKtx = "2.7.1"