From 5cc834f2063aed3b99a17013c3f31e427eef22ea Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 10 Nov 2025 09:18:30 -0300 Subject: [PATCH] Support for ping requests when using bunker --- app/src/main/AndroidManifest.xml | 2 +- .../greenart7c3/nostrsigner/SignerProvider.kt | 70 ++++++++++++ .../nostrsigner/models/SignerType.kt | 1 + .../nostrsigner/service/BunkerRequestUtils.kt | 2 + .../service/EventNotificationConsumer.kt | 2 +- .../ui/components/BunkerPingScreen.kt | 101 ++++++++++++++++++ .../components/BunkerSingleEventHomeScreen.kt | 57 ++++++++++ app/src/main/res/values/strings.xml | 2 + app/src/offline/AndroidManifest.xml | 2 +- 9 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerPingScreen.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7316f255..4cf4cc79 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -54,7 +54,7 @@ diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt b/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt index c4655747..ee0edbe5 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt @@ -456,7 +456,77 @@ class SignerProvider : ContentProvider() { cursor.addRow(arrayOf(result, result)) return cursor } + "content://$appId.PING" -> { + val npub = if (projection != null && projection.isNotEmpty()) IntentUtils.parsePubKey(projection[0]) else null + val account = if (npub != null) { + LocalPreferences.loadFromEncryptedStorageSync(context!!, npub) + } else { + LocalPreferences.allSavedAccounts(context!!).firstNotNullOfOrNull { accountInfo -> + val localDatabase = Amber.instance.getDatabase(accountInfo.npub) + val hasAccount = localDatabase.dao().getByKeySync(packageName) != null + if (hasAccount) { + LocalPreferences.loadFromEncryptedStorageSync(context!!, accountInfo.npub) + } else { + null + } + } + } + if (account == null) { + return null + } + val database = Amber.instance.getDatabase(account.npub) + val historyDatabase = Amber.instance.getHistoryDatabase(account.npub) + val permission = + database + .dao() + .getPermission( + packageName, + "PING", + ) + val signPolicy = database.dao().getSignPolicy(packageName) + val isRemembered = IntentUtils.isRemembered(signPolicy, permission) ?: return null + if (!isRemembered) { + scope.launch { + historyDatabase.dao().addHistory( + HistoryEntity( + 0, + packageName, + uri.toString().replace("content://$appId.", ""), + null, + TimeUtils.now(), + false, + ), + account.npub, + ) + } + + val cursor = + MatrixCursor(arrayOf("rejected")).also { + it.addRow(arrayOf("true")) + } + + return cursor + } + + scope.launch { + historyDatabase.dao().addHistory( + HistoryEntity( + 0, + packageName, + uri.toString().replace("content://$appId.", ""), + null, + TimeUtils.now(), + true, + ), + account.npub, + ) + } + + val cursor = MatrixCursor(arrayOf("signature", "result")) + cursor.addRow(arrayOf("pong", "pong")) + return cursor + } else -> null } } catch (e: Exception) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt index 96c838d2..42011e69 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/SignerType.kt @@ -10,5 +10,6 @@ enum class SignerType { NIP44_DECRYPT, GET_PUBLIC_KEY, DECRYPT_ZAP_EVENT, + PING, INVALID, } 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 90ee5e50..b5f409cc 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/BunkerRequestUtils.kt @@ -203,6 +203,7 @@ object BunkerRequestUtils { "nip44_encrypt" -> SignerType.NIP44_ENCRYPT "nip44_decrypt" -> SignerType.NIP44_DECRYPT "decrypt_zap_event" -> SignerType.DECRYPT_ZAP_EVENT + "ping" -> SignerType.PING else -> SignerType.INVALID } } @@ -216,6 +217,7 @@ object BunkerRequestUtils { AmberEvent.toEvent(amberEvent).toJson() } "nip04_encrypt", "nip04_decrypt", "nip44_encrypt", "nip44_decrypt", "decrypt_zap_event" -> bunkerRequest.params.getOrElse(1) { "" } + "ping" -> "pong" else -> "" } } 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 29a896e2..5fc15cd4 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt @@ -402,7 +402,7 @@ class EventNotificationConsumer(private val applicationContext: Context) { return } val data = BunkerRequestUtils.getDataFromBunker(bunkerRequest) - val projection = if (type == SignerType.GET_PUBLIC_KEY) { + val projection = if (type == SignerType.GET_PUBLIC_KEY || type == SignerType.PING) { arrayOf(acc.npub) } else { arrayOf(data, pubKey, acc.npub) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerPingScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerPingScreen.kt new file mode 100644 index 00000000..dbf6866a --- /dev/null +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerPingScreen.kt @@ -0,0 +1,101 @@ +package com.greenart7c3.nostrsigner.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.greenart7c3.nostrsigner.R +import com.greenart7c3.nostrsigner.models.Account +import com.greenart7c3.nostrsigner.ui.RememberType + +@Composable +fun BunkerPingScreen( + account: Account, + modifier: Modifier, + shouldRunOnAccept: Boolean?, + appName: String, + onAccept: (RememberType) -> Unit, + onReject: (RememberType) -> Unit, +) { + var rememberType by remember { + mutableStateOf(RememberType.NEVER) + } + + Column( + modifier, + ) { + ProfilePicture(account) + + val message = stringResource(R.string.ping) + + Text( + buildAnnotatedString { + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + append(appName) + } + append(" ${stringResource(R.string.requests)} $message") + }, + fontSize = 18.sp, + ) + Spacer(Modifier.size(4.dp)) + + Card( + modifier = Modifier + .fillMaxWidth(), + ) { + Column(Modifier.padding(6.dp)) { + Text( + stringResource(R.string.response), + fontWeight = FontWeight.Bold, + ) + + Text( + "pong", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + ) + } + } + + Spacer(modifier = Modifier.weight(1f)) + + RememberMyChoice( + shouldRunOnAccept, + null, + true, + onAccept, + onReject, + ) { + rememberType = it + } + + AcceptRejectButtons( + onAccept = { + onAccept(rememberType) + }, + onReject = { + onReject(rememberType) + }, + ) + } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt index c91b6821..88fcd7cb 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt @@ -46,6 +46,7 @@ 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.BunkerRequestPing import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestSign import com.vitorpamplona.quartz.nip55AndroidSigner.signString import com.vitorpamplona.quartz.utils.Hex @@ -83,6 +84,62 @@ fun BunkerSingleEventHomeScreen( val type = BunkerRequestUtils.getTypeFromBunker(bunkerRequest.request) when (bunkerRequest.request) { + is BunkerRequestPing -> { + 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 + } + + BunkerPingScreen( + modifier = modifier, + account = account, + appName = appName, + shouldRunOnAccept = acceptOrReject, + onAccept = { + val result = "pong" + + BunkerRequestUtils.sendResult( + context = context, + account = account, + key = key, + response = result, + bunkerRequest = bunkerRequest, + kind = null, + onLoading = onLoading, + permissions = null, + appName = appName, + signPolicy = null, + shouldCloseApplication = null, + rememberType = it, + ) + }, + onReject = { + BunkerRequestUtils.sendRejection( + key = key, + account = account, + bunkerRequest = bunkerRequest, + appName = appName, + rememberType = it, + signerType = type, + kind = null, + onLoading = onLoading, + ) + }, + ) + } is BunkerRequestConnect -> { var showExistingAppDialog by remember { mutableStateOf(false) } var localPermissions by remember { mutableStateOf?>(null) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f9bfb1f8..965b400e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -594,4 +594,6 @@ Read %1$s from %2$s encrypted content Read this list of tags from %1$s encrypted content Read this text from %1$s encrypted content + Ping + Response diff --git a/app/src/offline/AndroidManifest.xml b/app/src/offline/AndroidManifest.xml index dce3fc29..b05f56b8 100644 --- a/app/src/offline/AndroidManifest.xml +++ b/app/src/offline/AndroidManifest.xml @@ -20,7 +20,7 @@