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 @@