pakstr 0.31.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/android-template/app/build.gradle.kts +1 -0
- package/android-template/app/src/androidTest/java/com/pakstr/app/PakstrWebBridgeTest.kt +153 -0
- package/android-template/app/src/main/assets/nip07.js +124 -74
- package/android-template/app/src/main/java/com/pakstr/app/AppOrigin.kt +77 -0
- package/android-template/app/src/main/java/com/pakstr/app/PakstrBridge.kt +5 -5
- package/android-template/app/src/main/java/com/pakstr/app/PakstrWebBridge.kt +89 -0
- package/android-template/app/src/main/java/com/pakstr/app/WebMessageRouter.kt +186 -0
- package/android-template/app/src/main/java/com/pakstr/app/WebViewController.kt +65 -49
- package/android-template/app/src/main/java/com/pakstr/app/signer/AmberRequestChannel.kt +156 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/AmberSigner.kt +142 -390
- package/android-template/app/src/main/java/com/pakstr/app/signer/NostrBridge.kt +69 -489
- package/android-template/app/src/main/java/com/pakstr/app/signer/SignerManager.kt +13 -2
- package/android-template/app/src/test/java/com/pakstr/app/AppOriginTest.kt +114 -0
- package/android-template/app/src/test/java/com/pakstr/app/WebMessageRouterTest.kt +279 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/AmberRequestChannelTest.kt +294 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/NostrBridgeDispatchTest.kt +113 -0
- package/android-template/gradle/libs.versions.toml +2 -0
- package/package.json +1 -1
|
@@ -7,12 +7,10 @@ import android.net.Uri
|
|
|
7
7
|
import androidx.activity.result.ActivityResult
|
|
8
8
|
import androidx.activity.result.ActivityResultLauncher
|
|
9
9
|
import com.pakstr.app.crypto.NostrEventVerifier
|
|
10
|
-
import com.pakstr.app.debug.AppDebugLogger
|
|
11
10
|
import com.pakstr.app.signer.interfaces.NostrSigner
|
|
12
|
-
import kotlinx.coroutines.
|
|
13
|
-
import kotlinx.coroutines.
|
|
11
|
+
import kotlinx.coroutines.sync.Mutex
|
|
12
|
+
import kotlinx.coroutines.sync.withLock
|
|
14
13
|
import org.json.JSONObject
|
|
15
|
-
import kotlin.coroutines.resume
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
16
|
* NIP-07 signer implementation using Amber as an external signer.
|
|
@@ -31,170 +29,86 @@ import kotlin.coroutines.resume
|
|
|
31
29
|
* After receiving the signature, the event ID is generated and the
|
|
32
30
|
* signature is locally verified before returning the signed event.
|
|
33
31
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* Concurrent requests are serialized and matched to their activity results
|
|
33
|
+
* by [AmberRequestChannel].
|
|
36
34
|
*/
|
|
37
|
-
class AmberSigner(
|
|
38
|
-
private val launcher: ActivityResultLauncher<Intent
|
|
35
|
+
class AmberSigner internal constructor(
|
|
36
|
+
private val launcher: ActivityResultLauncher<Intent>,
|
|
37
|
+
private val channel: AmberRequestChannel
|
|
39
38
|
) : NostrSigner {
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
private var pendingCallback: ((String) -> Unit)? = null
|
|
45
|
-
private var cachedPubkey: String? = null
|
|
46
|
-
override suspend fun getPublicKey(): String {
|
|
47
|
-
|
|
48
|
-
cachedPubkey?.let {
|
|
49
|
-
|
|
50
|
-
return it
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return withTimeout(30_000) {
|
|
55
|
-
|
|
56
|
-
suspendCancellableCoroutine { continuation ->
|
|
57
|
-
|
|
58
|
-
pendingCallback = { pubkey ->
|
|
59
|
-
|
|
60
|
-
cachedPubkey = pubkey
|
|
61
|
-
|
|
62
|
-
if (continuation.isActive) {
|
|
63
|
-
|
|
64
|
-
continuation.resume(pubkey)
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
val intent = Intent(
|
|
71
|
-
|
|
72
|
-
Intent.ACTION_VIEW
|
|
73
|
-
|
|
74
|
-
).apply {
|
|
75
|
-
|
|
76
|
-
data = Uri.parse(
|
|
77
|
-
|
|
78
|
-
"nostrsigner:?type=get_public_key"
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
putExtra(
|
|
83
|
-
|
|
84
|
-
"type",
|
|
85
|
-
|
|
86
|
-
"get_public_key"
|
|
40
|
+
constructor(
|
|
41
|
+
launcher: ActivityResultLauncher<Intent>
|
|
42
|
+
) : this(launcher, AmberRequestChannel())
|
|
87
43
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
try {
|
|
44
|
+
private val session = channel.openSession()
|
|
93
45
|
|
|
94
|
-
|
|
46
|
+
private val pubkeyMutex = Mutex()
|
|
95
47
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (continuation.isActive) {
|
|
99
|
-
|
|
100
|
-
continuation.resume("")
|
|
48
|
+
@Volatile
|
|
49
|
+
private var cachedPubkey: String? = null
|
|
101
50
|
|
|
51
|
+
override suspend fun getPublicKey(): String {
|
|
52
|
+
cachedPubkey?.let { return it }
|
|
53
|
+
|
|
54
|
+
return session.withRequestTimeout {
|
|
55
|
+
// Concurrent callers share one Amber prompt.
|
|
56
|
+
pubkeyMutex.withLock {
|
|
57
|
+
cachedPubkey ?: send(
|
|
58
|
+
Intent(Intent.ACTION_VIEW).apply {
|
|
59
|
+
data = Uri.parse("nostrsigner:?type=get_public_key")
|
|
60
|
+
putExtra("type", "get_public_key")
|
|
102
61
|
}
|
|
103
|
-
|
|
62
|
+
).also { pubkey ->
|
|
63
|
+
if (pubkey.isNotBlank()) cachedPubkey = pubkey
|
|
104
64
|
}
|
|
105
|
-
|
|
106
|
-
continuation.invokeOnCancellation {
|
|
107
|
-
|
|
108
|
-
pendingCallback = null
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
65
|
}
|
|
113
|
-
|
|
114
66
|
}
|
|
115
|
-
|
|
116
67
|
}
|
|
117
68
|
|
|
118
69
|
override suspend fun signEvent(
|
|
119
70
|
eventJson: String
|
|
120
71
|
): String =
|
|
121
|
-
|
|
122
|
-
|
|
72
|
+
session.withRequestTimeout {
|
|
123
73
|
val event = JSONObject(eventJson)
|
|
124
74
|
|
|
125
75
|
val currentPubkey = event.optString("pubkey")
|
|
126
76
|
|
|
127
77
|
if (currentPubkey.isBlank()) {
|
|
128
|
-
|
|
129
78
|
val amberPubkey = getPublicKey()
|
|
130
79
|
|
|
131
80
|
require(amberPubkey.isNotBlank()) {
|
|
132
|
-
|
|
133
81
|
"Amber returned empty pubkey"
|
|
134
|
-
|
|
135
82
|
}
|
|
136
83
|
|
|
137
|
-
event.put(
|
|
138
|
-
|
|
139
|
-
"pubkey",
|
|
140
|
-
|
|
141
|
-
amberPubkey
|
|
84
|
+
event.put("pubkey", amberPubkey)
|
|
85
|
+
}
|
|
142
86
|
|
|
87
|
+
val intent = Intent(
|
|
88
|
+
Intent.ACTION_VIEW,
|
|
89
|
+
Uri.parse(
|
|
90
|
+
"nostrsigner:${Uri.encode(event.toString())}"
|
|
143
91
|
)
|
|
144
|
-
|
|
92
|
+
).apply {
|
|
93
|
+
putExtra("type", "sign_event")
|
|
145
94
|
}
|
|
146
95
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (signature.isEmpty()) {
|
|
152
|
-
|
|
153
|
-
continuation.resume("")
|
|
154
|
-
|
|
155
|
-
} else {
|
|
156
|
-
|
|
157
|
-
val signedEvent =
|
|
158
|
-
NostrEventBuilder.addId(event)
|
|
159
|
-
.apply {
|
|
160
|
-
put(
|
|
161
|
-
"sig",
|
|
162
|
-
signature
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
val verified =
|
|
168
|
-
NostrEventVerifier.verify(
|
|
169
|
-
signedEvent
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (verified) {
|
|
174
|
-
continuation.resume(
|
|
175
|
-
signedEvent.toString()
|
|
176
|
-
)
|
|
177
|
-
} else {
|
|
178
|
-
continuation.resume("")
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
96
|
+
val signature = session.request { requestId ->
|
|
97
|
+
launcher.launch(intent.putExtra("id", requestId))
|
|
98
|
+
}
|
|
182
99
|
|
|
100
|
+
if (signature.isEmpty()) {
|
|
101
|
+
""
|
|
102
|
+
} else {
|
|
103
|
+
val signedEvent =
|
|
104
|
+
NostrEventBuilder.addId(event)
|
|
105
|
+
.apply { put("sig", signature) }
|
|
183
106
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
)
|
|
189
|
-
).apply {
|
|
190
|
-
putExtra(
|
|
191
|
-
"type",
|
|
192
|
-
"sign_event"
|
|
193
|
-
)
|
|
107
|
+
if (NostrEventVerifier.verify(signedEvent)) {
|
|
108
|
+
signedEvent.toString()
|
|
109
|
+
} else {
|
|
110
|
+
""
|
|
194
111
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
launcher.launch(intent)
|
|
198
112
|
}
|
|
199
113
|
}
|
|
200
114
|
|
|
@@ -202,297 +116,135 @@ class AmberSigner(
|
|
|
202
116
|
pubkey: String,
|
|
203
117
|
plaintext: String
|
|
204
118
|
): String =
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
pendingCallback = { encrypted ->
|
|
210
|
-
|
|
211
|
-
if (continuation.isActive) {
|
|
212
|
-
continuation.resume(encrypted)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
val intent = Intent(
|
|
217
|
-
Intent.ACTION_VIEW
|
|
218
|
-
).apply {
|
|
219
|
-
|
|
119
|
+
session.withRequestTimeout {
|
|
120
|
+
send(
|
|
121
|
+
Intent(Intent.ACTION_VIEW).apply {
|
|
220
122
|
data = Uri.parse(
|
|
221
123
|
"nostrsigner:?type=nip04_encrypt" +
|
|
222
124
|
"&pubkey=$pubkey" +
|
|
223
125
|
"&plaintext=${Uri.encode(plaintext)}"
|
|
224
126
|
)
|
|
225
|
-
|
|
226
|
-
putExtra(
|
|
227
|
-
|
|
228
|
-
"nip04_encrypt"
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
putExtra(
|
|
232
|
-
"pubkey",
|
|
233
|
-
pubkey
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
putExtra(
|
|
237
|
-
"plaintext",
|
|
238
|
-
plaintext
|
|
239
|
-
)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
|
|
244
|
-
launcher.launch(intent)
|
|
245
|
-
|
|
246
|
-
} catch (_: ActivityNotFoundException) {
|
|
247
|
-
|
|
248
|
-
if (continuation.isActive) {
|
|
249
|
-
continuation.resume("")
|
|
250
|
-
}
|
|
127
|
+
putExtra("type", "nip04_encrypt")
|
|
128
|
+
putExtra("pubkey", pubkey)
|
|
129
|
+
putExtra("plaintext", plaintext)
|
|
251
130
|
}
|
|
252
|
-
|
|
253
|
-
continuation.invokeOnCancellation {
|
|
254
|
-
pendingCallback = null
|
|
255
|
-
}
|
|
256
|
-
}
|
|
131
|
+
)
|
|
257
132
|
}
|
|
258
133
|
|
|
259
134
|
override suspend fun nip04Decrypt(
|
|
260
135
|
pubkey: String,
|
|
261
136
|
ciphertext: String
|
|
262
|
-
): String =
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
Intent.ACTION_VIEW
|
|
275
|
-
).apply {
|
|
276
|
-
|
|
277
|
-
data = Uri.parse(
|
|
278
|
-
"nostrsigner:?type=nip04_decrypt" +
|
|
279
|
-
"&pubkey=$pubkey" +
|
|
280
|
-
"&message=${Uri.encode(ciphertext)}"
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
putExtra(
|
|
284
|
-
"type",
|
|
285
|
-
"nip04_decrypt"
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
putExtra(
|
|
289
|
-
"pubkey",
|
|
290
|
-
pubkey
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
putExtra(
|
|
294
|
-
"message",
|
|
295
|
-
ciphertext
|
|
296
|
-
)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
|
|
301
|
-
launcher.launch(intent)
|
|
302
|
-
|
|
303
|
-
} catch (_: ActivityNotFoundException) {
|
|
304
|
-
|
|
305
|
-
if (continuation.isActive) {
|
|
306
|
-
continuation.resume("")
|
|
137
|
+
): String =
|
|
138
|
+
session.withRequestTimeout {
|
|
139
|
+
send(
|
|
140
|
+
Intent(Intent.ACTION_VIEW).apply {
|
|
141
|
+
data = Uri.parse(
|
|
142
|
+
"nostrsigner:?type=nip04_decrypt" +
|
|
143
|
+
"&pubkey=$pubkey" +
|
|
144
|
+
"&message=${Uri.encode(ciphertext)}"
|
|
145
|
+
)
|
|
146
|
+
putExtra("type", "nip04_decrypt")
|
|
147
|
+
putExtra("pubkey", pubkey)
|
|
148
|
+
putExtra("message", ciphertext)
|
|
307
149
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
continuation.invokeOnCancellation {
|
|
311
|
-
pendingCallback = null
|
|
312
|
-
}
|
|
150
|
+
)
|
|
313
151
|
}
|
|
314
|
-
}
|
|
315
152
|
|
|
316
153
|
override suspend fun nip44Encrypt(
|
|
317
|
-
|
|
318
154
|
pubkey: String,
|
|
319
|
-
|
|
320
155
|
plaintext: String
|
|
321
|
-
|
|
322
156
|
): String =
|
|
323
|
-
|
|
324
|
-
withTimeout(30_000) {
|
|
325
|
-
|
|
157
|
+
session.withRequestTimeout {
|
|
326
158
|
val currentUser = getCachedPublicKey()
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
val intent = Intent(
|
|
341
|
-
|
|
342
|
-
Intent.ACTION_VIEW
|
|
343
|
-
|
|
344
|
-
).apply {
|
|
345
|
-
|
|
346
|
-
data = Uri.parse(
|
|
347
|
-
|
|
348
|
-
"nostrsigner:${Uri.encode(plaintext)}"
|
|
349
|
-
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
putExtra(
|
|
353
|
-
|
|
354
|
-
"type",
|
|
355
|
-
|
|
356
|
-
"nip44_encrypt"
|
|
357
|
-
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
putExtra(
|
|
361
|
-
|
|
362
|
-
"pubkey",
|
|
363
|
-
|
|
364
|
-
pubkey
|
|
365
|
-
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
putExtra(
|
|
369
|
-
|
|
370
|
-
"current_user",
|
|
371
|
-
|
|
372
|
-
currentUser
|
|
373
|
-
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
try {
|
|
379
|
-
|
|
380
|
-
launcher.launch(intent)
|
|
381
|
-
|
|
382
|
-
} catch (_: ActivityNotFoundException) {
|
|
383
|
-
|
|
384
|
-
if (continuation.isActive) {
|
|
385
|
-
|
|
386
|
-
continuation.resume("")
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
continuation.invokeOnCancellation {
|
|
393
|
-
|
|
394
|
-
pendingCallback = null
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
159
|
+
send(
|
|
160
|
+
Intent(Intent.ACTION_VIEW).apply {
|
|
161
|
+
data = Uri.parse("nostrsigner:${Uri.encode(plaintext)}")
|
|
162
|
+
putExtra("type", "nip44_encrypt")
|
|
163
|
+
putExtra("pubkey", pubkey)
|
|
164
|
+
putExtra("current_user", currentUser)
|
|
165
|
+
}
|
|
166
|
+
)
|
|
400
167
|
}
|
|
401
168
|
|
|
402
169
|
override suspend fun nip44Decrypt(
|
|
403
170
|
pubkey: String,
|
|
404
171
|
ciphertext: String
|
|
405
172
|
): String =
|
|
406
|
-
|
|
173
|
+
session.withRequestTimeout {
|
|
407
174
|
val currentUser = getCachedPublicKey()
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
val intent = Intent(
|
|
418
|
-
Intent.ACTION_VIEW
|
|
419
|
-
).apply {
|
|
420
|
-
|
|
421
|
-
data = Uri.parse(
|
|
422
|
-
"nostrsigner:${Uri.encode(ciphertext)}"
|
|
423
|
-
)
|
|
424
|
-
|
|
425
|
-
putExtra(
|
|
426
|
-
"type",
|
|
427
|
-
"nip44_decrypt"
|
|
428
|
-
)
|
|
429
|
-
|
|
430
|
-
putExtra(
|
|
431
|
-
"pubkey",
|
|
432
|
-
pubkey
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
putExtra(
|
|
436
|
-
"current_user",
|
|
437
|
-
currentUser
|
|
438
|
-
)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
|
|
443
|
-
launcher.launch(intent)
|
|
444
|
-
|
|
445
|
-
} catch (_: ActivityNotFoundException) {
|
|
446
|
-
|
|
447
|
-
if (continuation.isActive) {
|
|
448
|
-
continuation.resume("")
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
continuation.invokeOnCancellation {
|
|
454
|
-
pendingCallback = null
|
|
455
|
-
}
|
|
456
|
-
}
|
|
175
|
+
send(
|
|
176
|
+
Intent(Intent.ACTION_VIEW).apply {
|
|
177
|
+
data = Uri.parse("nostrsigner:${Uri.encode(ciphertext)}")
|
|
178
|
+
putExtra("type", "nip44_decrypt")
|
|
179
|
+
putExtra("pubkey", pubkey)
|
|
180
|
+
putExtra("current_user", currentUser)
|
|
181
|
+
}
|
|
182
|
+
)
|
|
457
183
|
}
|
|
458
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Delivers an Amber activity result to the request that launched it.
|
|
187
|
+
* Returns false if no outstanding request matched and it was dropped.
|
|
188
|
+
*/
|
|
459
189
|
fun onResult(
|
|
460
190
|
result: ActivityResult
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
191
|
+
): Boolean = channel.deliver(result)
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Fails this signer's waiting requests and stops it launching Amber;
|
|
195
|
+
* used when the signer is replaced.
|
|
196
|
+
*/
|
|
197
|
+
fun close() {
|
|
198
|
+
session.close()
|
|
199
|
+
}
|
|
466
200
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
201
|
+
/** Launches [intent] and waits for its result; "" if Amber is missing. */
|
|
202
|
+
private suspend fun send(intent: Intent): String {
|
|
203
|
+
return try {
|
|
204
|
+
session.request { requestId ->
|
|
205
|
+
launcher.launch(intent.putExtra("id", requestId))
|
|
470
206
|
}
|
|
471
|
-
|
|
472
|
-
|
|
207
|
+
} catch (_: ActivityNotFoundException) {
|
|
208
|
+
""
|
|
473
209
|
}
|
|
474
|
-
|
|
475
|
-
val value =
|
|
476
|
-
|
|
477
|
-
result.data?.getStringExtra("result")
|
|
478
|
-
|
|
479
|
-
?: result.data?.getStringExtra("event")
|
|
480
|
-
|
|
481
|
-
?: result.data?.getStringExtra("signature")
|
|
482
|
-
|
|
483
|
-
?: result.data?.getStringExtra("plaintext")
|
|
484
|
-
|
|
485
|
-
?: result.data?.getStringExtra("message")
|
|
486
|
-
?: result.data?.getStringExtra("content")
|
|
487
|
-
|
|
488
|
-
?: ""
|
|
489
|
-
|
|
490
|
-
callback?.invoke(value)
|
|
491
210
|
}
|
|
492
211
|
|
|
493
212
|
private suspend fun getCachedPublicKey(): String {
|
|
494
|
-
|
|
495
213
|
return cachedPubkey ?: getPublicKey()
|
|
496
|
-
|
|
497
214
|
}
|
|
498
|
-
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Delivers [result] to the request it answers; see [AmberRequestChannel.deliver]. */
|
|
218
|
+
internal fun AmberRequestChannel.deliver(result: ActivityResult): Boolean {
|
|
219
|
+
val data = result.data
|
|
220
|
+
return deliver(
|
|
221
|
+
data?.getStringExtra("id"),
|
|
222
|
+
amberResultOutcome(
|
|
223
|
+
resultCode = result.resultCode,
|
|
224
|
+
rejected = data?.getBooleanExtra("rejected", false) == true,
|
|
225
|
+
extra = { name -> data?.getStringExtra(name) }
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Maps an Amber activity result to the value its request resolves with.
|
|
232
|
+
* A NIP-55 rejection fails the request and none of its extras are read.
|
|
233
|
+
*/
|
|
234
|
+
internal fun amberResultOutcome(
|
|
235
|
+
resultCode: Int,
|
|
236
|
+
rejected: Boolean,
|
|
237
|
+
extra: (String) -> String?
|
|
238
|
+
): Result<String> = when {
|
|
239
|
+
resultCode != Activity.RESULT_OK -> Result.success("")
|
|
240
|
+
rejected -> Result.failure(AmberRequestRejectedException())
|
|
241
|
+
else -> Result.success(
|
|
242
|
+
extra("result")
|
|
243
|
+
?: extra("event")
|
|
244
|
+
?: extra("signature")
|
|
245
|
+
?: extra("plaintext")
|
|
246
|
+
?: extra("message")
|
|
247
|
+
?: extra("content")
|
|
248
|
+
?: ""
|
|
249
|
+
)
|
|
250
|
+
}
|