pakstr 0.27.0 → 0.28.0
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/android-template/app/src/androidTest/java/com/pakstr/app/signer/SignerManagerTest.kt +201 -0
- package/android-template/app/src/androidTest/java/com/pakstr/app/signer/local/LocalAccountStoreTest.kt +194 -0
- package/android-template/app/src/androidTest/java/com/pakstr/app/utils/SignerPickerBottomSheetTest.kt +131 -0
- package/android-template/app/src/main/java/com/pakstr/app/MainActivity.kt +87 -11
- package/android-template/app/src/main/java/com/pakstr/app/signer/LocalSigner.kt +100 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/NostrBridge.kt +6 -6
- package/android-template/app/src/main/java/com/pakstr/app/signer/SignerManager.kt +110 -3
- package/android-template/app/src/main/java/com/pakstr/app/signer/SignerType.kt +1 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/local/LocalAccountStore.kt +245 -0
- package/android-template/app/src/main/java/com/pakstr/app/utils/SignerPickerBottomSheet.kt +19 -9
- package/android-template/app/src/main/res/layout/dialog_picker.xml +18 -0
- package/android-template/app/src/main/res/values/strings.xml +3 -1
- package/android-template/app/src/main/res/xml/backup_rules.xml +4 -11
- package/android-template/app/src/main/res/xml/data_extraction_rules.xml +7 -13
- package/android-template/app/src/test/java/com/pakstr/app/signer/LocalSignerTest.kt +110 -0
- package/package.json +1 -1
|
@@ -42,7 +42,9 @@ import com.pakstr.app.permissions.interfaces.PermissionHandler
|
|
|
42
42
|
import com.pakstr.app.signer.SignerManager
|
|
43
43
|
import com.pakstr.app.signer.SignerType
|
|
44
44
|
import kotlinx.coroutines.CancellationException
|
|
45
|
+
import kotlinx.coroutines.Dispatchers
|
|
45
46
|
import kotlinx.coroutines.launch
|
|
47
|
+
import kotlinx.coroutines.withContext
|
|
46
48
|
import com.pakstr.app.utils.AppPreferences
|
|
47
49
|
import com.pakstr.app.utils.BunkerPairingDialog
|
|
48
50
|
import com.pakstr.app.utils.SignerPickerBottomSheet
|
|
@@ -61,6 +63,7 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
61
63
|
private lateinit var fab: FloatingActionButton
|
|
62
64
|
private lateinit var supportUnlockGestureLayout: SupportUnlockGestureLayout
|
|
63
65
|
private var bunkerPairingDialog: AlertDialog? = null
|
|
66
|
+
private var signerSetupInProgress = false
|
|
64
67
|
|
|
65
68
|
private val diagnosticsHandler = Handler(Looper.getMainLooper())
|
|
66
69
|
private val diagnosticsExpiryCheck = Runnable {
|
|
@@ -128,12 +131,6 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
initSigner()
|
|
131
|
-
if (
|
|
132
|
-
savedInstanceState == null &&
|
|
133
|
-
AppPreferences.getBoolean("isFirstRun", true)
|
|
134
|
-
) {
|
|
135
|
-
container.post(::showSignerPicker)
|
|
136
|
-
}
|
|
137
134
|
|
|
138
135
|
ViewCompat.setOnApplyWindowInsetsListener(
|
|
139
136
|
|
|
@@ -269,8 +266,26 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
269
266
|
SignerManager(
|
|
270
267
|
this,
|
|
271
268
|
amberLauncher,
|
|
272
|
-
bunkerAuthChallengeHandler = ::openBunkerAuthChallenge
|
|
269
|
+
bunkerAuthChallengeHandler = ::openBunkerAuthChallenge,
|
|
270
|
+
signerPickerRequestHandler = ::requestSignerPicker
|
|
273
271
|
)
|
|
272
|
+
signerManager.migrateLegacySignerConfiguration(
|
|
273
|
+
setupCompleted = !AppPreferences.getBoolean("isFirstRun", true)
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private fun requestSignerPicker() {
|
|
278
|
+
runOnUiThread {
|
|
279
|
+
if (
|
|
280
|
+
isFinishing ||
|
|
281
|
+
isDestroyed ||
|
|
282
|
+
supportFragmentManager.isStateSaved
|
|
283
|
+
) {
|
|
284
|
+
signerManager.cancelSignerSelection()
|
|
285
|
+
return@runOnUiThread
|
|
286
|
+
}
|
|
287
|
+
showSignerPicker()
|
|
288
|
+
}
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
private fun openBunkerAuthChallenge(value: String) {
|
|
@@ -408,6 +423,11 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
408
423
|
}
|
|
409
424
|
|
|
410
425
|
private fun showSignerPicker() {
|
|
426
|
+
if (
|
|
427
|
+
supportFragmentManager.findFragmentByTag(SIGNER_PICKER_TAG) != null
|
|
428
|
+
) {
|
|
429
|
+
return
|
|
430
|
+
}
|
|
411
431
|
SignerPickerBottomSheet(
|
|
412
432
|
hasBunkerSession = signerManager.hasBunkerSession(),
|
|
413
433
|
onAmberSelected = {
|
|
@@ -423,7 +443,14 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
423
443
|
false
|
|
424
444
|
}
|
|
425
445
|
},
|
|
426
|
-
|
|
446
|
+
onLocalSelected = {
|
|
447
|
+
signerSetupInProgress = true
|
|
448
|
+
createLocalAccount()
|
|
449
|
+
},
|
|
450
|
+
onSharePairing = { relayUrls ->
|
|
451
|
+
signerSetupInProgress = true
|
|
452
|
+
shareBunkerPairing(relayUrls)
|
|
453
|
+
},
|
|
427
454
|
onBunkerLogout = {
|
|
428
455
|
signerManager.logoutBunker()
|
|
429
456
|
Toast.makeText(
|
|
@@ -431,13 +458,45 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
431
458
|
R.string.signer_picker_disconnected,
|
|
432
459
|
Toast.LENGTH_SHORT
|
|
433
460
|
).show()
|
|
461
|
+
},
|
|
462
|
+
onDismissed = {
|
|
463
|
+
if (!signerSetupInProgress) {
|
|
464
|
+
signerManager.cancelSignerSelection()
|
|
465
|
+
}
|
|
434
466
|
}
|
|
435
467
|
).show(
|
|
436
468
|
supportFragmentManager,
|
|
437
|
-
|
|
469
|
+
SIGNER_PICKER_TAG
|
|
438
470
|
)
|
|
439
471
|
}
|
|
440
472
|
|
|
473
|
+
private fun createLocalAccount() {
|
|
474
|
+
lifecycleScope.launch {
|
|
475
|
+
val created =
|
|
476
|
+
withContext(Dispatchers.IO) {
|
|
477
|
+
try {
|
|
478
|
+
signerManager.createLocalAccount()
|
|
479
|
+
true
|
|
480
|
+
} catch (cancelled: CancellationException) {
|
|
481
|
+
throw cancelled
|
|
482
|
+
} catch (_: Exception) {
|
|
483
|
+
false
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
signerSetupInProgress = false
|
|
487
|
+
if (created) {
|
|
488
|
+
handleSignerSetup()
|
|
489
|
+
} else {
|
|
490
|
+
signerManager.cancelSignerSelection()
|
|
491
|
+
Toast.makeText(
|
|
492
|
+
this@MainActivity,
|
|
493
|
+
R.string.signer_picker_local_failed,
|
|
494
|
+
Toast.LENGTH_LONG
|
|
495
|
+
).show()
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
441
500
|
private fun shareBunkerPairing(relayUrls: List<String>) {
|
|
442
501
|
lifecycleScope.launch {
|
|
443
502
|
val pairingUri =
|
|
@@ -446,6 +505,8 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
446
505
|
} catch (cancelled: CancellationException) {
|
|
447
506
|
throw cancelled
|
|
448
507
|
} catch (_: Exception) {
|
|
508
|
+
signerSetupInProgress = false
|
|
509
|
+
signerManager.cancelSignerSelection()
|
|
449
510
|
Toast.makeText(
|
|
450
511
|
this@MainActivity,
|
|
451
512
|
R.string.signer_picker_connection_failed,
|
|
@@ -469,7 +530,18 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
469
530
|
R.string.signer_picker_pairing_waiting,
|
|
470
531
|
Toast.LENGTH_LONG
|
|
471
532
|
).show()
|
|
472
|
-
val connected =
|
|
533
|
+
val connected =
|
|
534
|
+
try {
|
|
535
|
+
signerManager.finishBunkerPairing()
|
|
536
|
+
} catch (cancelled: CancellationException) {
|
|
537
|
+
throw cancelled
|
|
538
|
+
} catch (_: Exception) {
|
|
539
|
+
false
|
|
540
|
+
}
|
|
541
|
+
signerSetupInProgress = false
|
|
542
|
+
if (!connected) {
|
|
543
|
+
signerManager.cancelSignerSelection()
|
|
544
|
+
}
|
|
473
545
|
if (pairingDialog.isShowing) {
|
|
474
546
|
pairingDialog.dismiss()
|
|
475
547
|
}
|
|
@@ -510,4 +582,8 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
510
582
|
DebugReportShare.share(this)
|
|
511
583
|
|
|
512
584
|
}
|
|
513
|
-
|
|
585
|
+
|
|
586
|
+
private companion object {
|
|
587
|
+
const val SIGNER_PICKER_TAG = "SignerPicker"
|
|
588
|
+
}
|
|
589
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
package com.pakstr.app.signer
|
|
2
|
+
|
|
3
|
+
import com.pakstr.app.crypto.Bip340Hash
|
|
4
|
+
import com.pakstr.app.crypto.Nip46ClientKey
|
|
5
|
+
import com.pakstr.app.crypto.NostrEventVerifier
|
|
6
|
+
import com.pakstr.app.signer.interfaces.NostrSigner
|
|
7
|
+
import com.pakstr.app.signer.local.LocalAccountStore
|
|
8
|
+
import org.json.JSONObject
|
|
9
|
+
|
|
10
|
+
internal class LocalSigner(
|
|
11
|
+
private val keyLoader: () -> Nip46ClientKey
|
|
12
|
+
) : NostrSigner {
|
|
13
|
+
constructor(store: LocalAccountStore) : this(
|
|
14
|
+
keyLoader = {
|
|
15
|
+
store.loadKey()
|
|
16
|
+
?: throw IllegalStateException(
|
|
17
|
+
"Local account has not been created"
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
override suspend fun getPublicKey(): String {
|
|
22
|
+
return requireKey().use { key ->
|
|
23
|
+
key.publicKeyHex
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override suspend fun signEvent(eventJson: String): String {
|
|
28
|
+
return requireKey().use { key ->
|
|
29
|
+
val event = JSONObject(eventJson)
|
|
30
|
+
val publicKey = key.publicKeyHex
|
|
31
|
+
val suppliedPublicKey = event.optString("pubkey")
|
|
32
|
+
require(
|
|
33
|
+
suppliedPublicKey.isBlank() ||
|
|
34
|
+
suppliedPublicKey == publicKey
|
|
35
|
+
) {
|
|
36
|
+
"Event public key does not match local signer"
|
|
37
|
+
}
|
|
38
|
+
event.put("pubkey", publicKey)
|
|
39
|
+
NostrEventBuilder.addId(event)
|
|
40
|
+
|
|
41
|
+
val eventId =
|
|
42
|
+
Bip340Hash.hexToBytes(
|
|
43
|
+
event.getString("id")
|
|
44
|
+
)
|
|
45
|
+
val signature = key.sign(eventId)
|
|
46
|
+
try {
|
|
47
|
+
event.put(
|
|
48
|
+
"sig",
|
|
49
|
+
Bip340Hash.bytesToHex(signature)
|
|
50
|
+
)
|
|
51
|
+
} finally {
|
|
52
|
+
eventId.fill(0)
|
|
53
|
+
signature.fill(0)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
check(NostrEventVerifier.verify(event)) {
|
|
57
|
+
"Generated signature failed verification"
|
|
58
|
+
}
|
|
59
|
+
event.toString()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override suspend fun nip04Encrypt(
|
|
64
|
+
pubkey: String,
|
|
65
|
+
plaintext: String
|
|
66
|
+
): String {
|
|
67
|
+
throw UnsupportedOperationException(
|
|
68
|
+
"NIP-04 is not supported by the local signer"
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override suspend fun nip04Decrypt(
|
|
73
|
+
pubkey: String,
|
|
74
|
+
ciphertext: String
|
|
75
|
+
): String {
|
|
76
|
+
throw UnsupportedOperationException(
|
|
77
|
+
"NIP-04 is not supported by the local signer"
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override suspend fun nip44Encrypt(
|
|
82
|
+
pubkey: String,
|
|
83
|
+
plaintext: String
|
|
84
|
+
): String {
|
|
85
|
+
return requireKey().use { key ->
|
|
86
|
+
key.encryptNip44(plaintext, pubkey)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
override suspend fun nip44Decrypt(
|
|
91
|
+
pubkey: String,
|
|
92
|
+
ciphertext: String
|
|
93
|
+
): String {
|
|
94
|
+
return requireKey().use { key ->
|
|
95
|
+
key.decryptNip44(ciphertext, pubkey)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private fun requireKey() = keyLoader()
|
|
100
|
+
}
|
|
@@ -48,7 +48,7 @@ class NostrBridge(
|
|
|
48
48
|
try {
|
|
49
49
|
val pk =
|
|
50
50
|
signerManager
|
|
51
|
-
.
|
|
51
|
+
.getConfiguredSigner()
|
|
52
52
|
.getPublicKey()
|
|
53
53
|
resolve(
|
|
54
54
|
callbackId,
|
|
@@ -107,7 +107,7 @@ class NostrBridge(
|
|
|
107
107
|
|
|
108
108
|
val result =
|
|
109
109
|
signerManager
|
|
110
|
-
.
|
|
110
|
+
.getConfiguredSigner()
|
|
111
111
|
.signEvent(
|
|
112
112
|
payload
|
|
113
113
|
)
|
|
@@ -180,7 +180,7 @@ class NostrBridge(
|
|
|
180
180
|
|
|
181
181
|
val result =
|
|
182
182
|
signerManager
|
|
183
|
-
.
|
|
183
|
+
.getConfiguredSigner()
|
|
184
184
|
.nip04Encrypt(
|
|
185
185
|
pubkey,
|
|
186
186
|
plaintext
|
|
@@ -254,7 +254,7 @@ class NostrBridge(
|
|
|
254
254
|
|
|
255
255
|
val result =
|
|
256
256
|
signerManager
|
|
257
|
-
.
|
|
257
|
+
.getConfiguredSigner()
|
|
258
258
|
.nip04Decrypt(
|
|
259
259
|
pubkey,
|
|
260
260
|
ciphertext
|
|
@@ -318,7 +318,7 @@ class NostrBridge(
|
|
|
318
318
|
|
|
319
319
|
val result =
|
|
320
320
|
signerManager
|
|
321
|
-
.
|
|
321
|
+
.getConfiguredSigner()
|
|
322
322
|
.nip44Encrypt(
|
|
323
323
|
json.getString("pubkey"),
|
|
324
324
|
json.getString("plaintext")
|
|
@@ -373,7 +373,7 @@ class NostrBridge(
|
|
|
373
373
|
|
|
374
374
|
val result =
|
|
375
375
|
signerManager
|
|
376
|
-
.
|
|
376
|
+
.getConfiguredSigner()
|
|
377
377
|
.nip44Decrypt(
|
|
378
378
|
json.getString("pubkey"),
|
|
379
379
|
json.getString("ciphertext")
|
|
@@ -14,12 +14,15 @@ import com.pakstr.app.signer.bunker.Nip46PairingUri
|
|
|
14
14
|
import com.pakstr.app.signer.bunker.RelayNip46Client
|
|
15
15
|
import com.pakstr.app.signer.exceptions.SignerUnavailableException
|
|
16
16
|
import com.pakstr.app.signer.interfaces.NostrSigner
|
|
17
|
+
import com.pakstr.app.signer.local.LocalAccountStore
|
|
18
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
17
19
|
import org.json.JSONObject
|
|
18
20
|
|
|
19
21
|
class SignerManager(
|
|
20
22
|
context: Context,
|
|
21
23
|
private val amberLauncher: ActivityResultLauncher<Intent>,
|
|
22
|
-
private val bunkerAuthChallengeHandler: ((String) -> Unit)? = null
|
|
24
|
+
private val bunkerAuthChallengeHandler: ((String) -> Unit)? = null,
|
|
25
|
+
private val signerPickerRequestHandler: (() -> Unit)? = null
|
|
23
26
|
) {
|
|
24
27
|
private val context = context.applicationContext
|
|
25
28
|
private val prefs =
|
|
@@ -30,10 +33,14 @@ class SignerManager(
|
|
|
30
33
|
private val bunkerStore by lazy {
|
|
31
34
|
BunkerConnectionStore(this.context)
|
|
32
35
|
}
|
|
36
|
+
private val localAccountStore by lazy {
|
|
37
|
+
LocalAccountStore(this.context)
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
private var activeSigner: NostrSigner? = null
|
|
35
41
|
private var currentUserPubkey: String? = null
|
|
36
42
|
private var pendingPairing: PendingPairing? = null
|
|
43
|
+
private var pendingSignerSelection: CompletableDeferred<Boolean>? = null
|
|
37
44
|
|
|
38
45
|
init {
|
|
39
46
|
AppDebugLogger.log(
|
|
@@ -44,7 +51,7 @@ class SignerManager(
|
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
fun getActiveType(): SignerType? {
|
|
47
|
-
val value = prefs.getString(
|
|
54
|
+
val value = prefs.getString(KEY_SIGNER_TYPE, SignerType.AMBER.name)
|
|
48
55
|
return try {
|
|
49
56
|
SignerType.valueOf(value.toString())
|
|
50
57
|
} catch (_: IllegalArgumentException) {
|
|
@@ -52,6 +59,20 @@ class SignerManager(
|
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
|
|
62
|
+
fun isSignerConfigured(): Boolean =
|
|
63
|
+
prefs.getBoolean(KEY_SIGNER_CONFIGURED, false)
|
|
64
|
+
|
|
65
|
+
fun migrateLegacySignerConfiguration(setupCompleted: Boolean) {
|
|
66
|
+
if (prefs.contains(KEY_SIGNER_CONFIGURED)) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if (setupCompleted || prefs.contains(KEY_SIGNER_TYPE)) {
|
|
70
|
+
prefs.edit {
|
|
71
|
+
putBoolean(KEY_SIGNER_CONFIGURED, true)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
55
76
|
fun hasBunkerSession(): Boolean {
|
|
56
77
|
return try {
|
|
57
78
|
val connection = bunkerStore.restore() ?: return false
|
|
@@ -169,6 +190,12 @@ class SignerManager(
|
|
|
169
190
|
setActiveTypeWithoutClosing(type)
|
|
170
191
|
}
|
|
171
192
|
|
|
193
|
+
fun createLocalAccount() {
|
|
194
|
+
localAccountStore.createAccount()
|
|
195
|
+
closeActiveSigner()
|
|
196
|
+
setActiveTypeWithoutClosing(SignerType.LOCAL)
|
|
197
|
+
}
|
|
198
|
+
|
|
172
199
|
fun logoutBunker() {
|
|
173
200
|
try {
|
|
174
201
|
(activeSigner as? BunkerSigner)?.disconnect()
|
|
@@ -182,6 +209,56 @@ class SignerManager(
|
|
|
182
209
|
}
|
|
183
210
|
}
|
|
184
211
|
|
|
212
|
+
suspend fun getConfiguredSigner(): NostrSigner {
|
|
213
|
+
if (isSignerConfigured()) {
|
|
214
|
+
return getSigner()
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
var shouldRequestPicker = false
|
|
218
|
+
val selection =
|
|
219
|
+
synchronized(this) {
|
|
220
|
+
if (isSignerConfigured()) {
|
|
221
|
+
null
|
|
222
|
+
} else {
|
|
223
|
+
pendingSignerSelection
|
|
224
|
+
?: CompletableDeferred<Boolean>().also {
|
|
225
|
+
pendingSignerSelection = it
|
|
226
|
+
shouldRequestPicker = true
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (selection == null) {
|
|
232
|
+
return getSigner()
|
|
233
|
+
}
|
|
234
|
+
if (shouldRequestPicker) {
|
|
235
|
+
val handler = signerPickerRequestHandler
|
|
236
|
+
if (handler == null) {
|
|
237
|
+
cancelSignerSelection()
|
|
238
|
+
} else {
|
|
239
|
+
try {
|
|
240
|
+
handler()
|
|
241
|
+
} catch (_: Exception) {
|
|
242
|
+
cancelSignerSelection()
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!selection.await()) {
|
|
247
|
+
throw SignerUnavailableException("Signer selection was dismissed")
|
|
248
|
+
}
|
|
249
|
+
return getSigner()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
fun cancelSignerSelection() {
|
|
253
|
+
val selection =
|
|
254
|
+
synchronized(this) {
|
|
255
|
+
pendingSignerSelection.also {
|
|
256
|
+
pendingSignerSelection = null
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
selection?.complete(false)
|
|
260
|
+
}
|
|
261
|
+
|
|
185
262
|
fun getSigner(): NostrSigner {
|
|
186
263
|
activeSigner?.let { return it }
|
|
187
264
|
val activeType = getActiveType()
|
|
@@ -195,6 +272,7 @@ class SignerManager(
|
|
|
195
272
|
when (activeType) {
|
|
196
273
|
SignerType.AMBER -> createAmberSigner()
|
|
197
274
|
SignerType.BUNKER -> createBunkerSigner()
|
|
275
|
+
SignerType.LOCAL -> createLocalSigner()
|
|
198
276
|
SignerType.FAKE -> FakeSigner()
|
|
199
277
|
null -> throw IllegalStateException("No signer selected")
|
|
200
278
|
}
|
|
@@ -204,6 +282,7 @@ class SignerManager(
|
|
|
204
282
|
|
|
205
283
|
fun close() {
|
|
206
284
|
cancelBunkerPairing()
|
|
285
|
+
cancelSignerSelection()
|
|
207
286
|
closeActiveSigner()
|
|
208
287
|
}
|
|
209
288
|
|
|
@@ -289,10 +368,36 @@ class SignerManager(
|
|
|
289
368
|
return BunkerSigner(context, client)
|
|
290
369
|
}
|
|
291
370
|
|
|
371
|
+
private fun createLocalSigner(): LocalSigner {
|
|
372
|
+
try {
|
|
373
|
+
localAccountStore.loadKey()?.use { key ->
|
|
374
|
+
currentUserPubkey = key.publicKeyHex
|
|
375
|
+
} ?: throw SignerUnavailableException(
|
|
376
|
+
"Local account has not been created"
|
|
377
|
+
)
|
|
378
|
+
} catch (error: SignerUnavailableException) {
|
|
379
|
+
throw error
|
|
380
|
+
} catch (error: Exception) {
|
|
381
|
+
throw IllegalStateException(
|
|
382
|
+
"Unable to restore local account",
|
|
383
|
+
error
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
return LocalSigner(localAccountStore)
|
|
387
|
+
}
|
|
388
|
+
|
|
292
389
|
private fun setActiveTypeWithoutClosing(type: SignerType) {
|
|
293
390
|
prefs.edit {
|
|
294
|
-
putString(
|
|
391
|
+
putString(KEY_SIGNER_TYPE, type.name)
|
|
392
|
+
putBoolean(KEY_SIGNER_CONFIGURED, true)
|
|
295
393
|
}
|
|
394
|
+
val selection =
|
|
395
|
+
synchronized(this) {
|
|
396
|
+
pendingSignerSelection.also {
|
|
397
|
+
pendingSignerSelection = null
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
selection?.complete(true)
|
|
296
401
|
}
|
|
297
402
|
|
|
298
403
|
private fun closeActiveSigner() {
|
|
@@ -319,6 +424,8 @@ class SignerManager(
|
|
|
319
424
|
}
|
|
320
425
|
|
|
321
426
|
private companion object {
|
|
427
|
+
const val KEY_SIGNER_TYPE = "type"
|
|
428
|
+
const val KEY_SIGNER_CONFIGURED = "signer_configured"
|
|
322
429
|
const val BUNKER_PERMISSIONS =
|
|
323
430
|
"sign_event,nip04_encrypt,nip04_decrypt," +
|
|
324
431
|
"nip44_encrypt,nip44_decrypt"
|