diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/MainActivity.kt b/app/src/main/java/com/greenart7c3/nostrsigner/MainActivity.kt index 97ed486c..d1284b29 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/MainActivity.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/MainActivity.kt @@ -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, + ) } } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/MainViewModel.kt b/app/src/main/java/com/greenart7c3/nostrsigner/MainViewModel.kt index 126d6178..914f3b07 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/MainViewModel.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/MainViewModel.kt @@ -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) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/BunkerRequest.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt similarity index 55% rename from app/src/main/java/com/greenart7c3/nostrsigner/models/BunkerRequest.kt rename to app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt index e3c56c61..c1d9e253 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/BunkerRequest.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt @@ -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, - var localKey: String, - var relays: List, - 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, + val currentAccount: String, + val encryptionType: EncryptionType, + val nostrConnectSecret: String, + val closeApplication: Boolean, + val name: String, + val signedEvent: Event?, + val encryptDecryptResponse: String?, + val checked: MutableState = mutableStateOf(true), + val rememberType: MutableState = 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::class.java) { + private class AmberBunkerRequestDeserializer : StdDeserializer(AmberBunkerRequest::class.java) { override fun deserialize( jp: JsonParser, ctxt: DeserializationContext, - ): BunkerRequest { + ): AmberBunkerRequest { return fromJson(jp.codec.readTree(jp)) } } - private class BunkerRequestSerializer : StdSerializer(BunkerRequest::class.java) { + private class AmberBunkerRequestSerializer : StdSerializer(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() } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/IntentData.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/IntentData.kt index c7d9577e..28a8d95a 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/IntentData.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/IntentData.kt @@ -18,7 +18,6 @@ data class IntentData( val currentAccount: String, val checked: MutableState, val rememberType: MutableState, - val bunkerRequest: BunkerRequest?, val route: String?, val event: Event?, val encryptedData: String?, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt index 9c0e5e36..25c005fa 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt @@ -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, context: Context, closeApplication: Boolean, - onRemoveIntentData: (List, 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) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt index da906e3d..401eb793 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt @@ -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(Channel.CONFLATED) - private val bunkerRequests = ConcurrentHashMap() + val state = MutableStateFlow(listOf()) - 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 { - return bunkerRequests + fun getBunkerRequests(): List { + return state.value } fun sendBunkerResponse( context: Context, account: Account, - bunkerRequest: BunkerRequest, + bunkerRequest: AmberBunkerRequest, bunkerResponse: BunkerResponse, relays: List, 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() - 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( - 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? = null, @@ -456,8 +241,6 @@ object BunkerRequestUtils { signPolicy: Int? = null, shouldCloseApplication: Boolean? = null, rememberType: RememberType, - intentData: IntentData, - onRemoveIntentData: (List, 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, 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, ) } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt index e182a724..8fd4bc0f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt @@ -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, ) } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt index cd337702..c625ca87 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt @@ -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(), ) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/MultiEventScreenIntents.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/MultiEventScreenIntents.kt index e4fa5049..7d394c0b 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/MultiEventScreenIntents.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/MultiEventScreenIntents.kt @@ -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() + var bunkerRequests = listOf() var appName = "" } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/NostrConnectUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/NostrConnectUtils.kt index 0386747b..95aadfc5 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/NostrConnectUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/NostrConnectUtils.kt @@ -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) } } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationUtils.kt index b8a50ba5..df970c85 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationUtils.kt @@ -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 = activeNotifications diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt index cdf219d4..e285d51b 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt @@ -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>, + bunkerRequests: List, 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) } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt index beed7d38..adc6d494 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/IncomingRequestScreen.kt @@ -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, + bunkerRequests: List, 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, + ) } } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt index 05fc2d72..ac6c9110 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt @@ -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, + bunkerRequests: List, packageName: String?, appName: String?, route: MutableState, @@ -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, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/SeeDetailsScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/SeeDetailsScreen.kt index 9177684c..ee04153b 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/SeeDetailsScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/SeeDetailsScreen.kt @@ -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, + ) + } + } + } } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AmberTopAppBar.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AmberTopAppBar.kt index 6b18800a..40f61c27 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AmberTopAppBar.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AmberTopAppBar.kt @@ -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, + bunkerRequests: List, 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 ?: "" diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt index acb66962..cba23cde 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt @@ -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, + bunkerRequests: List, packageName: String?, accountParam: Account, navController: NavController, onRemoveIntentData: (List, 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, + bunkerRequests: List, packageName: String?, accountParam: Account, navController: NavController, - onRemoveIntentData: (List, 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) { - 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>, + index: Int, + item: Pair>, + onDetailsClick: (List) -> 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, + ) + } + } + } + } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt index 510c0676..e7ae0928 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt @@ -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, 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, 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?>(null) } var localCloseApplication by remember { mutableStateOf(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, ) }, ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e0d21d1e..dbeb1ca1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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"